SafeExec.coffee 1.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243
  1. _ = require("underscore")
  2. logger = require("logger-sharelatex")
  3. child_process = require('child_process')
  4. # execute a command in the same way as 'exec' but with a timeout that
  5. # kills all child processes
  6. #
  7. # we spawn the command with 'detached:true' to make a new process
  8. # group, then we can kill everything in that process group.
  9. module.exports = (command, options, callback = (err, stdout, stderr) ->) ->
  10. # options are {timeout: number-of-milliseconds, killSignal: signal-name}
  11. [cmd, args...] = command.split(' ')
  12. child = child_process.spawn cmd, args, {detached:true}
  13. stdout = ""
  14. stderr = ""
  15. cleanup = _.once (err) ->
  16. clearTimeout killTimer if killTimer?
  17. callback err, stdout, stderr
  18. if options.timeout?
  19. killTimer = setTimeout () ->
  20. try
  21. # use negative process id to kill process group
  22. process.kill -child.pid, options.killSignal || "SIGTERM"
  23. catch error
  24. logger.log process: child.pid, kill_error: error, "error killing process"
  25. , options.timeout
  26. child.on 'close', (code, signal) ->
  27. err = if code then new Error("exit status #{code}") else signal
  28. cleanup err
  29. child.on 'error', (err) ->
  30. cleanup err
  31. child.stdout.on 'data', (chunk) ->
  32. stdout += chunk
  33. child.stderr.on 'data', (chunk) ->
  34. stderr += chunk