Repositorio del curso CCOM4030 el semestre B91 del proyecto kilometro0

exec.js 7.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. var common = require('./common');
  2. var _tempDir = require('./tempdir');
  3. var _pwd = require('./pwd');
  4. var path = require('path');
  5. var fs = require('fs');
  6. var child = require('child_process');
  7. // Hack to run child_process.exec() synchronously (sync avoids callback hell)
  8. // Uses a custom wait loop that checks for a flag file, created when the child process is done.
  9. // (Can't do a wait loop that checks for internal Node variables/messages as
  10. // Node is single-threaded; callbacks and other internal state changes are done in the
  11. // event loop).
  12. function execSync(cmd, opts) {
  13. var tempDir = _tempDir();
  14. var stdoutFile = path.resolve(tempDir+'/'+common.randomFileName()),
  15. codeFile = path.resolve(tempDir+'/'+common.randomFileName()),
  16. scriptFile = path.resolve(tempDir+'/'+common.randomFileName()),
  17. sleepFile = path.resolve(tempDir+'/'+common.randomFileName());
  18. var options = common.extend({
  19. silent: common.config.silent
  20. }, opts);
  21. var previousStdoutContent = '';
  22. // Echoes stdout changes from running process, if not silent
  23. function updateStdout() {
  24. if (options.silent || !fs.existsSync(stdoutFile))
  25. return;
  26. var stdoutContent = fs.readFileSync(stdoutFile, 'utf8');
  27. // No changes since last time?
  28. if (stdoutContent.length <= previousStdoutContent.length)
  29. return;
  30. process.stdout.write(stdoutContent.substr(previousStdoutContent.length));
  31. previousStdoutContent = stdoutContent;
  32. }
  33. function escape(str) {
  34. return (str+'').replace(/([\\"'])/g, "\\$1").replace(/\0/g, "\\0");
  35. }
  36. if (fs.existsSync(scriptFile)) common.unlinkSync(scriptFile);
  37. if (fs.existsSync(stdoutFile)) common.unlinkSync(stdoutFile);
  38. if (fs.existsSync(codeFile)) common.unlinkSync(codeFile);
  39. var execCommand = '"'+process.execPath+'" '+scriptFile;
  40. var execOptions = {
  41. env: process.env,
  42. cwd: _pwd(),
  43. maxBuffer: 20*1024*1024
  44. };
  45. if (typeof child.execSync === 'function') {
  46. var script = [
  47. "var child = require('child_process')",
  48. " , fs = require('fs');",
  49. "var childProcess = child.exec('"+escape(cmd)+"', {env: process.env, maxBuffer: 20*1024*1024}, function(err) {",
  50. " fs.writeFileSync('"+escape(codeFile)+"', err ? err.code.toString() : '0');",
  51. "});",
  52. "var stdoutStream = fs.createWriteStream('"+escape(stdoutFile)+"');",
  53. "childProcess.stdout.pipe(stdoutStream, {end: false});",
  54. "childProcess.stderr.pipe(stdoutStream, {end: false});",
  55. "childProcess.stdout.pipe(process.stdout);",
  56. "childProcess.stderr.pipe(process.stderr);",
  57. "var stdoutEnded = false, stderrEnded = false;",
  58. "function tryClosing(){ if(stdoutEnded && stderrEnded){ stdoutStream.end(); } }",
  59. "childProcess.stdout.on('end', function(){ stdoutEnded = true; tryClosing(); });",
  60. "childProcess.stderr.on('end', function(){ stderrEnded = true; tryClosing(); });"
  61. ].join('\n');
  62. fs.writeFileSync(scriptFile, script);
  63. if (options.silent) {
  64. execOptions.stdio = 'ignore';
  65. } else {
  66. execOptions.stdio = [0, 1, 2];
  67. }
  68. // Welcome to the future
  69. child.execSync(execCommand, execOptions);
  70. } else {
  71. cmd += ' > '+stdoutFile+' 2>&1'; // works on both win/unix
  72. var script = [
  73. "var child = require('child_process')",
  74. " , fs = require('fs');",
  75. "var childProcess = child.exec('"+escape(cmd)+"', {env: process.env, maxBuffer: 20*1024*1024}, function(err) {",
  76. " fs.writeFileSync('"+escape(codeFile)+"', err ? err.code.toString() : '0');",
  77. "});"
  78. ].join('\n');
  79. fs.writeFileSync(scriptFile, script);
  80. child.exec(execCommand, execOptions);
  81. // The wait loop
  82. // sleepFile is used as a dummy I/O op to mitigate unnecessary CPU usage
  83. // (tried many I/O sync ops, writeFileSync() seems to be only one that is effective in reducing
  84. // CPU usage, though apparently not so much on Windows)
  85. while (!fs.existsSync(codeFile)) { updateStdout(); fs.writeFileSync(sleepFile, 'a'); }
  86. while (!fs.existsSync(stdoutFile)) { updateStdout(); fs.writeFileSync(sleepFile, 'a'); }
  87. }
  88. // At this point codeFile exists, but it's not necessarily flushed yet.
  89. // Keep reading it until it is.
  90. var code = parseInt('', 10);
  91. while (isNaN(code)) {
  92. code = parseInt(fs.readFileSync(codeFile, 'utf8'), 10);
  93. }
  94. var stdout = fs.readFileSync(stdoutFile, 'utf8');
  95. // No biggie if we can't erase the files now -- they're in a temp dir anyway
  96. try { common.unlinkSync(scriptFile); } catch(e) {}
  97. try { common.unlinkSync(stdoutFile); } catch(e) {}
  98. try { common.unlinkSync(codeFile); } catch(e) {}
  99. try { common.unlinkSync(sleepFile); } catch(e) {}
  100. // some shell return codes are defined as errors, per http://tldp.org/LDP/abs/html/exitcodes.html
  101. if (code === 1 || code === 2 || code >= 126) {
  102. common.error('', true); // unix/shell doesn't really give an error message after non-zero exit codes
  103. }
  104. // True if successful, false if not
  105. var obj = {
  106. code: code,
  107. output: stdout
  108. };
  109. return obj;
  110. } // execSync()
  111. // Wrapper around exec() to enable echoing output to console in real time
  112. function execAsync(cmd, opts, callback) {
  113. var output = '';
  114. var options = common.extend({
  115. silent: common.config.silent
  116. }, opts);
  117. var c = child.exec(cmd, {env: process.env, maxBuffer: 20*1024*1024}, function(err) {
  118. if (callback)
  119. callback(err ? err.code : 0, output);
  120. });
  121. c.stdout.on('data', function(data) {
  122. output += data;
  123. if (!options.silent)
  124. process.stdout.write(data);
  125. });
  126. c.stderr.on('data', function(data) {
  127. output += data;
  128. if (!options.silent)
  129. process.stdout.write(data);
  130. });
  131. return c;
  132. }
  133. //@
  134. //@ ### exec(command [, options] [, callback])
  135. //@ Available options (all `false` by default):
  136. //@
  137. //@ + `async`: Asynchronous execution. Defaults to true if a callback is provided.
  138. //@ + `silent`: Do not echo program output to console.
  139. //@
  140. //@ Examples:
  141. //@
  142. //@ ```javascript
  143. //@ var version = exec('node --version', {silent:true}).output;
  144. //@
  145. //@ var child = exec('some_long_running_process', {async:true});
  146. //@ child.stdout.on('data', function(data) {
  147. //@ /* ... do something with data ... */
  148. //@ });
  149. //@
  150. //@ exec('some_long_running_process', function(code, output) {
  151. //@ console.log('Exit code:', code);
  152. //@ console.log('Program output:', output);
  153. //@ });
  154. //@ ```
  155. //@
  156. //@ Executes the given `command` _synchronously_, unless otherwise specified.
  157. //@ When in synchronous mode returns the object `{ code:..., output:... }`, containing the program's
  158. //@ `output` (stdout + stderr) and its exit `code`. Otherwise returns the child process object, and
  159. //@ the `callback` gets the arguments `(code, output)`.
  160. //@
  161. //@ **Note:** For long-lived processes, it's best to run `exec()` asynchronously as
  162. //@ the current synchronous implementation uses a lot of CPU. This should be getting
  163. //@ fixed soon.
  164. function _exec(command, options, callback) {
  165. if (!command)
  166. common.error('must specify command');
  167. // Callback is defined instead of options.
  168. if (typeof options === 'function') {
  169. callback = options;
  170. options = { async: true };
  171. }
  172. // Callback is defined with options.
  173. if (typeof options === 'object' && typeof callback === 'function') {
  174. options.async = true;
  175. }
  176. options = common.extend({
  177. silent: common.config.silent,
  178. async: false
  179. }, options);
  180. if (options.async)
  181. return execAsync(command, options, callback);
  182. else
  183. return execSync(command, options);
  184. }
  185. module.exports = _exec;