diff --git a/src/vs/base/parts/ipc/node/ipc.net.ts b/src/vs/base/parts/ipc/node/ipc.net.ts index 116b889597f..68bf15c6f0b 100644 --- a/src/vs/base/parts/ipc/node/ipc.net.ts +++ b/src/vs/base/parts/ipc/node/ipc.net.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { createHash } from 'crypto'; import { Socket, Server as NetServer, createConnection, createServer } from 'net'; import { Event, Emitter } from 'vs/base/common/event'; import { ClientConnectionEvent, IPCServer } from 'vs/base/parts/ipc/common/ipc'; @@ -13,6 +14,7 @@ import { IDisposable, Disposable } from 'vs/base/common/lifecycle'; import { VSBuffer } from 'vs/base/common/buffer'; import { ISocket, Protocol, Client, ChunkStream } from 'vs/base/parts/ipc/common/ipc.net'; import { onUnexpectedError } from 'vs/base/common/errors'; +import { Platform, platform } from 'vs/base/common/platform'; export class NodeSocket implements ISocket { public readonly socket: Socket; @@ -297,18 +299,67 @@ function unmask(buffer: VSBuffer, mask: number): void { } // Read this before there's any chance it is overwritten -const xdgRuntimeDir = process.env['XDG_RUNTIME_DIR']; +// Related to https://github.com/microsoft/vscode/issues/30624 +export const XDG_RUNTIME_DIR = process.env['XDG_RUNTIME_DIR']; -export function generateRandomPipeName(): string { +const safeIpcPathLengths: { [platform: number]: number } = { + [Platform.Linux]: 107, + [Platform.Mac]: 103 +}; + +export function generateRandomIPCHandle(): string { const randomSuffix = generateUuid(); + + // Windows: use named pipe if (process.platform === 'win32') { return `\\\\.\\pipe\\vscode-ipc-${randomSuffix}-sock`; } - // Mac/Unix: use socket file - if (xdgRuntimeDir) { - return join(xdgRuntimeDir, `vscode-ipc-${randomSuffix}.sock`); + + // Mac/Unix: use socket file and prefer + // XDG_RUNTIME_DIR over tmpDir + let result: string; + if (XDG_RUNTIME_DIR) { + result = join(XDG_RUNTIME_DIR, `vscode-ipc-${randomSuffix}.sock`); + } else { + result = join(tmpdir(), `vscode-ipc-${randomSuffix}.sock`); + } + + // Validate length + validateIPCHandleLength(result); + + return result; +} + +export function resolveUserDataIPCHandle(userDataPath: string, type: string, version: string): string { + const scope = createHash('md5').update(userDataPath).digest('hex'); + + // Windows: use named pipe + if (process.platform === 'win32') { + return `\\\\.\\pipe\\${scope}-${version}-${type}-sock`; + } + + // Mac/Unix: use socket file and prefer + // XDG_RUNTIME_DIR over user data path + // unless portable + let result: string; + if (XDG_RUNTIME_DIR && !process.env['VSCODE_PORTABLE']) { + result = join(XDG_RUNTIME_DIR, `vscode-${scope.substr(0, 8)}-${version}-${type}.sock`); + } else { + result = join(userDataPath, `${version}-${type}.sock`); + } + + // Validate length + validateIPCHandleLength(result); + + return result; +} + +function validateIPCHandleLength(handle: string): void { + const limit = safeIpcPathLengths[platform]; + if (typeof limit === 'number' && handle.length >= limit) { + // https://nodejs.org/api/net.html#net_identifying_paths_for_ipc_connections + console.warn(`WARNING: IPC handle "${handle}" is longer than ${limit} chars, try a shorter --user-data-dir`); } - return join(tmpdir(), `vscode-ipc-${randomSuffix}.sock`); } export class Server extends IPCServer { diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index b2ca5d979d5..9cc0a101fd1 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -12,7 +12,7 @@ import { parseMainProcessArgv, addArg } from 'vs/platform/environment/node/argvH import { createWaitMarkerFile } from 'vs/platform/environment/node/waitMarkerFile'; import { mkdirp } from 'vs/base/node/pfs'; import { LifecycleMainService, ILifecycleMainService } from 'vs/platform/lifecycle/electron-main/lifecycleMainService'; -import { Server, serve, connect } from 'vs/base/parts/ipc/node/ipc.net'; +import { Server, serve, connect, XDG_RUNTIME_DIR } from 'vs/base/parts/ipc/node/ipc.net'; import { createChannelSender } from 'vs/base/parts/ipc/common/ipc'; import { ILaunchMainService } from 'vs/platform/launch/electron-main/launchMainService'; import { ServicesAccessor, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -24,7 +24,6 @@ import { StateService } from 'vs/platform/state/node/stateService'; import { IStateService } from 'vs/platform/state/node/state'; import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; -import { xdgRuntimeDir } from 'vs/platform/environment/node/environmentService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { ConfigurationService } from 'vs/platform/configuration/common/configurationService'; import { IRequestService } from 'vs/platform/request/common/request'; @@ -353,8 +352,8 @@ class CodeMain { directories.push(environmentService.extensionsPath); } - if (xdgRuntimeDir) { - directories.push(xdgRuntimeDir); + if (XDG_RUNTIME_DIR) { + directories.push(XDG_RUNTIME_DIR); } this.showStartupWarningDialog( diff --git a/src/vs/platform/environment/electron-main/environmentMainService.ts b/src/vs/platform/environment/electron-main/environmentMainService.ts index 6b6d1af5394..058de0ca4f8 100644 --- a/src/vs/platform/environment/electron-main/environmentMainService.ts +++ b/src/vs/platform/environment/electron-main/environmentMainService.ts @@ -7,7 +7,9 @@ import { join } from 'vs/base/common/path'; import { memoize } from 'vs/base/common/decorators'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; import { INativeEnvironmentService } from 'vs/platform/environment/common/environment'; -import { getIPCHandle, NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService'; +import { resolveUserDataIPCHandle } from 'vs/base/parts/ipc/node/ipc.net'; +import product from 'vs/platform/product/common/product'; export const IEnvironmentMainService = createDecorator('environmentMainService'); @@ -42,7 +44,7 @@ export class EnvironmentMainService extends NativeEnvironmentService { get backupWorkspacesPath(): string { return join(this.backupHome, 'workspaces.json'); } @memoize - get mainIPCHandle(): string { return getIPCHandle(this.userDataPath, 'main'); } + get mainIPCHandle(): string { return resolveUserDataIPCHandle(this.userDataPath, 'main', product.version); } @memoize get sandbox(): boolean { return !!this._args['__sandbox']; } diff --git a/src/vs/platform/environment/node/environmentService.ts b/src/vs/platform/environment/node/environmentService.ts index 5ff0cd04726..6ffe5612bc0 100644 --- a/src/vs/platform/environment/node/environmentService.ts +++ b/src/vs/platform/environment/node/environmentService.ts @@ -5,7 +5,6 @@ import { IDebugParams, IExtensionHostDebugParams, INativeEnvironmentService } from 'vs/platform/environment/common/environment'; import { NativeParsedArgs } from 'vs/platform/environment/common/argv'; -import * as crypto from 'crypto'; import * as paths from 'vs/base/node/paths'; import * as os from 'os'; import * as path from 'vs/base/common/path'; @@ -13,9 +12,9 @@ import * as resources from 'vs/base/common/resources'; import { memoize } from 'vs/base/common/decorators'; import product from 'vs/platform/product/common/product'; import { toLocalISOString } from 'vs/base/common/date'; -import { isWindows, Platform, platform } from 'vs/base/common/platform'; import { FileAccess } from 'vs/base/common/network'; import { URI } from 'vs/base/common/uri'; +import { resolveUserDataIPCHandle } from 'vs/base/parts/ipc/node/ipc.net'; export class NativeEnvironmentService implements INativeEnvironmentService { @@ -190,7 +189,7 @@ export class NativeEnvironmentService implements INativeEnvironmentService { get logLevel(): string | undefined { return this._args.log; } @memoize - get sharedIPCHandle(): string { return getIPCHandle(this.userDataPath, 'shared'); } + get sharedIPCHandle(): string { return resolveUserDataIPCHandle(this.userDataPath, 'shared', product.version); } @memoize get serviceMachineIdResource(): URI { return resources.joinPath(URI.file(this.userDataPath), 'machineid'); } @@ -214,51 +213,6 @@ export class NativeEnvironmentService implements INativeEnvironmentService { } } -// Read this before there's any chance it is overwritten -// Related to https://github.com/microsoft/vscode/issues/30624 -export const xdgRuntimeDir = process.env['XDG_RUNTIME_DIR']; - -const safeIpcPathLengths: { [platform: number]: number } = { - [Platform.Linux]: 107, - [Platform.Mac]: 103 -}; - -function getNixIPCHandle(userDataPath: string, type: string): string { - const vscodePortable = process.env['VSCODE_PORTABLE']; - - let result: string; - if (xdgRuntimeDir && !vscodePortable) { - const scope = crypto.createHash('md5').update(userDataPath).digest('hex').substr(0, 8); - result = path.join(xdgRuntimeDir, `vscode-${scope}-${product.version}-${type}.sock`); - } else { - result = path.join(userDataPath, `${product.version}-${type}.sock`); - } - - const limit = safeIpcPathLengths[platform]; - if (typeof limit === 'number') { - if (result.length >= limit) { - // https://nodejs.org/api/net.html#net_identifying_paths_for_ipc_connections - console.warn(`WARNING: IPC handle "${result}" is longer than ${limit} chars, try a shorter --user-data-dir`); - } - } - - return result; -} - -function getWin32IPCHandle(userDataPath: string, type: string): string { - const scope = crypto.createHash('md5').update(userDataPath).digest('hex'); - - return `\\\\.\\pipe\\${scope}-${product.version}-${type}-sock`; -} - -export function getIPCHandle(userDataPath: string, type: string): string { - if (isWindows) { - return getWin32IPCHandle(userDataPath, type); - } - - return getNixIPCHandle(userDataPath, type); -} - export function parseExtensionHostPort(args: NativeParsedArgs, isBuild: boolean): IExtensionHostDebugParams { return parseDebugPort(args['inspect-extensions'], args['inspect-brk-extensions'], 5870, isBuild, args.debugId); } diff --git a/src/vs/workbench/api/node/extHostCLIServer.ts b/src/vs/workbench/api/node/extHostCLIServer.ts index 7cae126cc0f..a7cceda75dd 100644 --- a/src/vs/workbench/api/node/extHostCLIServer.ts +++ b/src/vs/workbench/api/node/extHostCLIServer.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { generateRandomPipeName } from 'vs/base/parts/ipc/node/ipc.net'; +import { generateRandomIPCHandle } from 'vs/base/parts/ipc/node/ipc.net'; import * as http from 'http'; import * as fs from 'fs'; import { IExtHostCommands } from 'vs/workbench/api/common/extHostCommands'; @@ -52,7 +52,7 @@ export class CLIServer { } private async setup(): Promise { - this._ipcHandlePath = generateRandomPipeName(); + this._ipcHandlePath = generateRandomIPCHandle(); try { this._server.listen(this.ipcHandlePath); diff --git a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts index ae70615cbdd..06aa8e136aa 100644 --- a/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts +++ b/src/vs/workbench/services/extensions/electron-browser/localProcessExtensionHost.ts @@ -20,7 +20,7 @@ import { logRemoteEntry } from 'vs/workbench/services/extensions/common/remoteCo import { findFreePort } from 'vs/base/node/ports'; import { IMessagePassingProtocol } from 'vs/base/parts/ipc/common/ipc'; import { PersistentProtocol } from 'vs/base/parts/ipc/common/ipc.net'; -import { generateRandomPipeName, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; +import { generateRandomIPCHandle, NodeSocket } from 'vs/base/parts/ipc/node/ipc.net'; import { INativeWorkbenchEnvironmentService } from 'vs/workbench/services/environment/electron-sandbox/environmentService'; import { ILabelService } from 'vs/platform/label/common/label'; import { ILifecycleService, WillShutdownEvent } from 'vs/platform/lifecycle/common/lifecycle'; @@ -315,7 +315,7 @@ export class LocalProcessExtensionHost implements IExtensionHost { */ private _tryListenOnPipe(): Promise { return new Promise((resolve, reject) => { - const pipeName = generateRandomPipeName(); + const pipeName = generateRandomIPCHandle(); this._namedPipeServer = createServer(); this._namedPipeServer.on('error', reject);