diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index fb3081ff495..781b3a35367 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -21,6 +21,7 @@ import { IWorkspaceFolder } from 'vs/platform/workspace/common/workspace'; import { ExtHostVariableResolverService } from 'vs/workbench/api/node/extHostDebugService'; import { ExtHostDocumentsAndEditors } from 'vs/workbench/api/common/extHostDocumentsAndEditors'; import { getSystemShell, detectAvailableShells } from 'vs/workbench/contrib/terminal/node/terminal'; +import { getMainProcessParentEnv } from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; const RENDERER_NO_PROCESS_ID = -1; @@ -522,6 +523,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { const envFromConfig = this._apiInspectConfigToPlain(configProvider.getConfiguration('terminal.integrated').inspect(`env.${platformKey}`)); const workspaceFolders = await this._extHostWorkspace.getWorkspaceFolders2(); const variableResolver = workspaceFolders ? new ExtHostVariableResolverService(workspaceFolders, this._extHostDocumentsAndEditors, configProvider) : undefined; + const baseEnv = terminalConfig.get('inheritEnv', true) ? process.env as platform.IProcessEnvironment : await getMainProcessParentEnv(); const env = terminalEnvironment.createTerminalEnvironment( shellLaunchConfig, lastActiveWorkspace, @@ -530,9 +532,7 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { isWorkspaceShellAllowed, pkg.version, terminalConfig.get('setLocaleVariables', false), - // Always inherit the environment as we need to be running in a login shell, this may - // change when macOS servers are supported - process.env as platform.IProcessEnvironment + baseEnv ); // Fork the process and listen for messages diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts index d725545794d..94b5a713671 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts @@ -7,17 +7,16 @@ import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/ import { ITerminalInstance, IWindowsShellHelper, IShellLaunchConfig, ITerminalChildProcess, IS_WORKSPACE_SHELL_ALLOWED_STORAGE_KEY } from 'vs/workbench/contrib/terminal/common/terminal'; import { WindowsShellHelper } from 'vs/workbench/contrib/terminal/node/windowsShellHelper'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { IProcessEnvironment, isLinux, isMacintosh, isWindows, platform, Platform } from 'vs/base/common/platform'; +import { IProcessEnvironment, platform, Platform } from 'vs/base/common/platform'; import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess'; import { getSystemShell } from 'vs/workbench/contrib/terminal/node/terminal'; import { Terminal as XTermTerminal } from 'xterm'; import { WebLinksAddon as XTermWebLinksAddon } from 'xterm-addon-web-links'; import { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; -import { readFile } from 'vs/base/node/pfs'; -import { basename } from 'vs/base/common/path'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { getDefaultShell, getDefaultShellArgs } from 'vs/workbench/contrib/terminal/common/terminalEnvironment'; import { StorageScope, IStorageService } from 'vs/platform/storage/common/storage'; +import { getMainProcessParentEnv } from 'vs/workbench/contrib/terminal/node/terminalEnvironment'; let Terminal: typeof XTermTerminal; let WebLinksAddon: typeof XTermWebLinksAddon; @@ -26,8 +25,6 @@ let SearchAddon: typeof XTermSearchAddon; export class TerminalInstanceService implements ITerminalInstanceService { public _serviceBrand: any; - private _mainProcessParentEnv: IProcessEnvironment | undefined; - constructor( @IInstantiationService private readonly _instantiationService: IInstantiationService, @IConfigurationService private readonly _configurationService: IConfigurationService, @@ -86,72 +83,7 @@ export class TerminalInstanceService implements ITerminalInstanceService { return Promise.resolve({ shell, args }); } - public async getMainProcessParentEnv(): Promise { - if (this._mainProcessParentEnv) { - return this._mainProcessParentEnv; - } - - // For Linux use /proc//status to get the parent of the main process and then fetch its - // env using /proc//environ. - if (isLinux) { - const mainProcessId = process.ppid; - const codeProcessName = basename(process.argv[0]); - let pid: number = 0; - let ppid: number = mainProcessId; - let name: string = codeProcessName; - do { - pid = ppid; - const status = await readFile(`/proc/${pid}/status`, 'utf8'); - const splitByLine = status.split('\n'); - splitByLine.forEach(line => { - if (line.indexOf('Name:') === 0) { - name = line.replace(/^Name:\s+/, ''); - } - if (line.indexOf('PPid:') === 0) { - ppid = parseInt(line.replace(/^PPid:\s+/, '')); - } - }); - } while (name === codeProcessName); - const rawEnv = await readFile(`/proc/${pid}/environ`, 'utf8'); - const env = {}; - rawEnv.split('\0').forEach(e => { - const i = e.indexOf('='); - env[e.substr(0, i)] = e.substr(i + 1); - }); - this._mainProcessParentEnv = env; - } - - // For macOS we want the "root" environment as shells by default run as login shells. It - // doesn't appear to be possible to get the "root" environment as `ps eww -o command` for - // PID 1 (the parent of the main process when launched from the dock/finder) returns no - // environment, because of this we will fill in the root environment using a whitelist of - // environment variables that we have. - if (isMacintosh) { - this._mainProcessParentEnv = {}; - // This list was generated by diffing launching a terminal with {} and the system - // terminal launched from finder. - const rootEnvVars = [ - 'SHELL', - 'SSH_AUTH_SOCK', - 'Apple_PubSub_Socket_Render', - 'XPC_FLAGS', - 'XPC_SERVICE_NAME', - 'HOME', - 'LOGNAME', - 'TMPDIR' - ]; - rootEnvVars.forEach(k => { - if (process.env[k]) { - this._mainProcessParentEnv![k] = process.env[k]!; - } - }); - } - - // TODO: Windows should return a fresh environment block, might need native code? - if (isWindows) { - this._mainProcessParentEnv = process.env as IProcessEnvironment; - } - - return this._mainProcessParentEnv!; + public getMainProcessParentEnv(): Promise { + return getMainProcessParentEnv(); } } \ No newline at end of file diff --git a/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts new file mode 100644 index 00000000000..1f8a127dbae --- /dev/null +++ b/src/vs/workbench/contrib/terminal/node/terminalEnvironment.ts @@ -0,0 +1,79 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IProcessEnvironment, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; +import { readFile } from 'vs/base/node/pfs'; +import { basename } from 'vs/base/common/path'; + +let mainProcessParentEnv: IProcessEnvironment | undefined; + +export async function getMainProcessParentEnv(): Promise { + if (mainProcessParentEnv) { + return mainProcessParentEnv; + } + + // For Linux use /proc//status to get the parent of the main process and then fetch its + // env using /proc//environ. + if (isLinux) { + const mainProcessId = process.ppid; + const codeProcessName = basename(process.argv[0]); + let pid: number = 0; + let ppid: number = mainProcessId; + let name: string = codeProcessName; + do { + pid = ppid; + const status = await readFile(`/proc/${pid}/status`, 'utf8'); + const splitByLine = status.split('\n'); + splitByLine.forEach(line => { + if (line.indexOf('Name:') === 0) { + name = line.replace(/^Name:\s+/, ''); + } + if (line.indexOf('PPid:') === 0) { + ppid = parseInt(line.replace(/^PPid:\s+/, '')); + } + }); + } while (name === codeProcessName); + const rawEnv = await readFile(`/proc/${pid}/environ`, 'utf8'); + const env = {}; + rawEnv.split('\0').forEach(e => { + const i = e.indexOf('='); + env[e.substr(0, i)] = e.substr(i + 1); + }); + mainProcessParentEnv = env; + } + + // For macOS we want the "root" environment as shells by default run as login shells. It + // doesn't appear to be possible to get the "root" environment as `ps eww -o command` for + // PID 1 (the parent of the main process when launched from the dock/finder) returns no + // environment, because of this we will fill in the root environment using a whitelist of + // environment variables that we have. + if (isMacintosh) { + mainProcessParentEnv = {}; + // This list was generated by diffing launching a terminal with {} and the system + // terminal launched from finder. + const rootEnvVars = [ + 'SHELL', + 'SSH_AUTH_SOCK', + 'Apple_PubSub_Socket_Render', + 'XPC_FLAGS', + 'XPC_SERVICE_NAME', + 'HOME', + 'LOGNAME', + 'TMPDIR' + ]; + rootEnvVars.forEach(k => { + if (process.env[k]) { + mainProcessParentEnv![k] = process.env[k]!; + } + }); + } + + // TODO: Windows should return a fresh environment block, might need native code? + if (isWindows) { + mainProcessParentEnv = process.env as IProcessEnvironment; + } + + return mainProcessParentEnv!; +} \ No newline at end of file