Execute multiple commands in Third party CLI mode using Node JS - node.js

I want to execute 5 commands in a sequence and log its output.For Example. First command XXXcli ip_address (This will connect me to the third party CLI mode) and the next commands will execute a script,the next will log output etc.But my problem is when I do SSH through node.js and spawn a shell inside ssh session, when I execute the first command I couldn't see any output on my Console. The Session creates a shell and once the shell enters the third party CLI ,Its becoming impossible for me to fire the next command or log the output of the first command.Kindly help me on this. I'm stuck with this for a long time
Update:
My Code:
session.on('exec', function (accept, reject, info) {
console.log('Client wants to execute: ' + inspect(info.command));
var stream = accept();
var cp = spawn('XXXCLI 10.21.254.12', {shell: true});
stream.stdin.pipe(cp.stdin);
cp.stdout.pipe(stream.stdout);
sleep(6000);
cp.stderr.pipe(stream.stderr);
cp.on('exit', function (code, signal) {
stream.exit(signal || code);
}).on('end', function (code, signal) {
stream.close();
});
});
When I manually type the first command 'XXXCLI ip_address' in my command prompt and press enter,I will get a output "Connected to CLI...." .Once I get this connection successful, I need to execute my second command i.e "Lmc sample" which will load the master config and I will get the output as "Message sent..", third command will execute a script,will get output as "Message sent.." .This is what happens when I enter these commands manually in cmd prompt and execute.
What is happening is once I execute my first command i.e "XXXCLI 10.21.254.12" manually in cmd, The path where we actually execute the commands i.e( C:\users\CLI>) will not be visible. This happens because now it got connected with the above mentioned ip (10.21.254.12) .And Only after connecting to this ip ,I can able to execute my other commands.i.e command to load master config ,cmd to execute script etc.
So I want to execute my first command and get its stream in a variable and execute rest of the commands inside the stream created by first command
Thanks!

I fixed this using Child Process in Node.js and writing the commands in the stream directly. When I did the same with Java it didn't work, but it did in Node.js.

Related

NodeJS child spawn exits without even waiting for process to finish

I'm trying to create an Angular11 application that connects to the NodeJS API that would run bash scripts when called and on exit it should either send an error or send a 200 status with a confirmation message.
here is one of the functions from that API. It runs a script called initialize_event.sh, gives it a few arguments when prompted and once the program finishes its course should display a success message (There is no error block for this function):
exports.create_event = function (req, res) {
var child = require("child_process").spawn;
var spawned = child("sh", ["/home/ubuntu/master/initialize_event.sh"]);
spawned.stdout.once("data", function (data) {
spawned.stdin.write(req.body.name + "\n");
});
spawned.stdout.once("data", function (data) {
spawned.stdin.write(req.body.domain_name + "\n");
});
spawned.on("exit", function (err) {
res.status(200).send(JSON.stringify("Event created successfully"));
});
};
The bash script is a long one, but what it basically does is take two variables (event name and domain name) and uses that to create a new event instance. Here are the first few lines of code for the program:
#!/bin/bash
#GET EVENT NAME
echo -n "Enter event name: "; read event;
echo -n "Enter event domain: "; read eventdomain;
#LOAD VARIABLES
export eventdomain;
export event;
export ename=$event-env;
export event_rds= someurl.com ;
export master_rds= otherurl.com;
export master_db=master;
# rest of code...
When called on its own directly from the terminal, the process takes around 30-40 seconds after taking input to create an event and then exits once completed. I can then check the list of events created using another script and the new event would show up in the list. However, when I call this script from the NodeJS function, it manages to take the inputs and the exit within 5 or 6 seconds, saying the event has been created successfully. When I check the list of events there is no event created. I wait to see if the process is still running and check back after a few minutes, still, no event created.
I suspect that the spawn exits before the script can be run completely. I thought that maybe the stdio streams are still open so I tried to use spawned.on.close instead of spawned.on.exit, but still the program exits before it even runs completely. I don't see any exceptions or errors appearing in the Node express console, so I can't really figure out why the program exits successfully without running all the way through.
I've used the same inputs when running from the terminal and on Postman, and have logged them as well to see if there are any empty variables being sent, but found nothing wrong with them either. I've double-checked the paths as well, literally copy-pasted from pwd to make sure I haven't been missing something, but still nothing.
What am I doing wrong here??
So here's the problem I found and solved:
The folder where the Node Express was being served from, and the folder where the bash scripts were saved were in different directories.
Problem:
So basically, whenever I created a child process, it was created with the following current directory:
var/www/html/node/
But the bash scripts were run from:
var/www/html/other/bash/scripts/
so any commands that were added to the bash script that involved directory change (like cd) were relative to the bash directory.
However, since the spawn's current directory was var/www/html/node the script being executed in the spawn also had the same current working directory as the node folder, and any directory changes within the script were now invalid since they didn't exist relative to node directory.
E.g.
When run from terminal:
test.sh -> cd /savedir/ -> /var/www/html/other/bash/scripts/savedir/ -> exists
When run from spawn:
test.sh -> cd /savedir/ -> /var/www/html/node/savedir/ -> Doesn't exist!
Solution:
The easiest way I was able to solve this was to modify the test.sh file. i.e during the start I added cd /var/www/html/other/bash/scripts/. This allowed the current directory of my spawn to change to the right directory that would make all the mv cd and other path relevant commands valid.

