Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Create pty host starter interface and move pty host to main process #178708

Merged
merged 15 commits into from May 12, 2023
Merged
31 changes: 30 additions & 1 deletion src/vs/code/electron-main/app.ts
Expand Up @@ -60,7 +60,7 @@ import { IssueMainService } from 'vs/platform/issue/electron-main/issueMainServi
import { IKeyboardLayoutMainService, KeyboardLayoutMainService } from 'vs/platform/keyboardLayout/electron-main/keyboardLayoutMainService';
import { ILaunchMainService, LaunchMainService } from 'vs/platform/launch/electron-main/launchMainService';
import { ILifecycleMainService, LifecycleMainPhase, ShutdownReason } from 'vs/platform/lifecycle/electron-main/lifecycleMainService';
import { ILogService } from 'vs/platform/log/common/log';
import { ILoggerService, ILogService } from 'vs/platform/log/common/log';
import { IMenubarMainService, MenubarMainService } from 'vs/platform/menubar/electron-main/menubarMainService';
import { INativeHostMainService, NativeHostMainService } from 'vs/platform/native/electron-main/nativeHostMainService';
import { IProductService } from 'vs/platform/product/common/productService';
Expand Down Expand Up @@ -118,6 +118,9 @@ import { massageMessageBoxOptions } from 'vs/platform/dialogs/common/dialogs';
import { IUtilityProcessWorkerMainService, UtilityProcessWorkerMainService } from 'vs/platform/utilityProcess/electron-main/utilityProcessWorkerMainService';
import { ipcUtilityProcessWorkerChannelName } from 'vs/platform/utilityProcess/common/utilityProcessWorkerService';
import { firstOrDefault } from 'vs/base/common/arrays';
import { ILocalPtyService, LocalReconnectConstants, TerminalIpcChannels, TerminalSettingId } from 'vs/platform/terminal/common/terminal';
import { ElectronPtyHostStarter } from 'vs/platform/terminal/electron-main/electronPtyHostStarter';
import { PtyHostService } from 'vs/platform/terminal/node/ptyHostService';

/**
* The main VS Code application. There will only ever be one instance,
Expand All @@ -133,6 +136,7 @@ export class CodeApplication extends Disposable {
private readonly userEnv: IProcessEnvironment,
@IInstantiationService private readonly mainInstantiationService: IInstantiationService,
@ILogService private readonly logService: ILogService,
@ILoggerService private readonly loggerService: ILoggerService,
@IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService,
@ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService,
@IConfigurationService private readonly configurationService: IConfigurationService,
Expand Down Expand Up @@ -922,6 +926,27 @@ export class CodeApplication extends Disposable {
services.set(IStorageMainService, new SyncDescriptor(StorageMainService));
services.set(IApplicationStorageMainService, new SyncDescriptor(ApplicationStorageMainService));

// Terminal
const ptyHostStarter = new ElectronPtyHostStarter({
graceTime: LocalReconnectConstants.GraceTime,
shortGraceTime: LocalReconnectConstants.ShortGraceTime,
scrollback: this.configurationService.getValue<number>(TerminalSettingId.PersistentSessionScrollback) ?? 100
}, false, this.environmentMainService);
const ptyHostService = new PtyHostService(
ptyHostStarter,
this.configurationService,
this.lifecycleMainService,
this.logService,
this.loggerService
);
ptyHostService.initialize();
services.set(ILocalPtyService, ptyHostService);
// services.set(ILocalPtyService, new SyncDescriptor(PtyHostService, [
// ptyHostStarter,
// this.configurationService,
// this.logService
// ]));

// External terminal
if (isWindows) {
services.set(IExternalTerminalMainService, new SyncDescriptor(WindowsExternalTerminalService));
Expand Down Expand Up @@ -1067,6 +1092,10 @@ export class CodeApplication extends Disposable {
const profileStorageListener = this._register(new ProfileStorageChangesListenerChannel(accessor.get(IStorageMainService), accessor.get(IUserDataProfilesMainService), this.logService));
sharedProcessClient.then(client => client.registerChannel('profileStorageListener', profileStorageListener));

// Terminal
const ptyHostChannel = ProxyChannel.fromService(accessor.get(ILocalPtyService));
mainProcessElectronServer.registerChannel(TerminalIpcChannels.LocalPty, ptyHostChannel);

// External Terminal
const externalTerminalChannel = ProxyChannel.fromService(accessor.get(IExternalTerminalMainService));
mainProcessElectronServer.registerChannel('externalTerminal', externalTerminalChannel);
Expand Down
25 changes: 0 additions & 25 deletions src/vs/code/node/sharedProcess/sharedProcessMain.ts
Expand Up @@ -4,8 +4,6 @@
*--------------------------------------------------------------------------------------------*/

