Node's spawn/exec not working when called from a scheduled Windows task - node.js

I'm facing a very odd issue where I have a Node script which invokes a process, it looks like this:
// wslPath declared here (it's a shell file)
const proc = cp.spawn('ubuntu.exe', ['run', wslPath]);
let stdout = '';
proc.stdout.on('data', data => stdout += data.toString());
let stderr = '';
proc.stderr.on('data', data => stderr += data.toString());
return await new Promise((resolve, reject) => {
proc.on('exit', async code => {
await fs.remove(winPath);
if (code) {
reject({ code, stdout, stderr });
}
resolve({ stdout, stderr });
});
});
As you can see, the script invokes WSL. WSL is enabled on the computer. When I run this script manually, it works fine. When I log in to the computer the script is at using RDP from another computer and run it with the same credentials, it works fine as well. But when the script is invoked from a scheduled task which also runs with the same credentials, the spawn call returns:
(node:4684) UnhandledPromiseRejectionWarning: Error: spawn UNKNOWN
at ChildProcess.spawn (internal/child_process.js:394:11)
at Object.spawn (child_process.js:540:9)
I verified the user is the same by logging require('os').userInfo() and require('child_process').spawnSync('whoami', { encoding: 'utf8' }) and it returns the same in all three cases.
I assume it is because ubuntu.exe is not being found, but I don't know why that would be as the user is the same in all three cases.
What could be the reason for this and how can I debug this further?

The Windows Task Scheduler allows you to specify a user to run as (for privilege reasons), but does not give you the environment (PATH and other environment variables) that are configured for that user.
So, when running programs from the Windows Task Scheduler, it's important to not make any assumptions about what's in the environment (particularly the PATH). If my program depends on certain things in the environment, I will sometimes change my Task to be a .BAT file that first sets up the environment as needed and then launch my program from there.
Among other things, the simplest way to not rely on the path is to specify the full path to the executable you are running rather than assuming it will be found in the path somewhere. But, you also need to make sure that your executable can find any other resources it might need without any environment variables or you need to configure those environment variables for it before running.

Related

Unable to read a continuous data stream from a node child process in Node.js

First things first. The goal I want to achieve:
I have two processes:
the first one is webpack that just watches for file changes and pushes the bundled files into the dist/ directory
the second process (Shopify CLI) watches for any file changes in the dist/ directory and pushes them to a remote destination
My goal is to have only one command (like npm run start) which simultaneously runs both processes without printing anything to the terminal so I can print custom messages. And that's where the problem starts:
How can I continuously read child process terminal output?
Printing custom messages for webpack events at the right time is pretty easy, since webpack has a Node API for that. But the Shopify CLI only gives me the ability to capture their output and process it.
Normally, the Shopify CLI prints something like "Finished Upload" as soon as the changed file has been pushed. It works perfectly fine for the first time but after that, nothing is printed to the terminal anymore.
Here is a minimal representation of what my current setup looks like:
const spawn = require('spawn');
const childProcess = spawn('shopify', ['theme', 'serve'], {
stdio: 'pipe',
});
childProcess.stdout.on('data', (data) => {
console.log(data);
});
childProcess.stderr.on('data', (data) => {
// Just to make sure there are no errors
console.log(data);
});

Run Linux command from Angular 4 component

Requirement is to fetch the output of a shell script's after running it from the Angular 4 component at the beginning during compilation i.e. just before the website is launched. I have already gone through the threads in stackoverflow i.e. 49700941 and 41637166.
From the first thread i tried to use the below code, but getting error:
Module not found: Error: Can't resolve 'child_process' in 'app/component ...'
const exec = require('child_process').exec; // Can't resolve 'child_process' error coming from this line
exec('/home/myDir/init_setup.sh', (err, stdout, stderr) => {
if (err){
console.error(err);
return;
};
console.log(stdout);
console.log(stderr);
/**
remaining logics
*/
});
Please let me know if I need to import some library explicitly or not to avoid this error.
The modern browsers opens the webpage in isolated sandbox so they have have no access to clients' computers.
Imagine the damage that could be done if a black hat could run batch script on computer that opens his webpage.
The only way to run the script is to run the desktop application on client's machine.
The example code you provided is Node.js code, the desktop framework that user have to install on his machine and run the code intentionally. There's (fortunately!) no way to run it remotely via webpage.