Create and communicate with a non-terminating shell via child_process in nodejs

Is there a way to spawn a single child_process in NodeJS and pass it various commands over time keeping the same process open as long as necessary? Sort of like a spawned terminal which accepts commands from Node.
Why? Performance.
I have a NodeJS/Electron application which should execute powershell commands and this is achieved using Node's child_process module. However the performance is not great: there appears to be a couple of seconds overhead each time I spawn a child process (which is to be expected I suppose).
This means that commands such as Get-Date take 600ms instead of a few (2) milliseconds. Other commands take 2+ seconds instead of say 800ms.
Desired workflow:
Start a child powershell process (exec with shell = powershell)
Pass it a command
Get the results (stdout/stderr)
Wait seconds to minutes for the user...
Pass it a second command
Get the results (stdout/stderr)
etc...
Close child process
I have considered writing powershell commands from NodeJS to a file commands.txt. Next I would start a single powershell child_process which watches/tails a file for new commands and executes them, passing the output into another file which the parent (NodeJS) process watches. This seems a bit hacky however...
I have found one solution using spawn and periodically piping input to the process with stdin.write:
const { spawn } = require("child_process");
const ps1 = spawn("C:\\Windows\\SysWOW64\\WindowsPowerShell\\v1.0\\powershell.exe", [], {});
console.log("PID", ps1.pid, "started");
ps1.stdout.on('data', (data)=>{
console.log("STDOUT:"+data);
});
ps1.stderr.on('data', (data)=>{
console.log("STDERR:"+data);
});
ps1.on('close', (code, signal) => {
console.log(`child process terminated due to receipt of signal ${signal}`);
});
setInterval(()=>{
ps1.stdin.write("Get-Date\n");
}, 1000);
Results:
PID 7688 started
STDOUT:Windows PowerShell
Copyright (C) Microsoft Corporation. All rights reserved.
STDOUT:PS W:\powershell\powowshell\bak>
STDOUT:Get-Date
STDOUT:
STDOUT:Freitag, 17. Mai 2019 17:55:52
STDOUT:
STDOUT:PS W:\powershell\powowshell\bak>
STDOUT:Get-Date
STDOUT:
STDOUT:Freitag, 17. Mai 2019 17:55:53
So now it's "just" a case of stripping whitespace and other fuzz and getting the results.

How to get mongo shell output(three dot) for unterminated command

