| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283 |
- const _ = require('underscore')
- const logger = require('logger-sharelatex')
- const childProcess = require('child_process')
- const Settings = require('settings-sharelatex')
- const { ConversionsDisabledError, FailedCommandError } = require('./Errors')
- // execute a command in the same way as 'exec' but with a timeout that
- // kills all child processes
- //
- // we spawn the command with 'detached:true' to make a new process
- // group, then we can kill everything in that process group.
- module.exports = safeExec
- module.exports.promises = safeExecPromise
- // options are {timeout: number-of-milliseconds, killSignal: signal-name}
- function safeExec(command, options, callback) {
- if (!Settings.enableConversions) {
- return callback(
- new ConversionsDisabledError('image conversions are disabled')
- )
- }
- const [cmd, ...args] = command
- const child = childProcess.spawn(cmd, args, { detached: true })
- let stdout = ''
- let stderr = ''
- let killTimer
- if (options.timeout) {
- killTimer = setTimeout(function() {
- try {
- // use negative process id to kill process group
- process.kill(-child.pid, options.killSignal || 'SIGTERM')
- } catch (error) {
- logger.log(
- { process: child.pid, kill_error: error },
- 'error killing process'
- )
- }
- }, options.timeout)
- }
- const cleanup = _.once(function(err) {
- if (killTimer) {
- clearTimeout(killTimer)
- }
- callback(err, stdout, stderr)
- })
- child.on('close', function(code, signal) {
- if (code || signal) {
- return cleanup(
- new FailedCommandError(command, code || signal, stdout, stderr)
- )
- }
- cleanup()
- })
- child.on('error', err => {
- cleanup(err)
- })
- child.stdout.on('data', chunk => {
- stdout += chunk
- })
- child.stderr.on('data', chunk => {
- stderr += chunk
- })
- }
- function safeExecPromise(command, options) {
- return new Promise((resolve, reject) => {
- safeExec(command, options, (err, stdout, stderr) => {
- if (err) {
- reject(err)
- }
- resolve({ stdout, stderr })
- })
- })
- }
|