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

bootstrap: use different scripts to setup different configurations #30862

Closed
wants to merge 2 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
117 changes: 18 additions & 99 deletions lib/internal/bootstrap/node.js
Expand Up @@ -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
Expand All @@ -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();

Expand All @@ -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
{
Expand All @@ -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');
Expand All @@ -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
Expand Down Expand Up @@ -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',
Expand Down
18 changes: 1 addition & 17 deletions lib/internal/bootstrap/pre_execution.js
Expand Up @@ -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.
Expand Down Expand Up @@ -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;
Expand Down
18 changes: 18 additions & 0 deletions 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 -----
96 changes: 96 additions & 0 deletions 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)
};
}