When type a unterminated command in a mongo shell, it will return three dots indicating need more input to complete this command like below:
> db.test.find(
... {
...
I am using nodejs child_process.spawn to create a mongo shell process and listen on its output. I can get the standard and error output from the mongo shell but I can't get the ... output. Below is my nodejs code:
const shell = spawn('mongo', params);
shell
.stdout
.on('data', (data) => {
winston.debug('get output ' + data);
});
shell
.stderr
.on('data', (data) => {
const output = data + '';
winston.error('get error output ', data);
});
I run below code to send command on the shell:
shell.stdin.write('db.test.find(');
I wander why I can't get the ... output on above method. Is it a special output?
EDIT1
I tried to use node-pty and pty.js. They can get the ... output but they mix the input and output data together. It is not possible to separate them.
I also tried to use stdbuf and unbuffer to disable buffer but it still doesn't work.
It seems that nodejs child_process doesn't work well with interactive command.
Your code doesn't include anything that writes to the stdin of your child process so I would be surprised if you got the ellipsis that indicates incomplete command when in fact you don't send any command at all - incomplete or otherwise.
That having been said, many command line utilities behave differently when they discover a real terminal connected to their stdin/stdout. E.g. git log will page the results when you run it directly but not when you pipe the results to some other command like git log | cat so this may also be the case here.
This can also have to do with the buffering - if your stream is line-buffered then you won't see any line that is not ended with a newline right away.
The real question is: do you see the > prompt? Do you send any command to the mongo shell?
Scritping interactive CLI tools can be tricky. E.g. see what I had to do to test a very simple interactive program here:
https://github.com/rsp/rsp-pjc-c01/blob/master/test-z05.sh#L8-L16
I had to create two named pipes, make sure that stdin, stderr and stdout are not buffered, and then use some other tricks to make it work. It is a shell script but it's just to show you an example.

Why does node finish/close when streaming input via process.stdin?

I am trying to create a node app that you can pipe in JSON data and display it as a question to the user.
I learned how to bind to process.stdin here http://dailyjs.com/2012/03/08/unix-node-pipes/
Here is an example. The following is my UI for asking questions in the terminal.
var inquirer = require("inquirer");
function runInquirer(choices) {
inquirer.prompt([
{
type: "checkbox",
message: "Select toppings",
name: "toppings",
choices: choices
}
], function( answers ) {
console.log( JSON.stringify(answers, null, " ") );
});
}
If I invoke the handler directly, it works
runInquirer([
{name:'Cheese'},
{name:'Tomato'}
]);
But when I try to pipe the data into it
process.stdin.resume();
process.stdin.setEncoding('utf8');
process.stdin.on('data', function(data) {
runInquirer(JSON.parse(data));
});
It displays the questions and then exits immediately. So it is not waiting for my user input.
Example invoking from the command line (note the pipe | )
➜ test git:(master) ✗ cat questions.json | node test.js
? Select toppings (Press <space> to select)
The usual:
❯◯ Peperonni
◉ Cheese
I have also tried this test with another node user interface library called https://github.com/yaronn/blessed-contrib and got the same result.
You appear to be misunderstanding a bit about how processes handle stdin. inquirer.prompt sets up a prompt by using stdin and stdout to accept user input and write output to the terminal. process.stdin.on('data', sets up a handler to read data as it arrives on stdin. When you run
cat questions.json | node test.js
You are instructing your terminal to stop using the terminal as stdin and to instead use the output of the command cat as stdin.
That means that both of your methods of input are in fact trying to use the same channel. With your current structure, the answer to the question would also have to be coming from your questions.json file. It is exiting immediately because the file has run out of content and cannot possibly have any answers for your inquirer.
What you probably want is for your test.js file to accept a JSON file as the first argument, and read that file instead of reading from process.stdin.

Making lftp write to stdout without having to close the process first

I'm trying to wrap the lftp program in a node.js application, using child_process. The problem is that lftp doesn't write its output to stdout, so I cannot catch its output in node.js. Sample code:
var proc = require('child_process').spawn('lftp', ['-p', port, '-u', username + ',' + password, host]);
proc.stdout.on('data', function (data) {
console.log('stdout:', data.toString('utf-8'));
});
proc.on('exit', function (code) {
console.log('process exited with code ' + code);
});
proc.stdin.write('ls');
// proc.stdin.end();
If I uncomment the line that calls stdin.end() for the lftp child process, the output from the ls command appears in my terminal as it should. If I don't the process simply hangs and nothing gets outputted.
I've also tried using unbuffer, but it doesn't seem to allow me to write to lftp's stdin anymore. It outputs the usual "[Resolving host address...]" stuff, but not the output from the ls command.
My question is: what do I have to do to be able to interact with lftp using node.js' child_process?
Well, this was dumb. I forgot to write a newline after the ls command to stdin. It seems to work without the need for unbuffer.

Resources