executing long running powershell script in nodejs - node.js

In my nodejs project I need to detect when usb flash drive is plugged in/out. I chose to use powershell script and run it using child_process exec or spawn.
psscript.ps1
Write-Host "listening for Win32_VolumeChangeEvent";
Register-WmiEvent -Query "SELECT * FROM Win32_VolumeChangeEvent" -Action {Write-Host "something happened"} | Out-Null;
I'm not an expert in powershell but it should just listen on volumeChangeEvent and write something happened when it fires
Nodejs spawns it as child using following code
nodescript.js
var spawn = require('child_process').spawn;
var child = spawn('powershell.exe', ['psscript.ps1']);
child.stdout.on('data', function(data){
console.log('DATA:', data.toString());
});
child.stdout.on('close', function(data){
console.log('psscript closed');
});
It should catch anything on child's stdout and parse it. For demonstration purpose it just prints out with prepended string DATA:.
This is the log. nodescript spawns the psscript and prints its output with the DATA: as i wanted. The problem is that the psscript closes and nodejs exits as well so i will never receive event.
C:\myproject>node nodescript.js
DATA: listening for Win32_VolumeChangeEvent
DATA:
psscript closed
C:\myproject>
so i was looking for a way to make the powershell not close. I ended up using -noexit argument in spawn var child = spawn('powershell.exe', ['-noexit', script]); which leads to this log.
C:\myproject>node nodescript.js
listening for Win32_VolumeChangeEvent
PS C:\myproject> something happened
something happened
nodescript spawns the psscript which prints the listening for Win32_VolumeChangeEvent just fine but then it prints PS C:\myproject> (notice the PS). I guess the powershell script registered event listener and then closed (hence the PS C:\myproject>) but powershell was still running (hence the something happened) as well as node. It's confusing fo me because I'm just used to the way node deals with event listeners (node never closes when any listenner is attached). Anyway the powershell script keeps on listening and notifying. That's the twice printed something happened because I plugged in and out the flash drive.
That is pretty much what I want but the problem is that the child spawned with -noexit takes over my nodejs console and pollutes it with output of the powershell while it never outputs it to the child.stdout and child.stdout.on('data', ...) never receives any data. As you can see in the second log there's no DATA:.
So I'm unable to work with it
Thank you for your help

To keep Node running - uou can just add a while(true) {} statement after your code snippet.
To have your node instance eventually timeout, you can use setTimeout and call process.exit() when timeout has expired.

Related

How to send "CTRL+C" to child process in Node.js?

