spawn.js (3602B)
1 /** 2 * @license Apache-2.0 3 * 4 * Copyright (c) 2018 The Stdlib Authors. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 'use strict'; 20 21 // MODULES // 22 23 var run = require( 'child_process' ).spawn; 24 var logger = require( 'debug' ); 25 var cwd = require( '@stdlib/process/cwd' ); 26 var proc = require( './process.js' ); 27 28 29 // VARIABLES // 30 31 var debug = logger( 'parallel:worker:spawn' ); 32 33 34 // MAIN // 35 36 /** 37 * Runs a script by spawning a child process. 38 * 39 * @private 40 * @param {string} cmd - command to run 41 * @param {string} script - script filepath 42 * @param {Callback} clbk - callback to invoke after a child process closes 43 * @returns {Process} child process 44 */ 45 function spawn( cmd, script, clbk ) { 46 var child; 47 var args; 48 var opts; 49 var err; 50 51 debug( 'Creating child process...' ); 52 args = [ script ]; 53 opts = { 54 'cwd': cwd(), 55 'env': proc.env, 56 'shell': false, // don't run the command inside a shell 57 'stdio': 'inherit' // use stdio of worker process 58 }; 59 if ( proc.env.WORKER_UID ) { 60 opts.uid = parseInt( proc.env.WORKER_UID, 10 ); 61 } 62 if ( proc.env.WORKER_GID ) { 63 opts.gid = parseInt( proc.env.WORKER_GID, 10 ); 64 } 65 child = run( cmd, args, opts ); 66 67 child.on( 'error', onError ); 68 child.on( 'close', onClose ); 69 child.on( 'exit', onExit ); 70 child.on( 'disconnect', onDisconnect ); 71 72 debug( 'Child process created. pid: %d.', child.pid ); 73 return child; 74 75 /** 76 * Callback invoked upon child process close. 77 * 78 * @private 79 * @param {(number|null)} code - exit code 80 * @param {(string|null)} signal - termination signal 81 * @returns {void} 82 */ 83 function onClose( code, signal ) { 84 debug( 'Child process closed. Code: %d. Signal: %s. pid: %d.', code, signal, child.pid ); 85 processExit( code, signal ); 86 if ( err ) { 87 return clbk( err ); 88 } 89 clbk( null, child.pid, script ); 90 } 91 92 /** 93 * Callback invoked upon child process exit. 94 * 95 * @private 96 * @param {(number|null)} code - exit code 97 * @param {(string|null)} signal - termination signal 98 */ 99 function onExit( code, signal ) { 100 debug( 'Child process exited. Code: %d. Signal: %s. pid: %d.', code, signal, child.pid ); 101 processExit( code, signal ); 102 } 103 104 /** 105 * Callback invoked upon child process disconnect. 106 * 107 * @private 108 */ 109 function onDisconnect() { 110 debug( 'Child process disconnected. pid: %d.', child.pid ); 111 } 112 113 /** 114 * Callback invoked upon child process error. 115 * 116 * @private 117 * @param {Error} error - error object 118 */ 119 function onError( error ) { 120 debug( 'Child process error: %s. pid: %d.', error.message, child.pid ); 121 clbk( error ); 122 } 123 124 /** 125 * Processes process exit values and sets the `err` state. 126 * 127 * @private 128 * @param {(number|null)} code - exit code 129 * @param {(string|null)} signal - termination signal 130 */ 131 function processExit( code, signal ) { 132 if ( err ) { 133 return; 134 } 135 if ( code !== null && code !== 0 ) { 136 err = new Error( cmd+' '+script+' failed with exit code: '+code+'.' ); 137 } else if ( signal !== null ) { 138 err = new Error( cmd+' '+script+' failed due to termination signal: '+signal+'.' ); 139 } 140 if ( err ) { 141 err.code = code; 142 err.signal = signal; 143 } 144 } 145 } 146 147 148 // EXPORTS // 149 150 module.exports = spawn;