Skip to content

Commit

Permalink
fix(plugins/plugin-bash-like): avoid using login shell for PTYs
Browse files Browse the repository at this point in the history
Fixes #1247
Fixes #1425
  • Loading branch information
starpit committed Apr 23, 2020
1 parent aa0f72f commit a0ec1a4
Show file tree
Hide file tree
Showing 3 changed files with 107 additions and 12 deletions.
85 changes: 85 additions & 0 deletions plugins/plugin-bash-like/src/pty/kuirc.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
/*
* Copyright 2020 IBM Corporation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import Debug from 'debug'
const debug = Debug('plugins/bash-like/pty/kuirc')

import { exec } from 'child_process'

/**
* Preprocess bash/zsh aliases
*
*/
function prefetchAliases() {
// eslint-disable-next-line no-async-promise-executor
return new Promise<string>(async (resolve, reject) => {
if (process.platform === 'win32') {
debug('skipping prefetchAliases')
return resolve('')
}

debug('prefetchAliases')
const os = import('os')
const fs = import('fs')
const path = import('path')
const uuid = import('uuid')
const { getLoginShell } = await import('./server')
const shell = await getLoginShell()
debug('prefetchAliases got shell', shell)

exec(`${shell} -l -c alias`, async (err, stdout, stderr) => {
try {
if (stderr) {
debug(stderr)
}
if (err) {
debug('error in prefetchAliases 1', err)
reject(err)
} else {
debug('got aliases')
const aliases = stdout
.toString()
.split(/\n/)
.filter(_ => _)
.map(alias => {
if (!/^alias /.test(alias)) {
return `alias ${alias}`
} else {
return alias
}
})
.join('\n')
const kuirc = (await path).join((await os).tmpdir(), `kuirc-${(await uuid).v4()}`)
debug('kuirc', kuirc)
;(await fs).writeFile(kuirc, aliases, err => {
if (err) {
debug(err)
reject(err)
} else {
resolve(kuirc)
}
})
}
} catch (err) {
console.error('error in prefetchEnv 2', err)
reject(err)
}
})
})
}

const kuirc: Promise<string> = prefetchAliases()
export default kuirc
3 changes: 1 addition & 2 deletions plugins/plugin-bash-like/src/pty/prefetch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@

import Debug from 'debug'
const debug = Debug('plugins/bash-like/pty/prefetch')
debug('loading')

import { exec } from 'child_process'
import * as propertiesParser from 'properties-parser'
Expand All @@ -35,7 +34,7 @@ function prefetchEnv() {

debug('prefetchEnv')
const { getLoginShell } = await import('./server')
const { shellExe: shell } = await getLoginShell()
const shell = await getLoginShell()
debug('prefetchEnv got shell', shell)

exec(`${shell} -l -c printenv`, (err, stdout, stderr) => {
Expand Down
31 changes: 21 additions & 10 deletions plugins/plugin-bash-like/src/pty/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ import { IncomingMessage } from 'http'
import { Channel } from './channel'
import { StdioChannelKuiSide } from './stdio-channel'

import kuirc from './kuirc'

import { CodedError, ExecOptions, Registrar } from '@kui-shell/core'

const debug = Debug('plugins/bash-like/pty/server')
Expand Down Expand Up @@ -148,49 +150,58 @@ export const disableBashSessions = async (): Promise<ExitHandler> => {
* Determine, and cache, the user's login shell
*
*/
const bashShellOpts = ['-l', '-i', '-c', '--']
const shellOpts = process.platform === 'win32' ? [] : bashShellOpts
type Shell = { shellExe: string; shellOpts: string[] }
let cachedLoginShell: Shell
export const getLoginShell = (): Promise<Shell> => {
let cachedLoginShell: string
export const getLoginShell = (): Promise<string> => {
return new Promise((resolve, reject) => {
if (cachedLoginShell) {
debug('returning cached login shell', cachedLoginShell)
resolve(cachedLoginShell)
} else if (process.env.SHELL) {
// Note how we intentionally assume bash here, even on windows
resolve({ shellExe: process.env.SHELL, shellOpts: bashShellOpts })
resolve(process.env.SHELL)
} else {
const defaultShell = process.platform === 'win32' ? 'powershell.exe' : '/bin/bash'

if (process.env.TRAVIS_JOB_ID !== undefined || process.platform === 'win32') {
debug('using defaultShell for travis')
cachedLoginShell = { shellExe: defaultShell, shellOpts }
cachedLoginShell = defaultShell
resolve(cachedLoginShell)
} else {
try {
exec(`${defaultShell} -l -c "echo $SHELL"`, (err, stdout, stderr) => {
exec(`${defaultShell} -l -c "echo $SHELL"`, async (err, stdout, stderr) => {
if (err) {
console.error('error in getLoginShell subroutine', err)
if (stderr) {
console.error(stderr)
}
reject(err)
} else {
cachedLoginShell = { shellExe: stdout.trim() || defaultShell, shellOpts }
cachedLoginShell = stdout.trim() || defaultShell
debug('login shell', cachedLoginShell)
resolve(cachedLoginShell)
}
})
} catch (err) {
console.error('error in exec of getLoginShell subroutine', err)
resolve({ shellExe: defaultShell, shellOpts })
resolve(defaultShell)
}
}
}
})
}

export async function getShellOpts(): Promise<Shell> {
const bashShellOpts = process.platform === 'win32' ? undefined : ['--rcfile', await kuirc, '-i', '-c', '--']
const shellOpts = process.platform === 'win32' ? [] : bashShellOpts
console.error('!!!!!!!!', bashShellOpts)

return {
shellExe: process.platform === 'win32' ? 'powershell.exe' : '/bin/bash',
shellOpts
}
}

/**
* Use precomputed shell aliases
*
Expand Down Expand Up @@ -305,7 +316,7 @@ export const onConnection = (exitNow: ExitHandler, uid?: number, gid?: number) =
const aliasedCmd = shellAliases[cmd]
const cmdline = aliasedCmd ? msg.cmdline.replace(new RegExp(`^${cmd}`), aliasedCmd) : msg.cmdline

const { shellExe, shellOpts } = await getLoginShell()
const { shellExe, shellOpts } = await getShellOpts()
let shell = spawn(shellExe, shellOpts.concat([cmdline]), {
uid,
gid,
Expand Down

0 comments on commit a0ec1a4

Please sign in to comment.