LocalCommandRunner.js 2.8 KB

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