diff --git a/src/vs/workbench/api/node/extHostTerminalService.ts b/src/vs/workbench/api/node/extHostTerminalService.ts index 59caecef265..a1de46cfdf5 100644 --- a/src/vs/workbench/api/node/extHostTerminalService.ts +++ b/src/vs/workbench/api/node/extHostTerminalService.ts @@ -500,7 +500,10 @@ export class ExtHostTerminalService implements ExtHostTerminalServiceShape { variableResolver, isWorkspaceShellAllowed, pkg.version, - terminalConfig.get('setLocaleVariables', false) + 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 + true ); // Fork the process and listen for messages diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts index 25b79bc85a6..0904438ef80 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.contribution.ts @@ -228,6 +228,11 @@ configurationRegistry.registerConfiguration({ }, default: [] }, + 'terminal.integrated.inheritEnv': { + markdownDescription: nls.localize('terminal.integrated.inheritEnv', "TODO"), + type: 'boolean', + default: true + }, 'terminal.integrated.env.osx': { markdownDescription: nls.localize('terminal.integrated.env.osx', "Object with environment variables that will be added to the VS Code process to be used by the terminal on macOS. Set to `null` to delete the environment variable."), type: 'object', diff --git a/src/vs/workbench/contrib/terminal/browser/terminal.ts b/src/vs/workbench/contrib/terminal/browser/terminal.ts index 06295c9666a..59c773a8f7b 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminal.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminal.ts @@ -26,6 +26,7 @@ export interface ITerminalInstanceService { createWindowsShellHelper(shellProcessId: number, instance: ITerminalInstance, xterm: XTermTerminal): IWindowsShellHelper; createTerminalProcess(shellLaunchConfig: IShellLaunchConfig, cwd: string, cols: number, rows: number, env: IProcessEnvironment, windowsEnableConpty: boolean): ITerminalChildProcess; getDefaultShell(p: Platform): string; + getMainProcessParentEnv(): Promise; } export interface IBrowserTerminalConfigHelper extends ITerminalConfigHelper { diff --git a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts index b9bd8954a3c..eb28c172632 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalInstanceService.ts @@ -8,6 +8,7 @@ import { IWindowsShellHelper, ITerminalChildProcess } from 'vs/workbench/contrib import { Terminal as XTermTerminal } from 'xterm'; import { WebLinksAddon as XTermWebLinksAddon } from 'xterm-addon-web-links'; import { SearchAddon as XTermSearchAddon } from 'xterm-addon-search'; +import { IProcessEnvironment } from 'vs/base/common/platform'; let Terminal: typeof XTermTerminal; let WebLinksAddon: typeof XTermWebLinksAddon; @@ -50,4 +51,8 @@ export class TerminalInstanceService implements ITerminalInstanceService { public getDefaultShell(): string { throw new Error('Not implemented'); } + + public async getMainProcessParentEnv(): Promise { + return {}; + } } \ No newline at end of file diff --git a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts index 0a3d373f178..51ed69c3793 100644 --- a/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts +++ b/src/vs/workbench/contrib/terminal/browser/terminalProcessManager.ts @@ -171,7 +171,8 @@ export class TerminalProcessManager implements ITerminalProcessManager { const lastActiveWorkspace = activeWorkspaceRootUri ? this._workspaceContextService.getWorkspaceFolder(activeWorkspaceRootUri) : null; const envFromConfigValue = this._workspaceConfigurationService.inspect(`terminal.integrated.env.${platformKey}`); const isWorkspaceShellAllowed = this._configHelper.checkWorkspaceShellPermissions(); - const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, lastActiveWorkspace, envFromConfigValue, this._configurationResolverService, isWorkspaceShellAllowed, this._productService.version, this._configHelper.config.setLocaleVariables); + this._terminalInstanceService.getMainProcessParentEnv(); + const env = terminalEnvironment.createTerminalEnvironment(shellLaunchConfig, lastActiveWorkspace, envFromConfigValue, this._configurationResolverService, isWorkspaceShellAllowed, this._productService.version, this._configHelper.config.setLocaleVariables, this._configHelper.config.inheritEnv); const useConpty = this._configHelper.config.windowsEnableConpty; return this._terminalInstanceService.createTerminalProcess(shellLaunchConfig, initialCwd, cols, rows, env, useConpty); diff --git a/src/vs/workbench/contrib/terminal/common/terminal.ts b/src/vs/workbench/contrib/terminal/common/terminal.ts index 5fd9248c115..bce06565e11 100644 --- a/src/vs/workbench/contrib/terminal/common/terminal.ts +++ b/src/vs/workbench/contrib/terminal/common/terminal.ts @@ -94,6 +94,7 @@ export interface ITerminalConfiguration { cwd: string; confirmOnExit: boolean; enableBell: boolean; + inheritEnv: boolean; env: { linux: { [key: string]: string }; osx: { [key: string]: string }; diff --git a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts index 813bd746af3..7d260cd9418 100644 --- a/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts +++ b/src/vs/workbench/contrib/terminal/common/terminalEnvironment.ts @@ -198,7 +198,8 @@ export function createTerminalEnvironment( configurationResolverService: IConfigurationResolverService | undefined, isWorkspaceShellAllowed: boolean, version: string | undefined, - setLocaleVariables: boolean + setLocaleVariables: boolean, + inheritEnv: boolean ): platform.IProcessEnvironment { // Create a terminal environment based on settings, launch config and permissions let env: platform.IProcessEnvironment = {}; @@ -207,7 +208,7 @@ export function createTerminalEnvironment( mergeNonNullKeys(env, shellLaunchConfig.env); } else { // Merge process env with the env from config and from shellLaunchConfig - mergeNonNullKeys(env, process.env); + mergeNonNullKeys(env, inheritEnv ? process.env : {}); // const platformKey = platform.isWindows ? 'windows' : (platform.isMacintosh ? 'osx' : 'linux'); // const envFromConfigValue = this._workspaceConfigurationService.inspect(`terminal.integrated.env.${platformKey}`); diff --git a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts index e0e69912cbc..45a5971aad9 100644 --- a/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts +++ b/src/vs/workbench/contrib/terminal/electron-browser/terminalInstanceService.ts @@ -7,12 +7,14 @@ import { ITerminalInstanceService } from 'vs/workbench/contrib/terminal/browser/ import { ITerminalInstance, IWindowsShellHelper, IShellLaunchConfig, ITerminalChildProcess } 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, Platform } from 'vs/base/common/platform'; +import { IProcessEnvironment, Platform, isLinux, isMacintosh, isWindows } from 'vs/base/common/platform'; import { TerminalProcess } from 'vs/workbench/contrib/terminal/node/terminalProcess'; import { getDefaultShell } 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'; let Terminal: typeof XTermTerminal; let WebLinksAddon: typeof XTermWebLinksAddon; @@ -21,6 +23,8 @@ let SearchAddon: typeof XTermSearchAddon; export class TerminalInstanceService implements ITerminalInstanceService { public _serviceBrand: any; + private _mainProcessParentEnv: IProcessEnvironment | undefined; + constructor( @IInstantiationService private readonly _instantiationService: IInstantiationService ) { @@ -58,4 +62,53 @@ export class TerminalInstanceService implements ITerminalInstanceService { public getDefaultShell(p: Platform): string { return getDefaultShell(p); } + + 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 just return the root environment which seems to always be {}, this is the + // parent of the main process when code is launched from the dock. + if (isMacintosh) { + this._mainProcessParentEnv = {}; + } + + // TODO: Windows should return a fresh environment block, might need native code? + if (isWindows) { + this._mainProcessParentEnv = process.env as IProcessEnvironment; + } + + return this._mainProcessParentEnv!; + } } \ No newline at end of file