Skip to content
This repository was archived by the owner on Jul 30, 2025. It is now read-only.

Commit a0ec1a4

Browse files
committed
fix(plugins/plugin-bash-like): avoid using login shell for PTYs
Fixes #1247 Fixes #1425
1 parent aa0f72f commit a0ec1a4

File tree

3 files changed

+107
-12
lines changed

3 files changed

+107
-12
lines changed
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2020 IBM Corporation
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import Debug from 'debug'
18+
const debug = Debug('plugins/bash-like/pty/kuirc')
19+
20+
import { exec } from 'child_process'
21+
22+
/**
23+
* Preprocess bash/zsh aliases
24+
*
25+
*/
26+
function prefetchAliases() {
27+
// eslint-disable-next-line no-async-promise-executor
28+
return new Promise<string>(async (resolve, reject) => {
29+
if (process.platform === 'win32') {
30+
debug('skipping prefetchAliases')
31+
return resolve('')
32+
}
33+
34+
debug('prefetchAliases')
35+
const os = import('os')
36+
const fs = import('fs')
37+
const path = import('path')
38+
const uuid = import('uuid')
39+
const { getLoginShell } = await import('./server')
40+
const shell = await getLoginShell()
41+
debug('prefetchAliases got shell', shell)
42+
43+
exec(`${shell} -l -c alias`, async (err, stdout, stderr) => {
44+
try {
45+
if (stderr) {
46+
debug(stderr)
47+
}
48+
if (err) {
49+
debug('error in prefetchAliases 1', err)
50+
reject(err)
51+
} else {
52+
debug('got aliases')
53+
const aliases = stdout
54+
.toString()
55+
.split(/\n/)
56+
.filter(_ => _)
57+
.map(alias => {
58+
if (!/^alias /.test(alias)) {
59+
return `alias ${alias}`
60+
} else {
61+
return alias
62+
}
63+
})
64+
.join('\n')
65+
const kuirc = (await path).join((await os).tmpdir(), `kuirc-${(await uuid).v4()}`)
66+
debug('kuirc', kuirc)
67+
;(await fs).writeFile(kuirc, aliases, err => {
68+
if (err) {
69+
debug(err)
70+
reject(err)
71+
} else {
72+
resolve(kuirc)
73+
}
74+
})
75+
}
76+
} catch (err) {
77+
console.error('error in prefetchEnv 2', err)
78+
reject(err)
79+
}
80+
})
81+
})
82+
}
83+
84+
const kuirc: Promise<string> = prefetchAliases()
85+
export default kuirc

plugins/plugin-bash-like/src/pty/prefetch.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

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

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

3635
debug('prefetchEnv')
3736
const { getLoginShell } = await import('./server')
38-
const { shellExe: shell } = await getLoginShell()
37+
const shell = await getLoginShell()
3938
debug('prefetchEnv got shell', shell)
4039

4140
exec(`${shell} -l -c printenv`, (err, stdout, stderr) => {

plugins/plugin-bash-like/src/pty/server.ts

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ import { IncomingMessage } from 'http'
2929
import { Channel } from './channel'
3030
import { StdioChannelKuiSide } from './stdio-channel'
3131

32+
import kuirc from './kuirc'
33+
3234
import { CodedError, ExecOptions, Registrar } from '@kui-shell/core'
3335

3436
const debug = Debug('plugins/bash-like/pty/server')
@@ -148,49 +150,58 @@ export const disableBashSessions = async (): Promise<ExitHandler> => {
148150
* Determine, and cache, the user's login shell
149151
*
150152
*/
151-
const bashShellOpts = ['-l', '-i', '-c', '--']
152-
const shellOpts = process.platform === 'win32' ? [] : bashShellOpts
153153
type Shell = { shellExe: string; shellOpts: string[] }
154-
let cachedLoginShell: Shell
155-
export const getLoginShell = (): Promise<Shell> => {
154+
let cachedLoginShell: string
155+
export const getLoginShell = (): Promise<string> => {
156156
return new Promise((resolve, reject) => {
157157
if (cachedLoginShell) {
158158
debug('returning cached login shell', cachedLoginShell)
159159
resolve(cachedLoginShell)
160160
} else if (process.env.SHELL) {
161161
// Note how we intentionally assume bash here, even on windows
162-
resolve({ shellExe: process.env.SHELL, shellOpts: bashShellOpts })
162+
resolve(process.env.SHELL)
163163
} else {
164164
const defaultShell = process.platform === 'win32' ? 'powershell.exe' : '/bin/bash'
165165

166166
if (process.env.TRAVIS_JOB_ID !== undefined || process.platform === 'win32') {
167167
debug('using defaultShell for travis')
168-
cachedLoginShell = { shellExe: defaultShell, shellOpts }
168+
cachedLoginShell = defaultShell
169169
resolve(cachedLoginShell)
170170
} else {
171171
try {
172-
exec(`${defaultShell} -l -c "echo $SHELL"`, (err, stdout, stderr) => {
172+
exec(`${defaultShell} -l -c "echo $SHELL"`, async (err, stdout, stderr) => {
173173
if (err) {
174174
console.error('error in getLoginShell subroutine', err)
175175
if (stderr) {
176176
console.error(stderr)
177177
}
178178
reject(err)
179179
} else {
180-
cachedLoginShell = { shellExe: stdout.trim() || defaultShell, shellOpts }
180+
cachedLoginShell = stdout.trim() || defaultShell
181181
debug('login shell', cachedLoginShell)
182182
resolve(cachedLoginShell)
183183
}
184184
})
185185
} catch (err) {
186186
console.error('error in exec of getLoginShell subroutine', err)
187-
resolve({ shellExe: defaultShell, shellOpts })
187+
resolve(defaultShell)
188188
}
189189
}
190190
}
191191
})
192192
}
193193

194+
export async function getShellOpts(): Promise<Shell> {
195+
const bashShellOpts = process.platform === 'win32' ? undefined : ['--rcfile', await kuirc, '-i', '-c', '--']
196+
const shellOpts = process.platform === 'win32' ? [] : bashShellOpts
197+
console.error('!!!!!!!!', bashShellOpts)
198+
199+
return {
200+
shellExe: process.platform === 'win32' ? 'powershell.exe' : '/bin/bash',
201+
shellOpts
202+
}
203+
}
204+
194205
/**
195206
* Use precomputed shell aliases
196207
*
@@ -305,7 +316,7 @@ export const onConnection = (exitNow: ExitHandler, uid?: number, gid?: number) =
305316
const aliasedCmd = shellAliases[cmd]
306317
const cmdline = aliasedCmd ? msg.cmdline.replace(new RegExp(`^${cmd}`), aliasedCmd) : msg.cmdline
307318

308-
const { shellExe, shellOpts } = await getLoginShell()
319+
const { shellExe, shellOpts } = await getShellOpts()
309320
let shell = spawn(shellExe, shellOpts.concat([cmdline]), {
310321
uid,
311322
gid,

0 commit comments

Comments
 (0)