/**
* Allow script mappers to run external processes
*
* Usage
* Usage from a script mapper:
*
* var runner = new ProcessRunner(executable_name);
* runner.addArgument(arg0);
* runner.addArgument(arg1);
* ...
* int_ExitValue = runner.executeCommand()
*
* This code will run an external process with 2 arguments and wait for the process to exit before returning. The
* exit value from the command will be returned by the executeCommand call.
*
*
* Handling output
* If the calling ScriptMapper has a 'onOutput' method defined, then each time the external process generates a
* line of output on either stdout or stderr, the onOutput method will be called using the following signature:
*
* onOutput(type, line)
*
* The type is one of:
* stdout - stdout output from the process
* stderr - stderr output from the process
* error - error from running the process (ie, not allowed, exception occurred that we caught)
*
* If 'onOutput' is not defined in the Script Mapper, stdout and stderr are collected while running the process
* and are available when the process completes using:
*
* lines[] = runner.getStdout();
* lines[] = runner.getStderr();
* errorString = runner.getErrorMessage();
*
* There are cases in which there is an onOutput method defined in the script mapper, but a specific call to
* executeCommand should not use onOutput, but rather gather the output for a call to runner.getStdout() after
* the command has completed. This occurs, when the script mapper handles different actions with some using onOutput
* and some using getStdout. To support this, call executeCommandWithOutput:
*
* runner.executeCommandWithOutput() // this will buffer all output for use with getStdout() instead of calling onOutput
*
* Whitelisting processes that may be executed
* In order for a process to be executed, the executable_name must be in the whitelist of
* processes in the work/setup/whitelist.config file:
*
* { processes: [ "name1", "name2", etc. ] }
*
* By default, there are no processes in the whitelist.
*
*
* Running as sudo
* To run the command as 'sudo', before calling 'executeCommand' call:
*
* runner.sudo();
*
* Note that to use this sudo support, 'sudo' should NOT be in the whitelist. However, any
* command that is to be executed must be in the whitelist. As added security, it is
* recommended that you also configure the DataService user in the sudo config (at the unix
* system level) to allow NOPASSWD and specify the list of commands that are allowed to be
* executed as sudo. The sudo config file should contain something like:
*
* dataservice_user ALL = NOPASSWD: /bin/ls, /bin/cat
*
* Running with nohup
* To run the command with nohup, before calling 'executeCommand' call:
*
* runner.nohup();
*
* Providing standard input
* Passing a string to standard input when running the process, use this API before calling
* executeCommand():
*
* runner.setInput(string_to_use_as_stdin);
*
*
* Running command asynchronously
* If you want to spawn the external process and not wait until the process exits, there are a few additional
* requirements to support this. First, use .executeCommandAsync() instead of .executeCommand() to run the
* external process so that it does not block waiting for completion. You must call 'this.startAsync()' before
* returning from the Script Mapper command you are executing. This allows the executed process to continue
* running while sending the output lines to the Script Mapper's onOutput callback. In the onOutput callback,
* the Script Mapper may generate websocket events using 'this.broadcast'. When the executed process completes,
* the Script Mapper's onComplete method is called.
*
* So, the way to run a Script Mapper that executes a process asynchronously:
*
* - in the script mapper method:
* - call processRunner.executeCommandAsync()
* - call this.startAsync()
* - provide an onOutput() method
* - call this.broadcast() when needed based on output from the process
* - provide an onComplete() method if handling the completion of the process is needed
* - in this onComplete() method call this.stopAsync()
*
* The onComplete method has the following signature:
*
* onComplete(exitValue, errorMessage)
*
*/
The following APIs are available to Script Mappers using the ProcessRunner object:
/** * Add an argument to the command line */ addArgument(String arg)
/** * Provide a string as standard input to the process to run. This must be * called before calling executeComand() */ setInput(String input)
/** * Set the exit value that indicates success */ setExitValue(int value)
/** * Set the timeout for a process execution as seconds * * If this is set and the process runs longer than this time, the process will be killed and * a 143 (SIGTERM) error will be returned by the ProcessRunner. */ setTimeout(int seconds);
/** * Use 'sudo' to run command */ sudo()
/** * Use 'nohup' to run command */ nohup()
/** * Execute the command, waiting for it to complete. * * @return exit value from process that was executed or one of these values: * * 2: process not found or not on whitelist * -1: IOException occurred */ int executeCommand()
/** * Execute the command, waiting for it to complete and buffer output (do not call onOutput if it exists). * * @return exit value from process that was executed or one of these values: * * 2: process not found or not on whitelist * -1: IOException occurred */ int executeCommandWithOutput()
/** * Execute the command, do not wait for it to complete, but rather return immediately after starting * * @return exit value from process that was executed or one of these values: * * 2: process not found or not on whitelist * -1: IOException occurred */ int executeCommandAsync()
/** * Cancel a currently async running command * * When this is called, the process will be killed a 143 (SIGTERM) error will be * returned by the ProcessRunner when it calls the onComplete callback. */ cancel();
/* * Return true if the call to execute the command resulted in an error */ boolean hasErrorMessage()
/* * Return the error message that resulted from the exeuction of the command */ String getErrorMessage()
/* * If 'onOutput' is not defined in the Script Mapper or executeCommandWithOutput was called, * standard outout lines are collected while running the process and are available when the process * completes using this method */ Array[String] getStdout()
/* * If 'onOutput' is not defined in the Script Mapper or executeCommandWithOutput was called, * standard error lines are collected while running the process and are available when the process * completes using this method */ Array[String] getStderr()
