stdFork.ts 3.8 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

'use strict';

import path = require('path');
import os = require('os');
import net = require('net');
import cp = require('child_process');
import uri from 'vs/base/common/uri';

export interface IForkOpts {
	cwd?: string;
	env?: any;
	encoding?: string;
	execArgv?: string[];
}

function makeRandomHexString(length:number): string {
	let chars = ['0', '1', '2', '3', '4', '5', '6', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'];
	let result = '';
	for (let i = 0; i < length; i++) {
		let idx = Math.floor(chars.length * Math.random());
		result += chars[idx];
	}
	return result;
}

function generatePipeName(): string {
B
Benjamin Pasero 已提交
32
	let randomName = 'vscode-' + makeRandomHexString(40);
E
Erich Gamma 已提交
33 34 35 36 37 38 39 40
	if (process.platform === 'win32') {
		return '\\\\.\\pipe\\' + randomName + '-sock';
	}

	// Mac/Unix: use socket file
	return path.join(os.tmpdir(), randomName + '.sock');
}

41
function generatePatchedEnv(env:any, stdInPipeName:string, stdOutPipeName:string, stdErrPipeName:string): any {
E
Erich Gamma 已提交
42 43
	// Set the two unique pipe names and the electron flag as process env

B
Benjamin Pasero 已提交
44 45
	let newEnv:any = {};
	for (let key in env) {
E
Erich Gamma 已提交
46 47 48 49 50
		newEnv[key] = env[key];
	}

	newEnv['STDIN_PIPE_NAME'] = stdInPipeName;
	newEnv['STDOUT_PIPE_NAME'] = stdOutPipeName;
51
	newEnv['STDERR_PIPE_NAME'] = stdErrPipeName;
52
	newEnv['ATOM_SHELL_INTERNAL_RUN_AS_NODE'] = '1';
E
Erich Gamma 已提交
53 54 55 56 57 58

	return newEnv;
}

export function fork(modulePath: string, args: string[], options: IForkOpts, callback:(error:any, cp:cp.ChildProcess)=>void): void {

B
Benjamin Pasero 已提交
59 60
	let callbackCalled = false;
	let resolve = (result: cp.ChildProcess) => {
E
Erich Gamma 已提交
61 62 63 64 65 66
		if (callbackCalled) {
			return;
		}
		callbackCalled = true;
		callback(null, result);
	};
B
Benjamin Pasero 已提交
67
	let reject = (err:any) => {
E
Erich Gamma 已提交
68 69 70 71 72 73 74
		if (callbackCalled) {
			return;
		}
		callbackCalled = true;
		callback(err, null);
	};

75
	// Generate three unique pipe names
B
Benjamin Pasero 已提交
76 77
	let stdInPipeName = generatePipeName();
	let stdOutPipeName = generatePipeName();
78
	let stdErrPipeName = generatePipeName();
E
Erich Gamma 已提交
79

80
	let newEnv = generatePatchedEnv(options.env || process.env, stdInPipeName, stdOutPipeName, stdErrPipeName);
E
Erich Gamma 已提交
81

B
Benjamin Pasero 已提交
82
	let childProcess: cp.ChildProcess;
E
Erich Gamma 已提交
83

84 85 86 87 88 89 90
	// Begin listening to stderr pipe
	let stdErrServer = net.createServer((stdErrStream) => {
		// From now on the childProcess.stderr is available for reading
		childProcess.stderr = stdErrStream;
	});
	stdErrServer.listen(stdErrPipeName);

E
Erich Gamma 已提交
91
	// Begin listening to stdout pipe
92
	let stdOutServer = net.createServer((stdOutStream) => {
E
Erich Gamma 已提交
93 94
		// The child process will write exactly one chunk with content `ready` when it has installed a listener to the stdin pipe

95
		stdOutStream.once('data', (chunk:Buffer) => {
E
Erich Gamma 已提交
96 97 98 99
			// The child process is sending me the `ready` chunk, time to connect to the stdin pipe
			childProcess.stdin = <any>net.connect(stdInPipeName);

			// From now on the childProcess.stdout is available for reading
100
			childProcess.stdout = stdOutStream;
E
Erich Gamma 已提交
101 102 103 104

			resolve(childProcess);
		});
	});
105
	stdOutServer.listen(stdOutPipeName);
E
Erich Gamma 已提交
106

B
Benjamin Pasero 已提交
107 108
	let serverClosed = false;
	let closeServer = () => {
E
Erich Gamma 已提交
109 110 111 112
		if (serverClosed) {
			return;
		}
		serverClosed = true;
113 114
		stdOutServer.close();
		stdErrServer.close();
B
Benjamin Pasero 已提交
115
	};
E
Erich Gamma 已提交
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135

	// Create the process
	let bootstrapperPath = (uri.parse(require.toUrl('./stdForkStart.js')).fsPath);
	childProcess = cp.fork(bootstrapperPath, [modulePath].concat(args), {
		silent: true,
		cwd: options.cwd,
		env: newEnv,
		execArgv: options.execArgv
	});

	childProcess.once('error', (err:Error) => {
		closeServer();
		reject(err);
	});

	childProcess.once('exit', (err:Error) => {
		closeServer();
		reject(err);
	});
}