Can't spawn `gcloud app deploy` from a Node.js script on Windows

I'm building an Electron application (Node.js) which needs to spawn gcloud app deploy from the application with realtime feedback (stdin/stdout/stderr).
I rapidly switched from child_process to execa because I had some issues on Mac OS X with the child_process buffer which is limited to 200kb (and gcloud app deploy sends some big chunk of string > 200kb which crash the command).
Now, with execa everything seems to work normally on OSX but not on Windows.
The code looks something like this:
let bin = `gcloud${/^win/.test(process.platform) ? '.cmd' : ''}`
//which: https://github.com/npm/node-which
which(bin, (err, fullpath) => {
let proc = execa(fullpath, ['app', 'deploy'], {
cwd: appPath
})
proc.stdout.on('data', data => {
parseDeploy(data.toString())
})
proc.stderr.on('data', data => {
parseDeploy(data.toString())
})
proc.then(() => {
...
}).catch(e => {
...
})
})
This code works perfectly on Mac OS X while I haven't the same result on Windows
I have tried lots of thing:
execa()
execa.shell()
options shell:true
I tried maxBuffer to 1GB (just in case)
It works with detached:true BUT I can't read stdout / stderr in realtime in the application as it prompts a new cmd.exe without interaction with the Node.js application
Lots of child_process variant.
I have made a GIST to show the responses I get for some tests I have done on Windows with basic Child Process scripts:
https://gist.github.com/thyb/9b53b65c25cd964bbe962d8a9754e31f
I also opened an issue on execa repository: https://github.com/sindresorhus/execa/issues/97
Does someone already got this issue ? I've searched around and found nothing promising except this reddit thread which doesn't solve this issue.
Behind the scene, gcloud.cmd is running a python script. After reading tons of Node.js issue with ChildProcess / Python and Windows, I fell on this thread: https://github.com/nodejs/node-v0.x-archive/issues/8298
There is some known issue about running Python scripts from a Node.js Child Process.
They talk in this comment about an unbuffered option for python. After updating the shell script in gcloud.cmd by adding the -u option, I noticed everything was working as expected
This comment explains how to set this option as an environment variable (to not modify the windows shell script directly): https://docs.python.org/2/using/cmdline.html#envvar-PYTHONUNBUFFERED
So adding PYTHONUNBUFFERED to the environment variable fix this issue !
execa(fullpath, ['app', 'deploy'], {
cwd: appPath,
env: Object.assign({}, process.env, {
PYTHONUNBUFFERED: true
})
})

Unable to spawn a custom .exe from express.js on Windows