/* eslint-disable local/code-layering, local/code-import-patterns */
import { ILocalPtyService } from 'vs/platform/terminal/electron-sandbox/terminal';

import { hostname, release } from 'os';
import { toErrorMessage } from 'vs/base/common/errorMessage';
import { onUnexpectedError, setUnexpectedErrorHandler } from 'vs/base/common/errors';
Expand Down Expand Up @@ -59,8 +57,6 @@ import { TelemetryLogAppender } from 'vs/platform/telemetry/common/telemetryLogA
import { TelemetryService } from 'vs/platform/telemetry/common/telemetryService';
import { supportsTelemetry, ITelemetryAppender, NullAppender, NullTelemetryService, getPiiPathsFromEnvironment, isInternalTelemetry } from 'vs/platform/telemetry/common/telemetryUtils';
import { CustomEndpointTelemetryService } from 'vs/platform/telemetry/node/customEndpointTelemetryService';
import { LocalReconnectConstants, TerminalIpcChannels, TerminalSettingId } from 'vs/platform/terminal/common/terminal';
import { PtyHostService } from 'vs/platform/terminal/node/ptyHostService';
import { ExtensionStorageService, IExtensionStorageService } from 'vs/platform/extensionManagement/common/extensionStorage';
import { IgnoredExtensionsManagementService, IIgnoredExtensionsManagementService } from 'vs/platform/userDataSync/common/ignoredExtensions';
import { IUserDataSyncBackupStoreService, IUserDataSyncLogService, IUserDataSyncEnablementService, IUserDataSyncService, IUserDataSyncStoreManagementService, IUserDataSyncStoreService, IUserDataSyncUtilService, registerConfiguration as registerUserDataSyncConfiguration, IUserDataSyncResourceProviderService } from 'vs/platform/userDataSync/common/userDataSync';
Expand Down Expand Up @@ -338,22 +334,6 @@ class SharedProcessMain extends Disposable {
services.set(IUserDataProfileStorageService, new SyncDescriptor(NativeUserDataProfileStorageService, undefined, true));
services.set(IUserDataSyncResourceProviderService, new SyncDescriptor(UserDataSyncResourceProviderService, undefined, true));

// Terminal

const ptyHostService = new PtyHostService({
graceTime: LocalReconnectConstants.GraceTime,
shortGraceTime: LocalReconnectConstants.ShortGraceTime,
scrollback: configurationService.getValue<number>(TerminalSettingId.PersistentSessionScrollback) ?? 100
},
configurationService,
environmentService,
logService,
loggerService
);
ptyHostService.initialize();

services.set(ILocalPtyService, this._register(ptyHostService));

// Signing
services.set(ISignService, new SyncDescriptor(SignService, undefined, false /* proxied to other processes */));

Expand Down Expand Up @@ -414,11 +394,6 @@ class SharedProcessMain extends Disposable {
const userDataAutoSyncChannel = new UserDataAutoSyncChannel(userDataAutoSync);
this.server.registerChannel('userDataAutoSync', userDataAutoSyncChannel);

// Terminal
const localPtyService = accessor.get(ILocalPtyService);
const localPtyChannel = ProxyChannel.fromService(localPtyService);
this.server.registerChannel(TerminalIpcChannels.LocalPty, localPtyChannel);

// Tunnel
const sharedProcessTunnelChannel = ProxyChannel.fromService(accessor.get(ISharedProcessTunnelService));
this.server.registerChannel(ipcSharedProcessTunnelChannelName, sharedProcessTunnelChannel);
Expand Down
9 changes: 9 additions & 0 deletions src/vs/platform/terminal/common/terminal.ts
Expand Up @@ -922,3 +922,12 @@ export interface ITerminalCommandSelector {
exitStatus: boolean;
commandExitResult: 'success' | 'error';
}

export const ILocalPtyService = createDecorator<ILocalPtyService>('localPtyService');

/**
* A service responsible for communicating with the pty host process on Electron.
*
* **This service should only be used within the terminal component.**
*/
export interface ILocalPtyService extends IPtyService { }
64 changes: 64 additions & 0 deletions src/vs/platform/terminal/electron-main/electronPtyHostStarter.ts
@@ -0,0 +1,64 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { IReconnectConstants } from 'vs/platform/terminal/common/terminal';
import { NodePtyHostStarter } from 'vs/platform/terminal/node/nodePtyHostStarter';
import { IPtyHostConnection, IPtyHostStarter } from 'vs/platform/terminal/node/ptyHost';
// import { FileAccess } from 'vs/base/common/network';
// import { Client, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp';
// import { parsePtyHostDebugPort } from 'vs/platform/environment/node/environmentService';
// import { UtilityProcess } from 'vs/platform/utilityProcess/electron-main/utilityProcess';

export class ElectronPtyHostStarter implements IPtyHostStarter {

// private utilityProcess: UtilityProcess | undefined = undefined;

constructor(
private readonly _reconnectConstants: IReconnectConstants,
private readonly _isRemote: boolean,
@IEnvironmentService private readonly _environmentService: INativeEnvironmentService
) {
}

start(lastPtyId: number): IPtyHostConnection {
return new NodePtyHostStarter(this._reconnectConstants, this._isRemote, this._environmentService).start(lastPtyId);

// console.log('use utility proc');

// // TODO: Convert to use utility process
// const opts: IIPCOptions = {
// serverName: 'Pty Host',
// args: ['--type=ptyHost', '--logsPath', this._environmentService.logsHome.fsPath],
// env: {
// VSCODE_LAST_PTY_ID: lastPtyId,
// VSCODE_PTY_REMOTE: this._isRemote,
// VSCODE_AMD_ENTRYPOINT: 'vs/platform/terminal/node/ptyHostMain',
// VSCODE_PIPE_LOGGING: 'true',
// VSCODE_VERBOSE_LOGGING: 'true', // transmit console logs from server to client,
// VSCODE_RECONNECT_GRACE_TIME: this._reconnectConstants.graceTime,
// VSCODE_RECONNECT_SHORT_GRACE_TIME: this._reconnectConstants.shortGraceTime,
// VSCODE_RECONNECT_SCROLLBACK: this._reconnectConstants.scrollback
// }
// };

// const ptyHostDebug = parsePtyHostDebugPort(this._environmentService.args, this._environmentService.isBuilt);
// if (ptyHostDebug) {
// if (ptyHostDebug.break && ptyHostDebug.port) {
// opts.debugBrk = ptyHostDebug.port;
// } else if (!ptyHostDebug.break && ptyHostDebug.port) {
// opts.debug = ptyHostDebug.port;
// }
Comment on lines +28 to +52
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

commented out code

// }

// const client = new Client(FileAccess.asFileUri('bootstrap-fork').fsPath, opts);

// return {
// client,
// dispose: client.dispose,
// onDidProcessExit: client.onDidProcessExit
// };
}
}
16 changes: 0 additions & 16 deletions src/vs/platform/terminal/electron-sandbox/terminal.ts

This file was deleted.

54 changes: 54 additions & 0 deletions src/vs/platform/terminal/node/nodePtyHostStarter.ts
@@ -0,0 +1,54 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { FileAccess } from 'vs/base/common/network';
import { Client, IIPCOptions } from 'vs/base/parts/ipc/node/ipc.cp';
import { IEnvironmentService, INativeEnvironmentService } from 'vs/platform/environment/common/environment';
import { parsePtyHostDebugPort } from 'vs/platform/environment/node/environmentService';
import { IReconnectConstants } from 'vs/platform/terminal/common/terminal';
import { IPtyHostConnection, IPtyHostStarter } from 'vs/platform/terminal/node/ptyHost';

export class NodePtyHostStarter implements IPtyHostStarter {
constructor(
private readonly _reconnectConstants: IReconnectConstants,
private readonly _isRemote: boolean,
@IEnvironmentService private readonly _environmentService: INativeEnvironmentService
) {
}

start(lastPtyId: number): IPtyHostConnection {
const opts: IIPCOptions = {
serverName: 'Pty Host',
args: ['--type=ptyHost', '--logsPath', this._environmentService.logsHome.fsPath],
env: {
VSCODE_LAST_PTY_ID: lastPtyId,
VSCODE_PTY_REMOTE: this._isRemote,
VSCODE_AMD_ENTRYPOINT: 'vs/platform/terminal/node/ptyHostMain',
VSCODE_PIPE_LOGGING: 'true',
VSCODE_VERBOSE_LOGGING: 'true', // transmit console logs from server to client,
VSCODE_RECONNECT_GRACE_TIME: this._reconnectConstants.graceTime,
VSCODE_RECONNECT_SHORT_GRACE_TIME: this._reconnectConstants.shortGraceTime,
VSCODE_RECONNECT_SCROLLBACK: this._reconnectConstants.scrollback
}
};

const ptyHostDebug = parsePtyHostDebugPort(this._environmentService.args, this._environmentService.isBuilt);
if (ptyHostDebug) {
if (ptyHostDebug.break && ptyHostDebug.port) {
opts.debugBrk = ptyHostDebug.port;
} else if (!ptyHostDebug.break && ptyHostDebug.port) {
opts.debug = ptyHostDebug.port;
}
}

const client = new Client(FileAccess.asFileUri('bootstrap-fork').fsPath, opts);

return {
client,
dispose: client.dispose,
onDidProcessExit: client.onDidProcessExit
};
}
}
23 changes: 23 additions & 0 deletions src/vs/platform/terminal/node/ptyHost.ts
@@ -0,0 +1,23 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/

import { Event } from 'vs/base/common/event';
import { IDisposable } from 'vs/base/common/lifecycle';
import { IChannelClient } from 'vs/base/parts/ipc/common/ipc';

export interface IPtyHostConnection extends IDisposable {
readonly client: IChannelClient;
readonly onDidProcessExit: Event<{ code: number; signal: string }>;
}

export interface IPtyHostStarter {
/**
* Creates a pty host and connects to it.
*
* @param lastPtyId Tracks the last terminal ID from the pty host so we can give it to the new
* pty host if it's restarted and avoid ID conflicts.
*/
start(lastPtyId: number): IPtyHostConnection;
}
15 changes: 11 additions & 4 deletions src/vs/platform/terminal/node/ptyHostMain.ts
Expand Up @@ -5,11 +5,12 @@

import { DefaultURITransformer } from 'vs/base/common/uriIpc';
import { ProxyChannel } from 'vs/base/parts/ipc/common/ipc';
import { Server } from 'vs/base/parts/ipc/node/ipc.cp';
import { Server as ChildProcessServer } from 'vs/base/parts/ipc/node/ipc.cp';
import { Server as UtilityProcessServer } from 'vs/base/parts/ipc/node/ipc.mp';
import { localize } from 'vs/nls';
import { OPTIONS, parseArgs } from 'vs/platform/environment/node/argv';
import { NativeEnvironmentService } from 'vs/platform/environment/node/environmentService';
import { ConsoleLogger, LogLevel } from 'vs/platform/log/common/log';
import { ConsoleLogger, getLogLevel } from 'vs/platform/log/common/log';
import { LoggerChannel } from 'vs/platform/log/common/logIpc';
import { LogService } from 'vs/platform/log/common/logService';
import { LoggerService } from 'vs/platform/log/node/loggerService';
Expand All @@ -18,8 +19,14 @@ import { IProductService } from 'vs/platform/product/common/productService';
import { IReconnectConstants, TerminalIpcChannels } from 'vs/platform/terminal/common/terminal';
import { HeartbeatService } from 'vs/platform/terminal/node/heartbeatService';
import { PtyService } from 'vs/platform/terminal/node/ptyService';
import { isUtilityProcess } from 'vs/base/parts/sandbox/node/electronTypes';

const server = new Server('ptyHost');
let server: ChildProcessServer<string> | UtilityProcessServer;
if (isUtilityProcess(process)) {
server = new UtilityProcessServer();
} else {
server = new ChildProcessServer(TerminalIpcChannels.PtyHost);
}

const lastPtyId = parseInt(process.env.VSCODE_LAST_PTY_ID || '0');
delete process.env.VSCODE_LAST_PTY_ID;
Expand All @@ -28,7 +35,7 @@ const productService: IProductService = { _serviceBrand: undefined, ...product }
const environmentService = new NativeEnvironmentService(parseArgs(process.argv, OPTIONS), productService);

// Logging
const loggerService = new LoggerService(LogLevel.Info, environmentService.logsHome);
const loggerService = new LoggerService(getLogLevel(environmentService), environmentService.logsHome);
server.registerChannel(TerminalIpcChannels.Logger, new LoggerChannel(loggerService, () => DefaultURITransformer));
const logger = loggerService.createLogger('ptyhost', { name: localize('ptyHost', "Pty Host") });
const logService = new LogService(logger, [new ConsoleLogger()]);
Expand Down