repo_description: Node.js JavaScript runtime ✨🐢🚀✨
repo_topics: javascript js linux macos mit node nodejs runtime windows
code: // Copyright Joyent, Inc. and other Node contributors.
  //
  // Permission is hereby granted, free of charge, to any person obtaining a
  // copy of this software and associated documentation files (the
  // "Software"), to deal in the Software without restriction, including
  // without limitation the rights to use, copy, modify, merge, publish,
  // distribute, sublicense, and/or sell copies of the Software, and to permit
  // persons to whom the Software is furnished to do so, subject to the
  // following conditions:
  //
  // The above copyright notice and this permission notice shall be included
  // in all copies or substantial portions of the Software.
  //
  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
  // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
  // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
  // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
  // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
  // USE OR OTHER DEALINGS IN THE SOFTWARE.
  
  'use strict';
  
  const {
    ArrayIsArray,
    ArrayPrototypeFilter,
    ArrayPrototypeIncludes,
    ArrayPrototypeJoin,
    ArrayPrototypeLastIndexOf,
    ArrayPrototypePush,
    ArrayPrototypePushApply,
    ArrayPrototypeSlice,
    ArrayPrototypeSort,
    ArrayPrototypeSplice,
    ArrayPrototypeUnshift,
    ObjectAssign,
    ObjectDefineProperty,
    ObjectPrototypeHasOwnProperty,
    PromiseWithResolvers,
    RegExpPrototypeExec,
    SafeSet,
    StringPrototypeIncludes,
    StringPrototypeSlice,
    StringPrototypeToUpperCase,
    SymbolDispose,
  } = primordials;
  
  const {
    assignFunctionName,
    convertToValidSignal,
    getSystemErrorName,
    kEmptyObject,
    promisify,
  } = require('internal/util');
  const { isArrayBufferView } = require('internal/util/types');
  let debug = require('internal/util/debuglog').debuglog(
    'child_process',
    (fn) => {
      debug = fn;
    },
  );
  const { Buffer } = require('buffer');
  const { Pipe, constants: PipeConstants } = internalBinding('pipe_wrap');
  
  const {
    AbortError,
    codes: {
      ERR_CHILD_PROCESS_IPC_REQUIRED,
      ERR_CHILD_PROCESS_STDIO_MAXBUFFER,
      ERR_INVALID_ARG_TYPE,
      ERR_INVALID_ARG_VALUE,
    },
    genericNodeError,
  } = require('internal/errors');
  const { clearTimeout, setTimeout } = require('timers');
  const { getValidatedPath } = require('internal/fs/utils');
  const {
    validateAbortSignal,
    validateArray,
    validateBoolean,
    validateFunction,
    validateInteger,
    validateInt32,
    validateNumber,
    validateObject,
    validateString,
  } = require('internal/validators');
  const child_process = require('internal/child_process');
  const {
    getValidStdio,
    setupChannel,
    ChildProcess,
    stdioStringToArray,
  } = child_process;
  
  const MAX_BUFFER = 1024 * 1024;
  
  const permission = require('internal/process/permission');
  
  const isZOS = process.platform === 'os390';
  let addAbortListener;
  
  /**
   * Spawns a new Node.js process + fork.
   * @param {string|URL} modulePath
   * @param {string[]} [args]
   * @param {{
   *   cwd?: string | URL;
   *   detached?: boolean;
   *   env?: Record<string, string>;
   *   execPath?: string;
   *   execArgv?: string[];
   *   gid?: number;
   *   serialization?: string;
   *   signal?: AbortSignal;
   *   killSignal?: string | number;
   *   silent?: boolean;
   *   stdio?: Array | string;
   *   uid?: number;
   *   windowsVerbatimArguments?: boolean;
   *   timeout?: number;
   *   }} [options]
   * @returns {ChildProcess}
   */
  function fork(modulePath, args = [], options) {
    modulePath = getValidatedPath(modulePath, 'modulePath');
  
    // Get options and args arguments.
    let execArgv;
  
    if (args == null) {
      args = [];
    } else if (typeof args === 'object' && !ArrayIsArray(args)) {
      options = args;
      args = [];
    } else {
      validateArray(args, 'args');
    }
  
    if (options != null) {
      validateObject(options, 'options');
    }
    options = { __proto__: null, ...options, shell: false };
    options.execPath ||= process.execPath;
    validateArgumentNullCheck(options.execPath, 'options.execPath');
  
    // Prepare arguments for fork:
    execArgv = options.execArgv || process.execArgv;
    validateArgumentsNullCheck(execArgv, 'options.execArgv');
  
    if (execArgv === process.execArgv && process._eval != null) {
      const index = ArrayPrototypeLastIndexOf(execArgv, process._eval);
      if (index > 0) {
        // Remove the -e switch to avoid fork bombing ourselves.
        execArgv = ArrayPrototypeSlice(execArgv);
        ArrayPrototypeSplice(execArgv, index - 1, 2);
      }
    }
  
    args = [...execArgv, modulePath, ...args];
  
    if (typeof options.stdio === 'string') {
      options.stdio = stdioStringToArray(options.stdio, 'ipc');
    } else if (!ArrayIsArray(options.stdio)) {
      // Use a separate fd=3 for the IPC channel. Inherit stdin, stdout,
      // and stderr from the parent if silent isn't set.
      options.stdio = stdioStringToArray(
        options.silent ? 'pipe' : 'inherit',
        'ipc');
    } else if (!ArrayPrototypeIncludes(options.stdio, 'ipc')) {
      throw new ERR_CHILD_PROCESS_IPC_REQUIRED('options.stdio');
    }
  
    return spawn(options.execPath, args, options);
  }
  
  function _forkChild(fd, serializationMode) {
    // set process.send()
    const p = new Pipe(PipeConstants.IPC);
    p.open(fd);
    p.unref();
    const control = setupChannel(process, p, serializationMode);
    process.on('newListener', function onNewListener(name) {
      if (name === 'message' || name === 'disconnect') control.refCounted();
    });
    process.on('removeListener', function onRemoveListener(name) {
      if (name === 'message' || name === 'disconnect') control.unrefCounted();
    });
  }
  
  function normalizeExecArgs(command, options, callback) {
    validateString(command, 'command');
    validateArgumentNullCheck(command, 'command');
  
    if (typeof options === 'function') {
      callback = options;
      options = undefined;
    }
  
    // Make a shallow copy so we don't clobber the user's options object.
    options = { __proto__: null, ...options };
    options.shell = typeof options.shell === 'string' ? options.shell : true;
  
    return {
      file: command,
      options: options,
      callback: callback,
    };
  }
  
  /**
   * Spawns a shell executing the given command.
   * @param {string} command
   * @param {{
   *   cmd?: string;
   *   env?: Record<string, string>;
   *   encoding?: string;
   *   shell?: string;
   *   signal?: AbortSignal;
   *   timeout?: number;
   *   maxBuffer?: number;
   *   killSignal?: string | number;
   *   uid?: number;
   *   gid?: number;
   *   windowsHide?: boolean;
   *   }} [options]
   * @param {(
   *   error?: Error,
   *   stdout?: string | Buffer,
   *   stderr?: string | Buffer
   *   ) => any} [callback]
   * @returns {ChildProcess}
   */
  function exec(command, options, callback) {
    const opts = normalizeExecArgs(command, options, callback);
    return module.exports.execFile(opts.file,
                                   opts.options,
                                   opts.callback);
  }
  
  const customPromiseExecFunction = (orig) => {
    return assignFunctionName(orig.name, function(...args) {
      const { promise, resolve, reject } = PromiseWithResolvers();
  
      promise.child = orig(...args, (err, stdout, stderr) => {
        if (err !== null) {
          err.stdout = stdout;
          err.stderr = stderr;
          reject(err);
        } else {
          resolve({ stdout, stderr });
        }
      });
  
      return promise;
    });
  };
  
  ObjectDefineProperty(exec, promisify.custom, {
    __proto__: null,
    enumerable: false,
    value: customPromiseExecFunction(exec),
  });
  
  function normalizeExecFileArgs(file, args, options, callback) {
    if (ArrayIsArray(args)) {
      args = ArrayPrototypeSlice(args);
    } else if (args != null && typeof args === 'object') {
      callback = options;
      options = args;
      args = null;
    } else if (typeof args === 'function') {
      callback = args;
      options = null;
      args = null;
    }
  
    args ??= [];
  
    if (typeof options === 'function') {
      callback = options;
    } else if (options != null) {
      validateObject(options, 'options');
    }
  
    options ??= kEmptyObject;
  
    if (callback != null) {
      validateFunction(callback, 'callback');
    }
  
    // Validate argv0, if present.
    if (options.argv0 != null) {
      validateString(options.argv0, 'options.argv0');
      validateArgumentNullCheck(options.argv0, 'options.argv0');
    }
  
    return { file, args, options, callback };
  }
  
  /**
   * Spawns the specified file as a shell.
   * @param {string} file
   * @param {string[]} [args]
   * @param {{
   *   cwd?: string | URL;
   *   env?: Record<string, string>;
   *   encoding?: string;
   *   timeout?: number;
   *   maxBuffer?: number;
   *   killSignal?: string | number;
   *   uid?: number;
   *   gid?: number;
   *   windowsHide?: boolean;
   *   windowsVerbatimArguments?: boolean;
   *   shell?: boolean | string;
   *   signal?: AbortSignal;
   *   }} [options]
   * @param {(
   *   error?: Error,
   *   stdout?: string | Buffer,
   *   stderr?: string | Buffer
   *   ) => any} [callback]
   * @returns {ChildProcess}
   */
  function execFile(file, args, options, callback) {
    ({ file, args, options, callback } = normalizeExecFileArgs(file, args, options, callback));
  
    options = {
      __proto__: null,
      encoding: 'utf8',
      timeout: 0,
      maxBuffer: MAX_BUFFER,
      killSignal: 'SIGTERM',
      cwd: null,
      env: null,
      shell: false,
      ...options,
    };
  
    // Validate the timeout, if present.
    validateTimeout(options.timeout);
  
    // Validate maxBuffer, if present.
    validateMaxBuffer(options.maxBuffer);
  
    options.killSignal = sanitizeKillSignal(options.killSignal);
  
    const child = spawn(file, args, {
      cwd: options.cwd,
      env: options.env,
      gid: options.gid,
      shell: options.shell,
      signal: options.signal,
      uid: options.uid,
      windowsHide: !!options.windowsHide,
      windowsVerbatimArguments: !!options.windowsVerbatimArguments,
    });
  
    let encoding;
    const _stdout = [];
    const _stderr = [];
    if (options.encoding !== 'buffer' && Buffer.isEncoding(options.encoding)) {
      encoding = options.encoding;
    } else {
      encoding = null;
    }
    let stdoutLen = 0;
    let stderrLen = 0;
    let killed = false;
    let exited = false;
    let timeoutId;
  
    let ex = null;
  
    let cmd = file;
  
    function exithandler(code, signal) {
      if (exited) return;
      exited = true;
  
      if (timeoutId) {
        clearTimeout(timeoutId);
        timeoutId = null;
      }
  
      if (!callback) return;
  
      // merge chunks
      let stdout;
      let stderr;
      if (encoding ||
        (
          child.stdout?.readableEncoding
        )) {
        stdout = ArrayPrototypeJoin(_stdout, '');
      } else {
        stdout = Buffer.concat(_stdout);
      }
      if (encoding ||
        (
          child.stderr?.readableEncoding
        )) {
        stderr = ArrayPrototypeJoin(_stderr, '');
      } else {
        stderr = Buffer.concat(_stderr);
      }
  
      if (!ex && code === 0 && signal === null) {
        callback(null, stdout, stderr);
        return;
      }
  
      if (args?.length)
        cmd += ` ${ArrayPrototypeJoin(args, ' ')}`;
  
      ex ||= genericNodeError(`Command failed: ${cmd}\n${stderr}`, {
        code: code < 0 ? getSystemErrorName(code) : code,
        killed: child.killed || killed,
        signal: signal,
      });
  
      ex.cmd = cmd;
      callback(ex, stdout, stderr);
    }
  
    function errorhandler(e) {
      ex = e;
  
      if (child.stdout)
        child.stdout.destroy();
  
      if (child.stderr)
        child.stderr.destroy();
  
      exithandler();
    }
  
    function kill() {
      if (child.stdout)
        child.stdout.destroy();
  
      if (child.stderr)
        child.stderr.destroy();
  
      killed = true;
      try {
        child.kill(options.killSignal);
      } catch (e) {
        ex = e;
        exithandler();
      }
    }
  
    if (options.timeout > 0) {
      timeoutId = setTimeout(function delayedKill() {
        kill();
        timeoutId = null;
      }, options.timeout);
    }
  
    if (child.stdout) {
      if (encoding)
        child.stdout.setEncoding(encoding);
  
      child.stdout.on('data', function onChildStdout(chunk) {
        // Do not need to count the length
        if (options.maxBuffer === Infinity) {
          ArrayPrototypePush(_stdout, chunk);
          return;
        }
        const encoding = child.stdout.readableEncoding;
        const length = encoding ?
          Buffer.byteLength(chunk, encoding) :
          chunk.length;
        const slice = encoding ? StringPrototypeSlice :
          (buf, ...args) => buf.slice(...args);
        stdoutLen += length;
  
        if (stdoutLen > options.maxBuffer) {
          const truncatedLen = options.maxBuffer - (stdoutLen - length);
          ArrayPrototypePush(_stdout, slice(chunk, 0, truncatedLen));
  
          ex = new ERR_CHILD_PROCESS_STDIO_MAXBUFFER('stdout');
          kill();
        } else {
          ArrayPrototypePush(_stdout, chunk);
        }
      });
    }
  
    if (child.stderr) {
      if (encoding)
        child.stderr.setEncoding(encoding);
  
      child.stderr.on('data', function onChildStderr(chunk) {
        // Do not need to count the length
        if (options.maxBuffer === Infinity) {
          ArrayPrototypePush(_stderr, chunk);
          return;
        }
        const encoding = child.stderr.readableEncoding;
        const length = encoding ?
          Buffer.byteLength(chunk, encoding) :
          chunk.length;
        stderrLen += length;
  
        if (stderrLen > options.maxBuffer) {
          const truncatedLen = options.maxBuffer - (stderrLen - length);
          ArrayPrototypePush(_stderr,
                             chunk.slice(0, truncatedLen));
  
          ex = new ERR_CHILD_PROCESS_STDIO_MAXBUFFER('stderr');
          kill();
        } else {
          ArrayPrototypePush(_stderr, chunk);
        }
      });
    }
  
    child.addListener('close', exithandler);
    child.addListener('error', errorhandler);
  
    return child;
  }
  
  ObjectDefineProperty(execFile, promisify.custom, {
    __proto__: null,
    enumerable: false,
    value: customPromiseExecFunction(execFile),
  });
  
  function copyProcessEnvToEnv(env, name, optionEnv) {
    if (process.env[name] &&
        (!optionEnv ||
         !ObjectPrototypeHasOwnProperty(optionEnv, name))) {
      env[name] = process.env[name];
    }
  }
  
  let permissionModelFlagsToCopy;
  
  function getPermissionModelFlagsToCopy() {
    if (permissionModelFlagsToCopy === undefined) {
      permissionModelFlagsToCopy = [...permission.availableFlags(), '--permission'];
    }
    return permissionModelFlagsToCopy;
  }
  
  function copyPermissionModelFlagsToEnv(env, key, args) {
    // Do not override if permission was already passed to file
    if (args.includes('--permission') || (env[key] && env[key].indexOf('--permission') !== -1)) {
      return;
    }
  
    const flagsToCopy = getPermissionModelFlagsToCopy();
    for (const arg of process.execArgv) {
      for (const flag of flagsToCopy) {
        if (arg.startsWith(flag)) {
          env[key] = `${env[key] ? env[key] + ' ' + arg : arg}`;
        }
      }
    }
  }
  
  let emittedDEP0190Already = false;
  function normalizeSpawnArguments(file, args, options) {
    validateString(file, 'file');
    validateArgumentNullCheck(file, 'file');
  
    if (file.length === 0)
      throw new ERR_INVALID_ARG_VALUE('file', file, 'cannot be empty');
  
    if (ArrayIsArray(args)) {
      args = ArrayPrototypeSlice(args);
    } else if (args == null) {
      args = [];
    } else if (typeof args !== 'object') {
      throw new ERR_INVALID_ARG_TYPE('args', 'object', args);
    } else {
      options = args;
      args = [];
    }
  
    validateArgumentsNullCheck(args, 'args');
  
    if (options === undefined)
      options = kEmptyObject;
    else
      validateObject(options, 'options');
  
    options = { __proto__: null, ...options };
    let cwd = options.cwd;
  
    // Validate the cwd, if present.
    if (cwd != null) {
      cwd = getValidatedPath(cwd, 'options.cwd');
    }
  
    // Validate detached, if present.
    if (options.detached != null) {
      validateBoolean(options.detached, 'options.detached');
    }
  
    // Validate the uid, if present.
    if (options.uid != null) {
      validateInt32(options.uid, 'options.uid');
    }
  
    // Validate the gid, if present.
    if (options.gid != null) {
      validateInt32(options.gid, 'options.gid');
    }
  
    // Validate the shell, if present.
    if (options.shell != null &&
        typeof options.shell !== 'boolean' &&
        typeof options.shell !== 'string') {
      throw new ERR_INVALID_ARG_TYPE('options.shell',
                                     ['boolean', 'string'], options.shell);
    }
  
    // Validate argv0, if present.
    if (options.argv0 != null) {
      validateString(options.argv0, 'options.argv0');
      validateArgumentNullCheck(options.argv0, 'options.argv0');
    }
  
    // Validate windowsHide, if present.
    if (options.windowsHide != null) {
      validateBoolean(options.windowsHide, 'options.windowsHide');
    }
  
    // Validate windowsVerbatimArguments, if present.
    let { windowsVerbatimArguments } = options;
    if (windowsVerbatimArguments != null) {
      validateBoolean(windowsVerbatimArguments,
                      'options.windowsVerbatimArguments');
    }
  
    if (options.shell) {
      validateArgumentNullCheck(options.shell, 'options.shell');
      if (args.length > 0 && !emittedDEP0190Already) {
        process.emitWarning(
          'Passing args to a child process with shell option true can lead to security ' +
          'vulnerabilities, as the arguments are not escaped, only concatenated.',
          'DeprecationWarning',
          'DEP0190');
        emittedDEP0190Already = true;
      }
  
      const command = args.length > 0 ? `${file} ${ArrayPrototypeJoin(args, ' ')}` : file;
      // Set the shell, switches, and commands.
      if (process.platform === 'win32') {
        if (typeof options.shell === 'string')
          file = options.shell;
        else
          file = process.env.comspec || 'cmd.exe';
        // '/d /s /c' is used only for cmd.exe.
        if (RegExpPrototypeExec(/^(?:.*\\)?cmd(?:\.exe)?$/i, file) !== null) {
          args = ['/d', '/s', '/c', `"${command}"`];
          windowsVerbatimArguments = true;
        } else {
          args = ['-c', command];
        }
      } else {
        if (typeof options.shell === 'string')
          file = options.shell;
        else if (process.platform === 'android')
          file = '/system/bin/sh';
        else
          file = '/bin/sh';
        args = ['-c', command];
      }
    }
  
    if (typeof options.argv0 === 'string') {
      ArrayPrototypeUnshift(args, options.argv0);
    } else {
      ArrayPrototypeUnshift(args, file);
    }
  
    // Shallow copy to guarantee changes won't impact process.env
    const env = options.env || { ...process.env };
    const envPairs = [];
  
    // process.env.NODE_V8_COVERAGE always propagates, making it possible to
    // collect coverage for programs that spawn with white-listed environment.
    copyProcessEnvToEnv(env, 'NODE_V8_COVERAGE', options.env);
  
    if (isZOS) {
      // The following environment variables must always propagate if set.
      copyProcessEnvToEnv(env, '_BPXK_AUTOCVT', options.env);
      copyProcessEnvToEnv(env, '_CEE_RUNOPTS', options.env);
      copyProcessEnvToEnv(env, '_TAG_REDIR_ERR', options.env);
      copyProcessEnvToEnv(env, '_TAG_REDIR_IN', options.env);
      copyProcessEnvToEnv(env, '_TAG_REDIR_OUT', options.env);
      copyProcessEnvToEnv(env, 'STEPLIB', options.env);
      copyProcessEnvToEnv(env, 'LIBPATH', options.env);
      copyProcessEnvToEnv(env, '_EDC_SIG_DFLT', options.env);
      copyProcessEnvToEnv(env, '_EDC_SUSV3', options.env);
    }
  
    if (permission.isEnabled()) {
      copyPermissionModelFlagsToEnv(env, 'NODE_OPTIONS', args);
    }
  
    let envKeys = [];
    // Prototype values are intentionally included.
    for (const key in env) {
      ArrayPrototypePush(envKeys, key);
    }
  
    if (process.platform === 'win32') {
      // On Windows env keys are case insensitive. Filter out duplicates,
      // keeping only the first one (in lexicographic order)
      const sawKey = new SafeSet();
      envKeys = ArrayPrototypeFilter(
        ArrayPrototypeSort(envKeys),
        (key) => {
          const uppercaseKey = StringPrototypeToUpperCase(key);
          if (sawKey.has(uppercaseKey)) {
            return false;
          }
          sawKey.add(uppercaseKey);
          return true;
        },
      );
    }
  
    for (const key of envKeys) {
      const value = env[key];
      if (value !== undefined) {
        validateArgumentNullCheck(key, `options.env['${key}']`);
        validateArgumentNullCheck(value, `options.env['${key}']`);
        ArrayPrototypePush(envPairs, `${key}=${value}`);
      }
    }
  
    return {
      // Make a shallow copy so we don't clobber the user's options object.
      __proto__: null,
      ...options,
      args,
      cwd,
      detached: !!options.detached,
      envPairs,
      file,
      windowsHide: !!options.windowsHide,
      windowsVerbatimArguments: !!windowsVerbatimArguments,
    };
  }
  
  function abortChildProcess(child, killSignal, reason) {
    if (!child)
      return;
    try {
      if (child.kill(killSignal)) {
        child.emit('error', new AbortError(undefined, { cause: reason }));
      }
    } catch (err) {
      child.emit('error', err);
    }
  }
  
  /**
   * Spawns a new process using the given `file`.
   * @param {string} file
   * @param {string[]} [args]
   * @param {{
   *   cwd?: string | URL;
   *   env?: Record<string, string>;
   *   argv0?: string;
   *   stdio?: Array | string;
   *   detached?: boolean;
   *   uid?: number;
   *   gid?: number;
   *   serialization?: string;
   *   shell?: boolean | string;
   *   windowsVerbatimArguments?: boolean;
   *   windowsHide?: boolean;
   *   signal?: AbortSignal;
   *   timeout?: number;
   *   killSignal?: string | number;
   *   }} [options]
   * @returns {ChildProcess}
   */
  function spawn(file, args, options) {
    options = normalizeSpawnArguments(file, args, options);
    validateTimeout(options.timeout);
    validateAbortSignal(options.signal, 'options.signal');
    const killSignal = sanitizeKillSignal(options.killSignal);
    const child = new ChildProcess();
  
    debug('spawn', options);
    child.spawn(options);
  
    if (options.timeout > 0) {
      let timeoutId = setTimeout(() => {
        if (timeoutId) {
          try {
            child.kill(killSignal);
          } catch (err) {
            child.emit('error', err);
          }
          timeoutId = null;
        }
      }, options.timeout);
  
      child.once('exit', () => {
        if (timeoutId) {
          clearTimeout(timeoutId);
          timeoutId = null;
        }
      });
    }
  
    if (options.signal) {
      const signal = options.signal;
      if (signal.aborted) {
        process.nextTick(onAbortListener);
      } else {
        addAbortListener ??= require('internal/events/abort_listener').addAbortListener;
        const disposable = addAbortListener(signal, onAbortListener);
        child.once('exit', disposable[SymbolDispose]);
      }
  
      function onAbortListener() {
        abortChildProcess(child, killSignal, options.signal.reason);
      }
    }
  
    return child;
  }
  
  /**
   * Spawns a new process synchronously using the given `file`.
   * @param {string} file
   * @param {string[]} [args]
   * @param {{
   *   cwd?: string | URL;
   *   input?: string | Buffer | TypedArray | DataView;
   *   argv0?: string;
   *   stdio?: string | Array;
   *   env?: Record<string, string>;
   *   uid?: number;
   *   gid?: number;
   *   timeout?: number;
   *   killSignal?: string | number;
   *   maxBuffer?: number;
   *   encoding?: string;
   *   shell?: boolean | string;
   *   windowsVerbatimArguments?: boolean;
   *   windowsHide?: boolean;
   *   }} [options]
   * @returns {{
   *   pid: number;
   *   output: Array;
   *   stdout: Buffer | string;
   *   stderr: Buffer | string;
   *   status: number | null;
   *   signal: string | null;
   *   error: Error;
   *   }}
   */
  function spawnSync(file, args, options) {
    options = {
      __proto__: null,
      maxBuffer: MAX_BUFFER,
      ...normalizeSpawnArguments(file, args, options),
    };
  
    debug('spawnSync', options);
  
    // Validate the timeout, if present.
    validateTimeout(options.timeout);
  
    // Validate maxBuffer, if present.
    validateMaxBuffer(options.maxBuffer);
  
    // Validate and translate the kill signal, if present.
    options.killSignal = sanitizeKillSignal(options.killSignal);
  
    options.stdio = getValidStdio(options.stdio || 'pipe', true).stdio;
  
    if (options.input) {
      const stdin = options.stdio[0] = { ...options.stdio[0] };
      stdin.input = options.input;
    }
  
    // We may want to pass data in on any given fd, ensure it is a valid buffer
    for (let i = 0; i < options.stdio.length; i++) {
      const input = options.stdio[i]?.input;
      if (input != null) {
        const pipe = options.stdio[i] = { ...options.stdio[i] };
        if (isArrayBufferView(input)) {
          pipe.input = input;
        } else if (typeof input === 'string') {
          pipe.input = Buffer.from(input, options.encoding);
        } else {
          throw new ERR_INVALID_ARG_TYPE(`options.stdio[${i}]`,
                                         ['Buffer',
                                          'TypedArray',
                                          'DataView',
                                          'string'],
                                         input);
        }
      }
    }
  
    return child_process.spawnSync(options);
  }
  
  
  function checkExecSyncError(ret, args, cmd) {
    let err;
    if (ret.error) {
      err = ret.error;
      ObjectAssign(err, ret);
    } else if (ret.status !== 0) {
      let msg = 'Command failed: ';
      msg += cmd || ArrayPrototypeJoin(args, ' ');
      if (ret.stderr && ret.stderr.length > 0)
        msg += `\n${ret.stderr.toString()}`;
      err = genericNodeError(msg, ret);
    }
    return err;
  }
  
  /**
   * Spawns a file as a shell synchronously.
   * @param {string} file
   * @param {string[]} [args]
   * @param {{
   *   cwd?: string | URL;
   *   input?: string | Buffer | TypedArray | DataView;
   *   stdio?: string | Array;
   *   env?: Record<string, string>;
   *   uid?: number;
   *   gid?: number;
   *   timeout?: number;
   *   killSignal?: string | number;
   *   maxBuffer?: number;
   *   encoding?: string;
   *   windowsHide?: boolean;
   *   shell?: boolean | string;
   *   }} [options]
   * @returns {Buffer | string}
   */
  function execFileSync(file, args, options) {
    ({ file, args, options } = normalizeExecFileArgs(file, args, options));
  
    const inheritStderr = !options.stdio;
    const ret = spawnSync(file, args, options);
  
    if (inheritStderr && ret.stderr)
      process.stderr.write(ret.stderr);
  
    const errArgs = [options.argv0 || file];
    ArrayPrototypePushApply(errArgs, args);
    const err = checkExecSyncError(ret, errArgs);
  
    if (err)
      throw err;
  
    return ret.stdout;
  }
  
  /**
   * Spawns a shell executing the given `command` synchronously.
   * @param {string} command
   * @param {{
   *   cwd?: string | URL;
   *   input?: string | Buffer | TypedArray | DataView;
   *   stdio?: string | Array;
   *   env?: Record<string, string>;
   *   shell?: string;
   *   uid?: number;
   *   gid?: number;
   *   timeout?: number;
   *   killSignal?: string | number;
   *   maxBuffer?: number;
   *   encoding?: string;
   *   windowsHide?: boolean;
   *   }} [options]
   * @returns {Buffer | string}
   */
  function execSync(command, options) {
    const opts = normalizeExecArgs(command, options, null);
    const inheritStderr = !opts.options.stdio;
  
    const ret = spawnSync(opts.file, opts.options);
  
    if (inheritStderr && ret.stderr)
      process.stderr.write(ret.stderr);
  
    const err = checkExecSyncError(ret, undefined, command);
  
    if (err)
      throw err;
  
    return ret.stdout;
  }
  
  
  function validateArgumentNullCheck(arg, propName) {
    if (typeof arg === 'string' && StringPrototypeIncludes(arg, '\u0000')) {
      throw new ERR_INVALID_ARG_VALUE(propName, arg, 'must be a string without null bytes');
    }
  }
  
  
  function validateArgumentsNullCheck(args, propName) {
    for (let i = 0; i < args.length; ++i) {
      validateArgumentNullCheck(args[i], `${propName}[${i}]`);
    }
  }
  
  
  function validateTimeout(timeout) {
    if (timeout != null) {
      validateInteger(timeout, 'timeout', 0);
    }
  }
  
  
  function validateMaxBuffer(maxBuffer) {
    if (maxBuffer != null) {
      validateNumber(maxBuffer, 'options.maxBuffer', 0);
    }
  }
  
  
  function sanitizeKillSignal(killSignal) {
    if (typeof killSignal === 'string' || typeof killSignal === 'number') {
      return convertToValidSignal(killSignal);
    } else if (killSignal != null) {
      throw new ERR_INVALID_ARG_TYPE('options.killSignal',
                                     ['string', 'number'],
                                     killSignal);
    }
  }
  
  module.exports = {
    _forkChild,
    ChildProcess,
    exec,
    execFile,
    execFileSync,
    execSync,
    fork,
    spawn,
    spawnSync,
  };
  