I tried to spawn child process - vvp (https://linux.die.net/man/1/vvp). At the certain time, I need to send CTRL+C to that process.
I am expecting that simulation will be interrupted and I get the interactive prompt. And after that I can continue the simulation by send command to the child process.
So, I tried something like this:
var child = require('child_process');
var fs = require('fs');
var vcdGen = child.spawn('vvp', ['qqq'], {});
vcdGen.stdout.on('data', function(data) {
console.log(data.toString())
});
setTimeout(function() {
vcdGen.kill('SIGINT');
}, 400);
In that case, a child process was stopped.
I also tried vcdGen.stdin.write('\x03') instead of vcdGen.kill('SIGINT'); but it isn't work.
Maybe it's because of Windows?
Is there any way to achieve the same behaviour as I got in cmd?
kill only really supports a rude process kill on Windows - the application signal model in Windows and *nix isn't compatible. You can't pass Ctrl+C through standard input, because it never comes through standard input - it's a function of the console subsystem (and thus you can only use it if the process has an attached console). It creates a new thread in the child process to do its work.
There's no supported way to do this programmatically. It's a feature for the user, not the applications. The only way to do this would be to do the same thing the console subsystem does - create a new thread in the target application and let it do the signalling. But the best way would be to simply use coöperative signalling instead - though that of course requires you to change the target application to understand the signal.
If you want to go the entirely unsupported route, have a look at https://stackoverflow.com/a/1179124/3032289.
If you want to find a middle ground, there's a way to send a signal to yourself, of course. Which also means that you can send Ctrl+C to a process if your consoles are attached. Needless to say, this is very tricky - you'd probably want to create a native host process that does nothing but create a console and run the actual program you want to run. Your host process would then listen for an event, and when the event is signalled, call GenerateConsoleCtrlEvent.

Close another process from node on Windows

How can I kill a process from node on Windows?
I'm making a updater. It needs close a windows executable (.exe) to download the updates. (The update process is download and overwrite). I read that this is possible with process.kill(pid[, signal])
But, How can I get the PID of the process if I know the name of the process?
According to the documentation, you simply access the property.
process.kill(process.pid, 'SIGKILL');
This is a theoretical, untested, psuedo function that may help explain what I have in mind
exec('tasklist', (err, out, code) => { //tasklist is windows, but run command to get proccesses
const id = processIdFromTaskList(processName, out); //get process id from name and parse from output of executed command
process.kill(id, "SIGKILL"); //rekt
});
Use node-windows to get pid of process you want to kill so that you can call process.kill. The library also provides an api to kill task.

What is the best way to get a script to execute on the server and display the output of the script on the client using node.js?

I am developing a small web app where I want to be able to give my user the ability to press a button and be able to execute a script remotely on the server. I want the output of the script to be displayed on the client.
I am just trying to understand the best way there is to implement functionality like that. Are there any libraries/nodules that let you do something like that directly?
Possible ways
Child.process()
Take a look at node's child_process library. Link
I've used the .exec function before, as that runs a script to completion and captures the output. That sounds like what your are looking for, just replace their example command with yours.
var exec = require('child_process').exec,
child;
child = exec('cat *.js bad_file | wc -l',
function (error, stdout, stderr) {
console.log('stdout: ' + stdout);
console.log('stderr: ' + stderr);
if (error !== null) {
console.log('exec error: ' + error);
}
});
Wrap this call in a callback, and you'll get a response to the callback, and then to the client, when the script finishes. Be aware of the scripts your are running via a HTTP request. Open strings like "run any command" would be obvious security problems, and long running scripts would be a problem for HTTP timeouts. You could implement a WebSockets based solution and pipe results as they come (take a look at .spawn if you want to kick off a process and keep it running, and maybe interact with it later.)

Node spawn stdout.on data delay

I am checking for USB drive removal on linux. I am simply monitoring the output of a command line process with child_process.spawn. But for some reason the child's stdout data event doesn't emit until like 20 lines have been printed, which makes it unable to detect a removed drive. After removing the drive many times, it does finally go. But obviously that won't do.
Original:
var udevmonitor = require("child_process").spawn("udevadm", ["monitor", "--udev"]);
udevmonitor.stdout.on("data", function(data) {
return console.log(data.toString());
});
Pretty simple. So I figure it's an issue with the piping node is using internally. So instead of using the pipe, I figure I'll just use a simple passthrough stream. That could solve the problem and give me real-time output. That code is:
var stdout = new require('stream').PassThrough();
require("child_process").spawn("udevadm", ["monitor", "--udev"], { stdio: ['pipe', stdout, 'pipe'] });
stdout.on("data", function(data) {
console.log(data.toString());
});
But that gives me an error:
child_process.js:922 throw new TypeError('Incorrect value for stdio stream: ' + stdio);
The documentation says you can pass a stream in. I don't see what I'm doing wrong and stepping through the child_process source didn't help.
Can someone help? You can run this yourself, provided you're on Linux. Run the code and insert a USB drive. Perhaps you can run the command 'udevadm monitor --udev' in another terminal to see what happens. Remove and reinsert a few times and eventually node will print out.
mscdex, I love you. Changing the spawn command to
spawn("stdbuf", ["-oL", "-eL", "udevadm", "monitor", "--udev"]);
Did the trick. I really appreciate your help!

Using forever with Node.js

I have a few, possibly trivial, questions with using forever with Node.js. From what I have read, forever can be used programatically and it maintains a list with all the scripts that use forever. When that process dies, it automatically spawns a new one until it is stopped.
However, my question is, how does forever do this? Does it add these scripts to be started on boot as well?
You can use forever programatically like this:
Using an instance of Forever inside a node.js script:
var forever = require('forever-monitor');
var child = new (forever.Monitor)('your-filename.js', {
max: 3,
silent: true,
options: []
});
child.on('exit', function () {
console.log('your-filename.js has exited after 3 restarts');
});
child.start();
You should take a minute and read over the options available in the excellent documentation for Forever in the README.md
You have a number of events that can be listened for in Forever as well:
error [err]: Raised when an error occurs
start [process, fvrFile, data]: Raise when the target script is first started.
stop [process]: Raised when the target script is stopped by the user
save [path, data]: Raised when the target Monitor saves the pid information to disk.
restart [forever]: Raised each time the target script is restarted
exit [forever]: Raised when the target script actually exits (permenantly).
stdout [data]: Raised when data is received from the child process' stdout
stderr [data]: Raised when data is received from the child process' stderr
It does this by attaching event listeners to the script you're trying to run and handling them in a graceful manner.
The code is pretty well documented if you want to take a look at exactly how it does it.
You should also read this excellent tutorial on how to keep a process running forever.
As for the second question: No, it does not add it to start at boot. For that, you'd need to add it as an upstart job or use something like Monit to monitor and start it. For that, you should take a look at Deploying Node.js with Upstart and Monit. It's a great tutorial.
This is an old post, but I stumbled across this on Google - its a little out of date, as forever branched the command line version from the programatic version. You need to use forever-monitor instead of forever. The example code should now be;
var forever = require('forever-monitor');
var child = new (forever.Monitor)('your-filename.js', {
max: 3,
silent: true,
options: []
});
child.on('exit', function () {
console.log('your-filename.js has exited after 3 restarts');
});
child.start();
I tried to suggest an edit to the original answer, but the powers that be rejected it. Figured I could spare some others the time it took me to figure out why the example code doesn't work :-)

Resources