Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/nodejs/node.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorJoyee Cheung <joyeec9h3@gmail.com>2019-12-09 05:53:02 +0300
committerJoyee Cheung <joyeec9h3@gmail.com>2019-12-20 17:10:13 +0300
commitc63d511c137130030c263fb98d0d261105244c08 (patch)
treeffda0e6de496ccb88080a5499ba55a2e28241ca2 /lib
parent277f3436890fac87fe6b762abc00f59d36a9ec6f (diff)
bootstrap: use different scripts to setup different configurations
This patch splits the handling of `isMainThread` and `ownsProcessState` from conditionals in `lib/internal/bootstrap/node.js` into different scripts under `lib/internal/bootstrap/switches/`, and call them accordingly from C++ after `node.js` is run. This: - Creates a common denominator of the main thread and the worker thread bootstrap that can be snapshotted and shared by both. - Makes it possible to override the configurations on-the-fly. PR-URL: https://github.com/nodejs/node/pull/30862 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Chengzhong Wu <legendecas@gmail.com>
Diffstat (limited to 'lib')
-rw-r--r--lib/internal/bootstrap/node.js117
-rw-r--r--lib/internal/bootstrap/pre_execution.js18
-rw-r--r--lib/internal/bootstrap/switches/does_not_own_process_state.js18
-rw-r--r--lib/internal/bootstrap/switches/does_own_process_state.js96
-rw-r--r--lib/internal/bootstrap/switches/is_main_thread.js263
-rw-r--r--lib/internal/bootstrap/switches/is_not_main_thread.js68
-rw-r--r--lib/internal/process/main_thread_only.js173
-rw-r--r--lib/internal/process/signal.js49
-rw-r--r--lib/internal/process/stdio.js206
-rw-r--r--lib/internal/process/worker_thread_only.js36
10 files changed, 514 insertions, 530 deletions
diff --git a/lib/internal/bootstrap/node.js b/lib/internal/bootstrap/node.js
index c586ddfd5e6..bb9a2b177c4 100644
--- a/lib/internal/bootstrap/node.js
+++ b/lib/internal/bootstrap/node.js
@@ -3,9 +3,7 @@
// This file is invoked by `node::RunBootstrapping()` in `src/node.cc`, and is
// responsible for setting up node.js core before executing main scripts
// under `lib/internal/main/`.
-// This file is currently run to bootstrap both the main thread and the worker
-// threads. Some setups are conditional, controlled with isMainThread and
-// ownsProcessState.
+//
// This file is expected not to perform any asynchronous operations itself
// when being executed - those should be done in either
// `lib/internal/bootstrap/pre_execution.js` or in main scripts. The majority
@@ -22,16 +20,21 @@
// module loaders, including `process.binding()`, `process._linkedBinding()`,
// `internalBinding()` and `NativeModule`.
//
-// After this file is run, one of the main scripts under `lib/internal/main/`
-// will be selected by C++ to start the actual execution. The main scripts may
-// run additional setups exported by `lib/internal/bootstrap/pre_execution.js`,
-// depending on the execution mode.
+// This file is run to bootstrap both the main thread and the worker threads.
+// After this file is run, certain properties are setup according to the
+// configuration of the Node.js instance using the files in
+// `lib/internal/bootstrap/switches/`.
+//
+// Then, depending on how the Node.js instance is launched, one of the main
+// scripts in `lib/internal/main` will be selected by C++ to start the actual
+// execution. They may run additional setups exported by
+// `lib/internal/bootstrap/pre_execution.js` depending on the runtime states.
'use strict';
// This file is compiled as if it's wrapped in a function with arguments
// passed by node::RunBootstrapping()
-/* global process, require, internalBinding, isMainThread, ownsProcessState */
+/* global process, require, internalBinding */
setupPrepareStackTrace();
@@ -54,48 +57,12 @@ setupBuffer();
process.domain = null;
process._exiting = false;
-// Bootstrappers for all threads, including worker threads and main thread
-const perThreadSetup = require('internal/process/per_thread');
-// Bootstrappers for the main thread only
-let mainThreadSetup;
-// Bootstrappers for the worker threads only
-let workerThreadSetup;
-if (ownsProcessState) {
- mainThreadSetup = require(
- 'internal/process/main_thread_only'
- );
-} else {
- workerThreadSetup = require(
- 'internal/process/worker_thread_only'
- );
-}
-
// process.config is serialized config.gypi
process.config = JSONParse(internalBinding('native_module').config);
+// Bootstrappers for all threads, including worker threads and main thread
+const perThreadSetup = require('internal/process/per_thread');
const rawMethods = internalBinding('process_methods');
-// Set up methods and events on the process object for the main thread
-if (isMainThread) {
- process.abort = rawMethods.abort;
- const wrapped = mainThreadSetup.wrapProcessMethods(rawMethods);
- process.umask = wrapped.umask;
- process.chdir = wrapped.chdir;
- process.cwd = wrapped.cwd;
-
- // TODO(joyeecheung): deprecate and remove these underscore methods
- process._debugProcess = rawMethods._debugProcess;
- process._debugEnd = rawMethods._debugEnd;
- process._startProfilerIdleNotifier =
- rawMethods._startProfilerIdleNotifier;
- process._stopProfilerIdleNotifier = rawMethods._stopProfilerIdleNotifier;
-} else {
- const wrapped = workerThreadSetup.wrapProcessMethods(rawMethods);
-
- process.abort = workerThreadSetup.unavailable('process.abort()');
- process.chdir = workerThreadSetup.unavailable('process.chdir()');
- process.umask = wrapped.umask;
- process.cwd = rawMethods.cwd;
-}
// Set up methods on the process object for all threads
{
@@ -119,6 +86,11 @@ if (isMainThread) {
process.memoryUsage = wrapped.memoryUsage;
process.kill = wrapped.kill;
process.exit = wrapped.exit;
+
+ process.openStdin = function() {
+ process.stdin.resume();
+ return process.stdin;
+ };
}
const credentials = internalBinding('credentials');
@@ -128,34 +100,6 @@ if (credentials.implementsPosixCredentials) {
process.getgid = credentials.getgid;
process.getegid = credentials.getegid;
process.getgroups = credentials.getgroups;
-
- if (ownsProcessState) {
- const wrapped = mainThreadSetup.wrapPosixCredentialSetters(credentials);
- process.initgroups = wrapped.initgroups;
- process.setgroups = wrapped.setgroups;
- process.setegid = wrapped.setegid;
- process.seteuid = wrapped.seteuid;
- process.setgid = wrapped.setgid;
- process.setuid = wrapped.setuid;
- } else {
- process.initgroups =
- workerThreadSetup.unavailable('process.initgroups()');
- process.setgroups = workerThreadSetup.unavailable('process.setgroups()');
- process.setegid = workerThreadSetup.unavailable('process.setegid()');
- process.seteuid = workerThreadSetup.unavailable('process.seteuid()');
- process.setgid = workerThreadSetup.unavailable('process.setgid()');
- process.setuid = workerThreadSetup.unavailable('process.setuid()');
- }
-}
-
-if (isMainThread) {
- const { getStdout, getStdin, getStderr } =
- require('internal/process/stdio').getMainThreadStdio();
- setupProcessStdio(getStdout, getStdin, getStderr);
-} else {
- const { getStdout, getStdin, getStderr } =
- workerThreadSetup.createStdioGetters();
- setupProcessStdio(getStdout, getStdin, getStderr);
}
// Setup the callbacks that node::AsyncWrap will call when there are hooks to
@@ -343,31 +287,6 @@ function setupProcessObject() {
});
}
-function setupProcessStdio(getStdout, getStdin, getStderr) {
- ObjectDefineProperty(process, 'stdout', {
- configurable: true,
- enumerable: true,
- get: getStdout
- });
-
- ObjectDefineProperty(process, 'stderr', {
- configurable: true,
- enumerable: true,
- get: getStderr
- });
-
- ObjectDefineProperty(process, 'stdin', {
- configurable: true,
- enumerable: true,
- get: getStdin
- });
-
- process.openStdin = function() {
- process.stdin.resume();
- return process.stdin;
- };
-}
-
function setupGlobalProxy() {
ObjectDefineProperty(global, SymbolToStringTag, {
value: 'global',
diff --git a/lib/internal/bootstrap/pre_execution.js b/lib/internal/bootstrap/pre_execution.js
index 40dabdda504..7af4be660fc 100644
--- a/lib/internal/bootstrap/pre_execution.js
+++ b/lib/internal/bootstrap/pre_execution.js
@@ -36,9 +36,6 @@ function prepareMainThreadExecution(expandArgv1 = false) {
setupDebugEnv();
- // Only main thread receives signals.
- setupSignalHandlers();
-
// Process initial diagnostic reporting configuration, if present.
initializeReport();
initializeReportSignalHandlers(); // Main-thread-only.
@@ -174,20 +171,7 @@ function setupDebugEnv() {
}
}
-function setupSignalHandlers() {
- const {
- createSignalHandlers
- } = require('internal/process/main_thread_only');
- const {
- startListeningIfSignal,
- stopListeningIfSignal
- } = createSignalHandlers();
- process.on('newListener', startListeningIfSignal);
- process.on('removeListener', stopListeningIfSignal);
-}
-
-// This has to be called after both initializeReport() and
-// setupSignalHandlers() are called
+// This has to be called after initializeReport() is called
function initializeReportSignalHandlers() {
if (!getOptionValue('--experimental-report')) {
return;
diff --git a/lib/internal/bootstrap/switches/does_not_own_process_state.js b/lib/internal/bootstrap/switches/does_not_own_process_state.js
new file mode 100644
index 00000000000..1ec449b34a9
--- /dev/null
+++ b/lib/internal/bootstrap/switches/does_not_own_process_state.js
@@ -0,0 +1,18 @@
+'use strict';
+
+const credentials = internalBinding('credentials');
+
+if (credentials.implementsPosixCredentials) {
+ // TODO: this should be detached from ERR_WORKER_UNSUPPORTED_OPERATION
+ const { unavailable } = require('internal/process/worker_thread_only');
+
+ process.initgroups = unavailable('process.initgroups()');
+ process.setgroups = unavailable('process.setgroups()');
+ process.setegid = unavailable('process.setegid()');
+ process.seteuid = unavailable('process.seteuid()');
+ process.setgid = unavailable('process.setgid()');
+ process.setuid = unavailable('process.setuid()');
+}
+
+// ---- keep the attachment of the wrappers above so that it's easier to ----
+// ---- compare the setups side-by-side -----
diff --git a/lib/internal/bootstrap/switches/does_own_process_state.js b/lib/internal/bootstrap/switches/does_own_process_state.js
new file mode 100644
index 00000000000..88f77520720
--- /dev/null
+++ b/lib/internal/bootstrap/switches/does_own_process_state.js
@@ -0,0 +1,96 @@
+'use strict';
+
+const credentials = internalBinding('credentials');
+
+if (credentials.implementsPosixCredentials) {
+ const wrapped = wrapPosixCredentialSetters(credentials);
+
+ process.initgroups = wrapped.initgroups;
+ process.setgroups = wrapped.setgroups;
+ process.setegid = wrapped.setegid;
+ process.seteuid = wrapped.seteuid;
+ process.setgid = wrapped.setgid;
+ process.setuid = wrapped.setuid;
+}
+
+// ---- keep the attachment of the wrappers above so that it's easier to ----
+// ---- compare the setups side-by-side -----
+
+function wrapPosixCredentialSetters(credentials) {
+ const {
+ ArrayIsArray,
+ } = primordials;
+ const {
+ codes: {
+ ERR_INVALID_ARG_TYPE,
+ ERR_UNKNOWN_CREDENTIAL
+ }
+ } = require('internal/errors');
+ const {
+ validateUint32
+ } = require('internal/validators');
+
+ const {
+ initgroups: _initgroups,
+ setgroups: _setgroups,
+ setegid: _setegid,
+ seteuid: _seteuid,
+ setgid: _setgid,
+ setuid: _setuid
+ } = credentials;
+
+ function initgroups(user, extraGroup) {
+ validateId(user, 'user');
+ validateId(extraGroup, 'extraGroup');
+ // Result is 0 on success, 1 if user is unknown, 2 if group is unknown.
+ const result = _initgroups(user, extraGroup);
+ if (result === 1) {
+ throw new ERR_UNKNOWN_CREDENTIAL('User', user);
+ } else if (result === 2) {
+ throw new ERR_UNKNOWN_CREDENTIAL('Group', extraGroup);
+ }
+ }
+
+ function setgroups(groups) {
+ if (!ArrayIsArray(groups)) {
+ throw new ERR_INVALID_ARG_TYPE('groups', 'Array', groups);
+ }
+ for (let i = 0; i < groups.length; i++) {
+ validateId(groups[i], `groups[${i}]`);
+ }
+ // Result is 0 on success. A positive integer indicates that the
+ // corresponding group was not found.
+ const result = _setgroups(groups);
+ if (result > 0) {
+ throw new ERR_UNKNOWN_CREDENTIAL('Group', groups[result - 1]);
+ }
+ }
+
+ function wrapIdSetter(type, method) {
+ return function(id) {
+ validateId(id, 'id');
+ // Result is 0 on success, 1 if credential is unknown.
+ const result = method(id);
+ if (result === 1) {
+ throw new ERR_UNKNOWN_CREDENTIAL(type, id);
+ }
+ };
+ }
+
+ function validateId(id, name) {
+ if (typeof id === 'number') {
+ validateUint32(id, name);
+ } else if (typeof id !== 'string') {
+ throw new ERR_INVALID_ARG_TYPE(name, ['number', 'string'], id);
+ }
+ }
+
+ return {
+ initgroups,
+ setgroups,
+ setegid: wrapIdSetter('Group', _setegid),
+ seteuid: wrapIdSetter('User', _seteuid),
+ setgid: wrapIdSetter('Group', _setgid),
+ setuid: wrapIdSetter('User', _setuid)
+ };
+}
diff --git a/lib/internal/bootstrap/switches/is_main_thread.js b/lib/internal/bootstrap/switches/is_main_thread.js
new file mode 100644
index 00000000000..4f7ef7b6a69
--- /dev/null
+++ b/lib/internal/bootstrap/switches/is_main_thread.js
@@ -0,0 +1,263 @@
+'use strict';
+
+const { ObjectDefineProperty } = primordials;
+const rawMethods = internalBinding('process_methods');
+
+process.abort = rawMethods.abort;
+process.umask = wrappedUmask;
+process.chdir = wrappedChdir;
+process.cwd = wrappedCwd;
+
+// TODO(joyeecheung): deprecate and remove these underscore methods
+process._debugProcess = rawMethods._debugProcess;
+process._debugEnd = rawMethods._debugEnd;
+process._startProfilerIdleNotifier = rawMethods._startProfilerIdleNotifier;
+process._stopProfilerIdleNotifier = rawMethods._stopProfilerIdleNotifier;
+
+function defineStream(name, getter) {
+ ObjectDefineProperty(process, name, {
+ configurable: true,
+ enumerable: true,
+ get: getter
+ });
+}
+
+defineStream('stdout', getStdout);
+defineStream('stdin', getStdin);
+defineStream('stderr', getStderr);
+
+// Worker threads don't receive signals.
+const {
+ startListeningIfSignal,
+ stopListeningIfSignal
+} = require('internal/process/signal');
+process.on('newListener', startListeningIfSignal);
+process.on('removeListener', stopListeningIfSignal);
+
+// ---- keep the attachment of the wrappers above so that it's easier to ----
+// ---- compare the setups side-by-side -----
+
+const { guessHandleType } = internalBinding('util');
+const {
+ parseMode,
+ validateString
+} = require('internal/validators');
+
+// Cache the working directory to prevent lots of lookups. If the working
+// directory is changed by `chdir`, it'll be updated.
+let cachedCwd = '';
+
+function wrappedChdir(directory) {
+ validateString(directory, 'directory');
+ rawMethods.chdir(directory);
+ // Mark cache that it requires an update.
+ cachedCwd = '';
+}
+
+function wrappedUmask(mask) {
+ if (mask !== undefined) {
+ mask = parseMode(mask, 'mask');
+ }
+ return rawMethods.umask(mask);
+}
+
+function wrappedCwd() {
+ if (cachedCwd === '')
+ cachedCwd = rawMethods.cwd();
+ return cachedCwd;
+}
+
+function createWritableStdioStream(fd) {
+ let stream;
+ // Note stream._type is used for test-module-load-list.js
+ switch (guessHandleType(fd)) {
+ case 'TTY':
+ const tty = require('tty');
+ stream = new tty.WriteStream(fd);
+ stream._type = 'tty';
+ break;
+
+ case 'FILE':
+ const SyncWriteStream = require('internal/fs/sync_write_stream');
+ stream = new SyncWriteStream(fd, { autoClose: false });
+ stream._type = 'fs';
+ break;
+
+ case 'PIPE':
+ case 'TCP':
+ const net = require('net');
+
+ // If fd is already being used for the IPC channel, libuv will return
+ // an error when trying to use it again. In that case, create the socket
+ // using the existing handle instead of the fd.
+ if (process.channel && process.channel.fd === fd) {
+ stream = new net.Socket({
+ handle: process.channel,
+ readable: false,
+ writable: true
+ });
+ } else {
+ stream = new net.Socket({
+ fd,
+ readable: false,
+ writable: true
+ });
+ }
+
+ stream._type = 'pipe';
+ break;
+
+ default:
+ // Provide a dummy black-hole output for e.g. non-console
+ // Windows applications.
+ const { Writable } = require('stream');
+ stream = new Writable({
+ write(buf, enc, cb) {
+ cb();
+ }
+ });
+ }
+
+ // For supporting legacy API we put the FD here.
+ stream.fd = fd;
+
+ stream._isStdio = true;
+
+ return stream;
+}
+
+function dummyDestroy(err, cb) {
+ cb(err);
+
+ // We need to emit 'close' anyway so that the closing
+ // of the stream is observable. We just make sure we
+ // are not going to do it twice.
+ // The 'close' event is needed so that finished and
+ // pipeline work correctly.
+ if (!this._writableState.emitClose) {
+ process.nextTick(() => {
+ this.emit('close');
+ });
+ }
+}
+
+let stdin;
+let stdout;
+let stderr;
+
+function getStdout() {
+ if (stdout) return stdout;
+ stdout = createWritableStdioStream(1);
+ stdout.destroySoon = stdout.destroy;
+ // Override _destroy so that the fd is never actually closed.
+ stdout._destroy = dummyDestroy;
+ if (stdout.isTTY) {
+ process.on('SIGWINCH', () => stdout._refreshSize());
+ }
+ return stdout;
+}
+
+function getStderr() {
+ if (stderr) return stderr;
+ stderr = createWritableStdioStream(2);
+ stderr.destroySoon = stderr.destroy;
+ // Override _destroy so that the fd is never actually closed.
+ stderr._destroy = dummyDestroy;
+ if (stderr.isTTY) {
+ process.on('SIGWINCH', () => stderr._refreshSize());
+ }
+ return stderr;
+}
+
+function getStdin() {
+ if (stdin) return stdin;
+ const fd = 0;
+
+ switch (guessHandleType(fd)) {
+ case 'TTY':
+ const tty = require('tty');
+ stdin = new tty.ReadStream(fd, {
+ highWaterMark: 0,
+ readable: true,
+ writable: false
+ });
+ break;
+
+ case 'FILE':
+ const fs = require('fs');
+ stdin = new fs.ReadStream(null, { fd: fd, autoClose: false });
+ break;
+
+ case 'PIPE':
+ case 'TCP':
+ const net = require('net');
+
+ // It could be that process has been started with an IPC channel
+ // sitting on fd=0, in such case the pipe for this fd is already
+ // present and creating a new one will lead to the assertion failure
+ // in libuv.
+ if (process.channel && process.channel.fd === fd) {
+ stdin = new net.Socket({
+ handle: process.channel,
+ readable: true,
+ writable: false,
+ manualStart: true
+ });
+ } else {
+ stdin = new net.Socket({
+ fd: fd,
+ readable: true,
+ writable: false,
+ manualStart: true
+ });
+ }
+ // Make sure the stdin can't be `.end()`-ed
+ stdin._writableState.ended = true;
+ break;
+
+ default:
+ // Provide a dummy contentless input for e.g. non-console
+ // Windows applications.
+ const { Readable } = require('stream');
+ stdin = new Readable({ read() {} });
+ stdin.push(null);
+ }
+
+ // For supporting legacy API we put the FD here.
+ stdin.fd = fd;
+
+ // `stdin` starts out life in a paused state, but node doesn't
+ // know yet. Explicitly to readStop() it to put it in the
+ // not-reading state.
+ if (stdin._handle && stdin._handle.readStop) {
+ stdin._handle.reading = false;
+ stdin._readableState.reading = false;
+ stdin._handle.readStop();
+ }
+
+ // If the user calls stdin.pause(), then we need to stop reading
+ // once the stream implementation does so (one nextTick later),
+ // so that the process can close down.
+ stdin.on('pause', () => {
+ process.nextTick(onpause);
+ });
+
+ function onpause() {
+ if (!stdin._handle)
+ return;
+ if (stdin._handle.reading && !stdin.readableFlowing) {
+ stdin._readableState.reading = false;
+ stdin._handle.reading = false;
+ stdin._handle.readStop();
+ }
+ }
+
+ return stdin;
+}
+
+// Used by internal tests.
+rawMethods.resetStdioForTesting = function() {
+ stdin = undefined;
+ stdout = undefined;
+ stderr = undefined;
+};
diff --git a/lib/internal/bootstrap/switches/is_not_main_thread.js b/lib/internal/bootstrap/switches/is_not_main_thread.js
new file mode 100644
index 00000000000..33e98d387cf
--- /dev/null
+++ b/lib/internal/bootstrap/switches/is_not_main_thread.js
@@ -0,0 +1,68 @@
+'use strict';
+
+const { ObjectDefineProperty } = primordials;
+const rawMethods = internalBinding('process_methods');
+const {
+ unavailable
+} = require('internal/process/worker_thread_only');
+
+process.abort = unavailable('process.abort()');
+process.chdir = unavailable('process.chdir()');
+process.umask = wrappedUmask;
+process.cwd = rawMethods.cwd;
+
+delete process._debugProcess;
+delete process._debugEnd;
+delete process._startProfilerIdleNotifier;
+delete process._stopProfilerIdleNotifier;
+
+function defineStream(name, getter) {
+ ObjectDefineProperty(process, name, {
+ configurable: true,
+ enumerable: true,
+ get: getter
+ });
+}
+
+defineStream('stdout', getStdout);
+defineStream('stdin', getStdin);
+defineStream('stderr', getStderr);
+
+// Worker threads don't receive signals.
+const {
+ startListeningIfSignal,
+ stopListeningIfSignal
+} = require('internal/process/signal');
+process.removeListener('newListener', startListeningIfSignal);
+process.removeListener('removeListener', stopListeningIfSignal);
+
+// ---- keep the attachment of the wrappers above so that it's easier to ----
+// ---- compare the setups side-by-side -----
+
+const {
+ createWorkerStdio
+} = require('internal/worker/io');
+const {
+ codes: { ERR_WORKER_UNSUPPORTED_OPERATION }
+} = require('internal/errors');
+
+let workerStdio;
+function lazyWorkerStdio() {
+ if (!workerStdio) workerStdio = createWorkerStdio();
+ return workerStdio;
+}
+
+function getStdout() { return lazyWorkerStdio().stdout; }
+
+function getStderr() { return lazyWorkerStdio().stderr; }
+
+function getStdin() { return lazyWorkerStdio().stdin; }
+
+function wrappedUmask(mask) {
+ // process.umask() is a read-only operation in workers.
+ if (mask !== undefined) {
+ throw new ERR_WORKER_UNSUPPORTED_OPERATION('Setting process.umask()');
+ }
+
+ return rawMethods.umask(mask);
+}
diff --git a/lib/internal/process/main_thread_only.js b/lib/internal/process/main_thread_only.js
deleted file mode 100644
index ab56500744b..00000000000
--- a/lib/internal/process/main_thread_only.js
+++ /dev/null
@@ -1,173 +0,0 @@
-'use strict';
-
-// This file contains process bootstrappers that can only be
-// run in the main thread
-
-const {
- ArrayIsArray,
-} = primordials;
-
-const {
- errnoException,
- codes: {
- ERR_INVALID_ARG_TYPE,
- ERR_UNKNOWN_CREDENTIAL
- }
-} = require('internal/errors');
-const {
- parseMode,
- validateUint32,
- validateString
-} = require('internal/validators');
-
-const { signals } = internalBinding('constants').os;
-
-// The execution of this function itself should not cause any side effects.
-function wrapProcessMethods(binding) {
- // Cache the working directory to prevent lots of lookups. If the working
- // directory is changed by `chdir`, it'll be updated.
- let cachedCwd = '';
-
- function chdir(directory) {
- validateString(directory, 'directory');
- binding.chdir(directory);
- // Mark cache that it requires an update.
- cachedCwd = '';
- }
-
- function umask(mask) {
- if (mask !== undefined) {
- mask = parseMode(mask, 'mask');
- }
- return binding.umask(mask);
- }
-
- function cwd() {
- if (cachedCwd === '')
- cachedCwd = binding.cwd();
- return cachedCwd;
- }
-
- return {
- chdir,
- umask,
- cwd
- };
-}
-
-function wrapPosixCredentialSetters(credentials) {
- const {
- initgroups: _initgroups,
- setgroups: _setgroups,
- setegid: _setegid,
- seteuid: _seteuid,
- setgid: _setgid,
- setuid: _setuid
- } = credentials;
-
- function initgroups(user, extraGroup) {
- validateId(user, 'user');
- validateId(extraGroup, 'extraGroup');
- // Result is 0 on success, 1 if user is unknown, 2 if group is unknown.
- const result = _initgroups(user, extraGroup);
- if (result === 1) {
- throw new ERR_UNKNOWN_CREDENTIAL('User', user);
- } else if (result === 2) {
- throw new ERR_UNKNOWN_CREDENTIAL('Group', extraGroup);
- }
- }
-
- function setgroups(groups) {
- if (!ArrayIsArray(groups)) {
- throw new ERR_INVALID_ARG_TYPE('groups', 'Array', groups);
- }
- for (let i = 0; i < groups.length; i++) {
- validateId(groups[i], `groups[${i}]`);
- }
- // Result is 0 on success. A positive integer indicates that the
- // corresponding group was not found.
- const result = _setgroups(groups);
- if (result > 0) {
- throw new ERR_UNKNOWN_CREDENTIAL('Group', groups[result - 1]);
- }
- }
-
- function wrapIdSetter(type, method) {
- return function(id) {
- validateId(id, 'id');
- // Result is 0 on success, 1 if credential is unknown.
- const result = method(id);
- if (result === 1) {
- throw new ERR_UNKNOWN_CREDENTIAL(type, id);
- }
- };
- }
-
- function validateId(id, name) {
- if (typeof id === 'number') {
- validateUint32(id, name);
- } else if (typeof id !== 'string') {
- throw new ERR_INVALID_ARG_TYPE(name, ['number', 'string'], id);
- }
- }
-
- return {
- initgroups,
- setgroups,
- setegid: wrapIdSetter('Group', _setegid),
- seteuid: wrapIdSetter('User', _seteuid),
- setgid: wrapIdSetter('Group', _setgid),
- setuid: wrapIdSetter('User', _setuid)
- };
-}
-
-let Signal;
-function isSignal(event) {
- return typeof event === 'string' && signals[event] !== undefined;
-}
-
-// Worker threads don't receive signals.
-function createSignalHandlers() {
- const signalWraps = new Map();
-
- // Detect presence of a listener for the special signal types
- function startListeningIfSignal(type) {
- if (isSignal(type) && !signalWraps.has(type)) {
- if (Signal === undefined)
- Signal = internalBinding('signal_wrap').Signal;
- const wrap = new Signal();
-
- wrap.unref();
-
- wrap.onsignal = process.emit.bind(process, type, type);
-
- const signum = signals[type];
- const err = wrap.start(signum);
- if (err) {
- wrap.close();
- throw errnoException(err, 'uv_signal_start');
- }
-
- signalWraps.set(type, wrap);
- }
- }
-
- function stopListeningIfSignal(type) {
- const wrap = signalWraps.get(type);
- if (wrap !== undefined && process.listenerCount(type) === 0) {
- wrap.close();
- signalWraps.delete(type);
- }
- }
-
- return {
- startListeningIfSignal,
- stopListeningIfSignal
- };
-}
-
-module.exports = {
- wrapProcessMethods,
- createSignalHandlers,
- wrapPosixCredentialSetters
-};
diff --git a/lib/internal/process/signal.js b/lib/internal/process/signal.js
new file mode 100644
index 00000000000..6929c73c51f
--- /dev/null
+++ b/lib/internal/process/signal.js
@@ -0,0 +1,49 @@
+'use strict';
+
+const {
+ errnoException,
+} = require('internal/errors');
+
+const { signals } = internalBinding('constants').os;
+
+let Signal;
+const signalWraps = new Map();
+
+function isSignal(event) {
+ return typeof event === 'string' && signals[event] !== undefined;
+}
+
+// Detect presence of a listener for the special signal types
+function startListeningIfSignal(type) {
+ if (isSignal(type) && !signalWraps.has(type)) {
+ if (Signal === undefined)
+ Signal = internalBinding('signal_wrap').Signal;
+ const wrap = new Signal();
+
+ wrap.unref();
+
+ wrap.onsignal = process.emit.bind(process, type, type);
+
+ const signum = signals[type];
+ const err = wrap.start(signum);
+ if (err) {
+ wrap.close();
+ throw errnoException(err, 'uv_signal_start');
+ }
+
+ signalWraps.set(type, wrap);
+ }
+}
+
+function stopListeningIfSignal(type) {
+ const wrap = signalWraps.get(type);
+ if (wrap !== undefined && process.listenerCount(type) === 0) {
+ wrap.close();
+ signalWraps.delete(type);
+ }
+}
+
+module.exports = {
+ startListeningIfSignal,
+ stopListeningIfSignal
+};
diff --git a/lib/internal/process/stdio.js b/lib/internal/process/stdio.js
deleted file mode 100644
index 2491f14cdf0..00000000000
--- a/lib/internal/process/stdio.js
+++ /dev/null
@@ -1,206 +0,0 @@
-'use strict';
-
-const { guessHandleType } = internalBinding('util');
-exports.getMainThreadStdio = getMainThreadStdio;
-
-function dummyDestroy(err, cb) {
- cb(err);
-
- // We need to emit 'close' anyway so that the closing
- // of the stream is observable. We just make sure we
- // are not going to do it twice.
- // The 'close' event is needed so that finished and
- // pipeline work correctly.
- if (!this._writableState.emitClose) {
- process.nextTick(() => {
- this.emit('close');
- });
- }
-}
-
-function getMainThreadStdio() {
- let stdin;
- let stdout;
- let stderr;
-
- function getStdout() {
- if (stdout) return stdout;
- stdout = createWritableStdioStream(1);
- stdout.destroySoon = stdout.destroy;
- // Override _destroy so that the fd is never actually closed.
- stdout._destroy = dummyDestroy;
- if (stdout.isTTY) {
- process.on('SIGWINCH', () => stdout._refreshSize());
- }
- return stdout;
- }
-
- function getStderr() {
- if (stderr) return stderr;
- stderr = createWritableStdioStream(2);
- stderr.destroySoon = stderr.destroy;
- // Override _destroy so that the fd is never actually closed.
- stderr._destroy = dummyDestroy;
- if (stderr.isTTY) {
- process.on('SIGWINCH', () => stderr._refreshSize());
- }
- return stderr;
- }
-
- function getStdin() {
- if (stdin) return stdin;
- const fd = 0;
-
- switch (guessHandleType(fd)) {
- case 'TTY':
- const tty = require('tty');
- stdin = new tty.ReadStream(fd, {
- highWaterMark: 0,
- readable: true,
- writable: false
- });
- break;
-
- case 'FILE':
- const fs = require('fs');
- stdin = new fs.ReadStream(null, { fd: fd, autoClose: false });
- break;
-
- case 'PIPE':
- case 'TCP':
- const net = require('net');
-
- // It could be that process has been started with an IPC channel
- // sitting on fd=0, in such case the pipe for this fd is already
- // present and creating a new one will lead to the assertion failure
- // in libuv.
- if (process.channel && process.channel.fd === fd) {
- stdin = new net.Socket({
- handle: process.channel,
- readable: true,
- writable: false,
- manualStart: true
- });
- } else {
- stdin = new net.Socket({
- fd: fd,
- readable: true,
- writable: false,
- manualStart: true
- });
- }
- // Make sure the stdin can't be `.end()`-ed
- stdin._writableState.ended = true;
- break;
-
- default:
- // Provide a dummy contentless input for e.g. non-console
- // Windows applications.
- const { Readable } = require('stream');
- stdin = new Readable({ read() {} });
- stdin.push(null);
- }
-
- // For supporting legacy API we put the FD here.
- stdin.fd = fd;
-
- // `stdin` starts out life in a paused state, but node doesn't
- // know yet. Explicitly to readStop() it to put it in the
- // not-reading state.
- if (stdin._handle && stdin._handle.readStop) {
- stdin._handle.reading = false;
- stdin._readableState.reading = false;
- stdin._handle.readStop();
- }
-
- // If the user calls stdin.pause(), then we need to stop reading
- // once the stream implementation does so (one nextTick later),
- // so that the process can close down.
- stdin.on('pause', () => {
- process.nextTick(onpause);
- });
-
- function onpause() {
- if (!stdin._handle)
- return;
- if (stdin._handle.reading && !stdin.readableFlowing) {
- stdin._readableState.reading = false;
- stdin._handle.reading = false;
- stdin._handle.readStop();
- }
- }
-
- return stdin;
- }
-
- exports.resetStdioForTesting = function() {
- stdin = undefined;
- stdout = undefined;
- stderr = undefined;
- };
-
- return {
- getStdout,
- getStderr,
- getStdin
- };
-}
-
-function createWritableStdioStream(fd) {
- let stream;
- // Note stream._type is used for test-module-load-list.js
- switch (guessHandleType(fd)) {
- case 'TTY':
- const tty = require('tty');
- stream = new tty.WriteStream(fd);
- stream._type = 'tty';
- break;
-
- case 'FILE':
- const SyncWriteStream = require('internal/fs/sync_write_stream');
- stream = new SyncWriteStream(fd, { autoClose: false });
- stream._type = 'fs';
- break;
-
- case 'PIPE':
- case 'TCP':
- const net = require('net');
-
- // If fd is already being used for the IPC channel, libuv will return
- // an error when trying to use it again. In that case, create the socket
- // using the existing handle instead of the fd.
- if (process.channel && process.channel.fd === fd) {
- stream = new net.Socket({
- handle: process.channel,
- readable: false,
- writable: true
- });
- } else {
- stream = new net.Socket({
- fd,
- readable: false,
- writable: true
- });
- }
-
- stream._type = 'pipe';
- break;
-
- default:
- // Provide a dummy black-hole output for e.g. non-console
- // Windows applications.
- const { Writable } = require('stream');
- stream = new Writable({
- write(buf, enc, cb) {
- cb();
- }
- });
- }
-
- // For supporting legacy API we put the FD here.
- stream.fd = fd;
-
- stream._isStdio = true;
-
- return stream;
-}
diff --git a/lib/internal/process/worker_thread_only.js b/lib/internal/process/worker_thread_only.js
index 920f0f77261..9d2a43b441a 100644
--- a/lib/internal/process/worker_thread_only.js
+++ b/lib/internal/process/worker_thread_only.js
@@ -4,41 +4,9 @@
// run in the worker thread.
const {
- createWorkerStdio
-} = require('internal/worker/io');
-
-const {
codes: { ERR_WORKER_UNSUPPORTED_OPERATION }
} = require('internal/errors');
-let workerStdio;
-function lazyWorkerStdio() {
- if (!workerStdio) workerStdio = createWorkerStdio();
- return workerStdio;
-}
-
-function createStdioGetters() {
- return {
- getStdout() { return lazyWorkerStdio().stdout; },
- getStderr() { return lazyWorkerStdio().stderr; },
- getStdin() { return lazyWorkerStdio().stdin; }
- };
-}
-
-// The execution of this function itself should not cause any side effects.
-function wrapProcessMethods(binding) {
- function umask(mask) {
- // process.umask() is a read-only operation in workers.
- if (mask !== undefined) {
- throw new ERR_WORKER_UNSUPPORTED_OPERATION('Setting process.umask()');
- }
-
- return binding.umask(mask);
- }
-
- return { umask };
-}
-
function unavailable(name) {
function unavailableInWorker() {
throw new ERR_WORKER_UNSUPPORTED_OPERATION(name);
@@ -49,7 +17,5 @@ function unavailable(name) {
}
module.exports = {
- createStdioGetters,
- unavailable,
- wrapProcessMethods
+ unavailable
};