src/vs/base/node/processes.ts

180 LOC · 65 covered · 115 uncovered · 8 ranges · 2354 concepts · 2 introducers · 1128 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- processes.ts ×5
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import * as cp from 'child_process';
7 > import { Stats, promises } from 'fs';
8 > import { getCaseInsensitive } from '../common/objects.js';
9 > import * as path from '../common/path.js';
10 > import * as Platform from '../common/platform.js';
11 > import * as processCommon from '../common/process.js';
12 > import { CommandOptions, ForkOptions, Source, SuccessData, TerminateResponse, TerminateResponseCode } from '../common/processes.js';
13 > import * as Types from '../common/types.js';
14 > import * as pfs from './pfs.js';
15 > import { FileAccess } from '../common/network.js';
16 > import Stream from 'stream';
17 > export { Source, TerminateResponseCode, type CommandOptions, type ForkOptions, type SuccessData, type TerminateResponse };
18 >
19 > export type ValueCallback<T> = (value: T | Promise<T>) => void;
20 > export type ErrorCallback = (error?: any) => void;
21 > export type ProgressCallback<T> = (progress: T) => void;
22 >
23 >
24 > export function getWindowsShell(env = processCommon.env): string {
25 return env['comspec'] || 'cmd.exe';
26 }
28 > export interface IQueuedSender {
29 > send: (msg: any) => void;
30 > }
31 >
32 > // Wrapper around process.send() that will queue any messages if the internal node.js
33 > // queue is filled with messages and only continue sending messages when the internal
34 > // queue is free again to consume messages.
35 > // On Windows we always wait for the send() method to return before sending the next message
36 > // to workaround https://github.com/nodejs/node/issues/7657 (IPC can freeze process)
37 > export function createQueuedSender(childProcess: cp.ChildProcess): IQueuedSender {
38 let msgQueue: string[] = [];
39 let useQueue = false;
40
41 const send = function (msg: any): void {
42 if (useQueue) {
43 msgQueue.push(msg); // add to the queue if the process cannot handle more messages
44 return;
45 }
46
47 const result = childProcess.send(msg, (error: Error | null) => {
48 if (error) {
49 console.error(error); // unlikely to happen, best we can do is log this error
50 }
51
52 useQueue = false; // we are good again to send directly without queue
53
54 // now send all the messages that we have in our queue and did not send yet
55 if (msgQueue.length > 0) {
56 const msgQueueCopy = msgQueue.slice(0);
57 msgQueue = [];
58 msgQueueCopy.forEach(entry => send(entry));
59 }
60 });
61
62 if (!result || Platform.isWindows /* workaround https://github.com/nodejs/node/issues/7657 */) {
63 useQueue = true;
64 }
65 };
66
67 return { send };
68 }
70 async function fileExistsDefault(path: string): Promise<boolean> {
71 if (await pfs.Promises.exists(path)) {
72 let statValue: Stats | undefined;
73 try {
74 statValue = await promises.stat(path);
75 } catch (e) {
76 if (e.message.startsWith('EACCES')) {
77 // it might be symlink
78 statValue = await promises.lstat(path);
79 }
80 }
81 return statValue ? !statValue.isDirectory() : false;
82 }
83 return false;
84 }
86 export async function findExecutable(command: string, cwd?: string, paths?: string[], env: Platform.IProcessEnvironment = processCommon.env, fileExists: (path: string) => Promise<boolean> = fileExistsDefault): Promise<string | undefined> {
87 // If we have an absolute path then we take it.
88 if (path.isAbsolute(command)) {
89 return await fileExists(command) ? command : undefined;
90 }
91 if (cwd === undefined) {
92 cwd = processCommon.cwd();
93 }
94 const dir = path.dirname(command);
95 if (dir !== '.') {
96 // We have a directory and the directory is relative (see above). Make the path absolute
97 // to the current working directory.
98 const fullPath = path.join(cwd, command);
99 return await fileExists(fullPath) ? fullPath : undefined;
100 }
101 const envPath = getCaseInsensitive(env, 'PATH');
102 if (paths === undefined && Types.isString(envPath)) {
103 paths = envPath.split(path.delimiter);
104 }
105 // No PATH environment. Make path absolute to the cwd.
106 if (paths === undefined || paths.length === 0) {
107 const fullPath = path.join(cwd, command);
108 return await fileExists(fullPath) ? fullPath : undefined;
109 }
110
111 // We have a simple file name. We get the path variable from the env
112 // and try to find the executable on the path.
113 for (const pathEntry of paths) {
114 // The path entry is absolute.
115 let fullPath: string;
116 if (path.isAbsolute(pathEntry)) {
117 fullPath = path.join(pathEntry, command);
118 } else {
119 fullPath = path.join(cwd, pathEntry, command);
120 }
121 if (Platform.isWindows) {
122 const pathExt = getCaseInsensitive(env, 'PATHEXT') as string || '.COM;.EXE;.BAT;.CMD';
123 const pathExtsFound = pathExt.split(';').map(async ext => {
124 const withExtension = fullPath + ext;
125 return await fileExists(withExtension) ? withExtension : undefined;
126 });
127 for (const foundPromise of pathExtsFound) {
128 const found = await foundPromise;
129 if (found) {
130 return found;
131 }
132 }
133 }
134
135 if (await fileExists(fullPath)) {
136 return fullPath;
137 }
138 }
139 const fullPath = path.join(cwd, command);
140 return await fileExists(fullPath) ? fullPath : undefined;
141 }
143 > /**
144 > * Kills a process and all its children.
145 > * @param pid the process id to kill
146 > * @param forceful whether to forcefully kill the process (default: false). Note
147 > * that on Windows, terminal processes can _only_ be killed forcefully and this
148 > * will throw when not forceful.
149 > */
150 > export async function killTree(pid: number, forceful = false) { mcpStdioStateHandler.ts ×8
151 > let child: cp.ChildProcessByStdio<null, Stream.Readable, Stream.Readable>;
152 > if (Platform.isWindows) {
153 const windir = process.env['WINDIR'] || 'C:\\Windows';
154 const taskKill = path.join(windir, 'System32', 'taskkill.exe');
155
156 const args = ['/T'];
157 if (forceful) {
158 args.push('/F');
159 }
160 args.push('/PID', String(pid));
161 child = cp.spawn(taskKill, args, { stdio: ['ignore', 'pipe', 'pipe'] });
163 > const killScript = FileAccess.asFileUri('vs/base/node/terminateProcess.sh').fsPath;
164 > child = cp.spawn('/bin/sh', [killScript, String(pid), forceful ? '9' : '15'], { stdio: ['ignore', 'pipe', 'pipe'] });
165 > }
166 >
167 > return new Promise<void>((resolve, reject) => {
168 > const stdout: Buffer[] = [];
169 > child.stdout.on('data', (data) => stdout.push(data));
170 > child.stderr.on('data', (data) => stdout.push(data));
171 > child.on('error', reject);
172 > child.on('exit', (code) => {
173 > if (code === 0) {
174 > resolve();
175 > } else {
176 reject(new Error(`taskkill exited with code ${code}: ${Buffer.concat(stdout).toString()}`));
177 }
179 > });
180 > }