LocalCommandRunner.js 2.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. /* eslint-disable
  2. camelcase,
  3. handle-callback-err,
  4. no-return-assign,
  5. no-unused-vars,
  6. */
  7. // TODO: This file was created by bulk-decaffeinate.
  8. // Fix any style issues and re-enable lint.
  9. /*
  10. * decaffeinate suggestions:
  11. * DS101: Remove unnecessary use of Array.from
  12. * DS102: Remove unnecessary code created because of implicit returns
  13. * DS207: Consider shorter variations of null checks
  14. * Full docs: https://github.com/decaffeinate/decaffeinate/blob/master/docs/suggestions.md
  15. */
  16. let CommandRunner
  17. const { spawn } = require('child_process')
  18. const _ = require('underscore')
  19. const logger = require('logger-sharelatex')
  20. logger.info('using standard command runner')
  21. module.exports = CommandRunner = {
  22. run(project_id, command, directory, image, timeout, environment, callback) {
  23. let key, value
  24. if (callback == null) {
  25. callback = function(error) {}
  26. } else {
  27. callback = _.once(callback)
  28. }
  29. command = Array.from(command).map(arg =>
  30. arg.toString().replace('$COMPILE_DIR', directory)
  31. )
  32. logger.log({ project_id, command, directory }, 'running command')
  33. logger.warn('timeouts and sandboxing are not enabled with CommandRunner')
  34. // merge environment settings
  35. const env = {}
  36. for (key in process.env) {
  37. value = process.env[key]
  38. env[key] = value
  39. }
  40. for (key in environment) {
  41. value = environment[key]
  42. env[key] = value
  43. }
  44. // run command as detached process so it has its own process group (which can be killed if needed)
  45. const proc = spawn(command[0], command.slice(1), { cwd: directory, env })
  46. let stdout = ''
  47. proc.stdout.on('data', data => (stdout += data))
  48. proc.on('error', function(err) {
  49. logger.err(
  50. { err, project_id, command, directory },
  51. 'error running command'
  52. )
  53. return callback(err)
  54. })
  55. proc.on('close', function(code, signal) {
  56. let err
  57. logger.info({ code, signal, project_id }, 'command exited')
  58. if (signal === 'SIGTERM') {
  59. // signal from kill method below
  60. err = new Error('terminated')
  61. err.terminated = true
  62. return callback(err)
  63. } else if (code === 1) {
  64. // exit status from chktex
  65. err = new Error('exited')
  66. err.code = code
  67. return callback(err)
  68. } else {
  69. return callback(null, { stdout: stdout })
  70. }
  71. })
  72. return proc.pid
  73. }, // return process id to allow job to be killed if necessary
  74. kill(pid, callback) {
  75. if (callback == null) {
  76. callback = function(error) {}
  77. }
  78. try {
  79. process.kill(-pid) // kill all processes in group
  80. } catch (err) {
  81. return callback(err)
  82. }
  83. return callback()
  84. }
  85. }