I am using node.js/express.js on windows and I have a command I execute when a user takes a image and uploads up from there phone. Once it is uploaded I run myApp.exe to perform some openCV image processing and I output the updated images to a output directory that is a argument in the command below.
I am able to kick this off from my webapp using child_process.exec, but the performance is 60x slower if I run it at command line by itself. To increase the performance I was hoping to use Spawn, but I don't know if this is an accurate assumption, please let me know if it is not.
var exec = require('child_process').exec;
var child = exec('C:\\opt\\package_v030_package\\myApp.exe
--user="C:\\opt\\package_v030_package\\Phone\\'+file.filename+'"
--mv="C:\\opt\\package_v030_package\\mv\\'+req.body.detectionString+'.bmp"
--outPath="C:\\opt\\package_v030_package\\output"
--outputScaled
--outputScaledOverlaid');
child.stdout.on('data', function(data) {
console.log('stdout: ' + data);
});
child.stderr.on('data', function(data) {
console.log('stdout: ' + data);
});
child.on('close', function(code) {
console.log('closing code: ' + code);
//res.json("success")
});
I have tried to kick it off using spawn, but it fails to execute with the following: "error child process exited with code 4294967295". The code is below:
var spawn = require('child_process').spawn;
var cmd = spawn('cmd', ['/s',
'/c',
'C:\\opt\\package_v030_package\\myApp.exe',
'--user="C:\\opt\\package_v030_package\\Phone\\'+file.filename+'"',
'--mv="C:\\opt\\package_v030_package\\mv\\'+req.body.detectionString+'.bmp"',
'--outPath="C:\\opt\\package_v030_package\\output"',
'--outputScaled',
'--outputScaledOverlaid'
]);
cmd.stdout.on('data', (data) => {
console.log(`stdout: ${data}`);
});
cmd.stderr.on('data', (data) => {
console.log(`stderr: ${data}`);
});
cmd.on('close', (code) => {
console.log(`child process exited with code ${code}`);
});
It seems I am able to execute just myApp.exe from spawn because when I add any of my arguments it fails. Even when I hard code the variables that I inject. Is there an issue with my arguments or am I spawning myApp.exe incorrectly?
Update 1
I placed the command in a .bat and was able to execute it from node.js using spawn. It does not increase performance which leads me to believe that the decrease in performance is a limitation of node.js on the windows platform.
In addition, I performed a few tests using postman to see if I could optimize the call without anything else happening, but I did not succeed. I will leave this question open in the event this changes and node.js is able to better handle performance of a CPU intensive child process.
Update 2 & Answer
I was able to fix this by placing the command that we run at the command line into a java class taking in the detectionString as a parameter. Then from node I use spawn to kick off the .jar file. This caused the speed to increase significantly and run as if I was running it myself at command line.
I was able to fix this by placing the command that we run at the command line into a java class taking in the detectionString as a parameter. Then from node I use spawn to kick off the .jar file. This caused the speed to increase significantly and run as if I was running it myself at command line.

Equivalent of Rake's 'sh' for Jake?

I've got some experience with Ruby and Rake, but now I'm working on a Node project and want to learn how to do the same things with Jake.
Ruby has a system function that will shell out to a command and wait for it to exit. Rake extends this by adding an sh function that will additionally throw an error if the child process returned a nonzero exit code (or couldn't be found at all). sh is really handy for Rake tasks that shell out to things like compilers or test frameworks, because it automatically terminates the task as soon as anything fails.
Node doesn't seem to have anything like system or sh -- it looks like the nearest equivalents are child_process.spawn and child_process.exec, but neither of them wires up STDOUT or STDERR, so you can't see any output from the child process unless you do some extra work.
What's the best way to get an sh method for Jake? (Though since this is Node, I'd expect it to be async, rather than blocking until the command returns like Ruby does.) Is there an npm module that has already invented this particular wheel, or does someone have a code sample that does this?
I've already seen sh.js, but it looks awfully heavyweight for this (it tries to build an entire command interpreter in Node), and it doesn't look like it's async (though the docs don't say one way or the other).
I'm looking for something that I could use more or less like this (using Jake's support for async tasks):
file('myprogram', ['in.c'], function() {
// sh(command, args, successCallback)
sh('gcc', ['in.c', '-o', 'myprogram'], function() {
// sh should throw if gcc couldn't be found or returned nonzero.
// So if we got here, we can tell Jake our task completed successfully.
complete();
});
}, true);
Here's some code I've come up with that seems to work well. (But if anyone has a better answer, or knows of an existing npm module that already does this, please add another answer.)
Supports full shell syntax, so you can use | and < and > to pipe and redirect output, you can run Windows batch files, etc.
Displays output (both STDOUT and STDERR) as the child process generates it, so you see incremental output as the command runs.
No limitation on the amount of output the command can generate (unlike a previous exec-based version of this code).
Cross-platform (works on Windows, should work on Mac/Linux as well). I borrowed the platform-specific-shell (if platform === 'win32') technique from npm.
Here's the code:
function sh(command, callback) {
var shell = '/bin/sh', args = ['-c', commandLine], child;
if (process.platform === 'win32') {
shell = 'cmd';
args = ['/c', commandLine];
}
child = child_process.spawn(shell, args);
child.stdout.pipe(process.stdout);
child.stderr.pipe(process.stderr);
child.on('exit', function(code, signal) {
if (signal != null)
throw new Error("Process terminated with signal " + signal);
if (code !== 0)
throw new Error("Process exited with error code " + code);
callback();
});
};

Resources