How can I call functions one-by-one after completing? I have code like that, which must be executed continuously.
For example, I have:
var Connection = require('ssh2');
var c = new Connection();
c.on('connect', function() {
console.log('Connection :: connect');
});
c.on('ready', function() {
console.log('Connection :: ready');
c.exec('uptime', function(err, stream) {
if (err) throw err;
stream.on('data', function(data, extended) {
console.log((extended === 'stderr' ? 'STDERR: ' : 'STDOUT: ')
+ data);
});
stream.on('end', function() {
console.log('Stream :: EOF');
});
stream.on('close', function() {
console.log('Stream :: close');
});
stream.on('exit', function(code, signal) {
console.log('Stream :: exit :: code: ' + code + ', signal: ' + signal);
c.end();
});
});
});
c.on('error', function(err) {
console.log('Connection :: error :: ' + err);
});
c.on('end', function() {
console.log('Connection :: end');
});
c.on('close', function(had_error) {
console.log('Connection :: close');
});
c.connect({
host: '192.168.100.100',
port: 22,
username: 'frylock',
privateKey: require('fs').readFileSync('/here/is/my/key')
});
In what way can I call several functions one-by-one? When the connection to the ssh2 server is established, and the first request is executed, I have to parse the output of this request and send another request with data from the previous request.
I have functions which can do all this; the only problem is how to make another request after the first one is completed.
You can use nested callbacks. In the 'end' handler of each request, create a new connection and setup its end handler. Nest until needed. Similar to:
c.on('end', function() {
// after c is done, create newC
var newC = new Connection();
newC.on('end', function() {
// more if needed.
});
});
Another approach is to use some library for promises, like Q, which clears up the syntax a lot.
You can also take a look at this question: Understanding promises in node.js
From the sounds of it, you might want to look into a control flow node module such as async. Also you shouldn't need to tear down the connection just to execute another command on the same server.
Related
I'm using ssh2 nodejs client (https://github.com/mscdex/ssh2)
I'm trying to do the following:
SSH into box.
Login to docker.
It will re-prompt for password. Enter that password.
I'm failing on 3rd step.
This is my code
var Client = require('ssh2').Client;
var conn = new Client();
conn.on('ready', function() {
console.log('Client :: ready');
conn.exec('sudo docker ps', {pty: true}, function(err, stream) {
if (err) throw err;
stream.on('close', function(code, signal) {
conn.end();
// data comes here
}).on('data', function(data) {
console.log('STDOUT: ' + data);
}).stderr.on('data', function(data) {
console.log('STDERR: ' + data);
});
// stream.end(user.password+'\n');
^^ If i do this, it will work but I won't be able to do anything else afterwards
});
}).connect({
host: 'demo.landingpage.com',
username: 'demo',
password: 'testuser!'
});
How do I enter the password programmatically? (I'm already using {pty: true} while doing conn.exec
Please enlighten!
Assuming your stream is a duplex stream, you have the possibility to write into the stream without ending it by writing
.on('data', (data) => {
stream.write(user.password+'\n');
}
or you can use an cb function
function write(data, cb) {
if (!stream.write(data)) {
stream.once('drain', cb);
} else {
process.nextTick(cb);
}
}
and
.on('data', (data) => {
write(user.password+ '\n', () => {
console.log('done');
}
});
I am fairly new to nodejs. I am trying to simply connect to my webserver below with a host, port, username and password and get the up time response as shown below. executing the file from commandline is not giving me any output, it just gives me an empty commandline. removing any of ip, port or username/pwd is throwing me errors though. What am I doing wrong?
var Client = require('ssh2').Client;
var conn = new Client();
conn.on('ready', function() {
console.log('Client :: ready');
conn.exec('uptime', function(err, stream) {
if (err) throw err;
stream.on('close', function(code, signal) {
console.log('Stream :: close :: code: ' + code + ', signal: ' + signal);
conn.end();
}).on('data', function(data) {
console.log('STDOUT: ' + data);
}).stderr.on('data', function(data) {
console.log('STDERR: ' + data);
});
});
}).connect({
host: '192.168.100.100',
port: 2122,
username: '!##!##!#',
password: '!##!##!'
});
// example output:
// Client :: ready
// STDOUT: 17:41:15 up 22 days, 18:09, 1 user, load average: 0.00, 0.01, 0.05
//
// Stream :: exit :: code: 0, signal: undefined
// Stream :: close
You need to attach an 'error' event handler to the actual connection object prior to any other handler.
var Client = require('ssh2').Client;
var conn = new Client();
conn.on('error', function(err) {
console.log('SSH - Connection Error: ' + err);
});
conn.on('end', function() {
console.log('SSH - Connection Closed');
});
conn.on('ready', function() {
// code to work with SSH
});
conn.connect({
host: '192.168.100.100',
port: 2122,
username: '!##!##!#',
password: '!##!##!'
});
I was having the same issue. Doing below resolved it -
const { Client } = require('ssh2');
I have two commands to send to server, first move forward, get the acknowledgment and then send next command move backward. I have written two separate java script files do achieve this. Can it is possible to write in single function. I am trying below code but only move forward command is sent to server.
var net = require('net');
var HOST = '127.0.0.1';
var PORT = 1850;
var client = new net.Socket();
client.connect(PORT, HOST, function() {
console.log('CONNECTED TO: ' + HOST + ':' + PORT);
client.write('READER_FWD');
//client.end();
});
client.on('data', function(data) {
console.log('DATA: ' + data);
//client.destroy();
//
if (data == 'ACK')
{
console.log('DATA1: ' + data);
client.end();
console.log('DATA2: ' + data);
client.connect(PORT, HOST, function() {
console.log('CONNECTED TO: ' + HOST + ':' + PORT);
client.write('READER_BWD');
//client.end();
console.log('DATA3: ' + data);
});
}
client.end();
});
client.on('end', function() {
console.log('disconnected from server');
});
client.on('error', function(err) {
console.log(err)
});
I have updated the code, as you rightly pointed out connection is getting close while writing, i have added some delay.
var net = require('net');
var config = {
host: '127.0.0.1',
port: 1850
};
var move = {
forward: 'READER_FWD',
backward: 'READER_BWD'
};
var client = new net.Socket();
client.connect({
host: config.host,
port: config.port
}, function () {
console.log('connected to ' + config.host + ':' + config.port);
client.write(move.forward, function () {
console.log('move forward command sent');
});
});
client.on('data', function (data)
{
var str = data.toString();
if (str === 'ACK')
{
setTimeout(function()
{
console.log('ACK received');
client.write(move.backward, function ()
{
console.log('move backward sent');
client.end();
});
}, 3000);
}
});
client.on('error', function (err) {
console.log('Error : ', err);
});
client.on('close', function () {
console.log('socket closed');
});
You don't have to end your socket and re-open it again in your 'data' listener. You can keep the same socket.
Here is my client.js file which sends the commands:
var net = require('net');
var config = {
host: '127.0.0.1',
port: 1850
};
var move = {
forward: 'READER_FWD',
backward: 'READER_BWD'
};
var client = new net.Socket();
client.connect({
host: config.host,
port: config.port
}, function () {
console.log('connected to ' + config.host + ':' + config.port);
client.write(move.forward, function () {
console.log('move forward command sent');
});
});
client.on('data', function (data) {
var str = data.toString();
if (str === 'ACK') {
console.log('ACK received');
client.write(move.backward, function () {
console.log('move backward sent');
client.end();
});
}
});
client.on('error', function (err) {
console.log('Error : ', err);
});
client.on('close', function () {
console.log('socket closed');
});
The connect() method connects the socket to the server and send the forward command to it. It's exactly the same as yours.
Then, the problem comes from your 'data' listener. Your data listener must do the following things (as you mentionned in your description):
Get data from the server
If it's the ACK message: send the backward command
Then, close the connection (if needed; if not, keep it alive)
Be careful to the following point: the Socket nodejs documentation for the event 'data' says that we are receiving a Buffer. So you need to convert it to a String to compare with another String, using for this the .toString() method of the Buffer.
Thus, as is the Nodejs net.Socket is used with events, I don't think it is possible to send the forward command, listen to the 'data' event and send the backward command.
First, it is not a good idea, because you will put the on 'data' listener after the connection and it is possible that you will miss some data!
Secondly, as it is event based, you should create your architecture that follows the process :)
Below is my code for the server:
var net = require('net');
var port = 1850;
var move = {
forward: 'READER_FWD',
backward: 'READER_BWD'
};
var server = net.createServer(function (client) {
console.log('client connected');
client.on('end', function () {
console.log('client disconnected');
});
client.on('data', function (data) {
var str = data.toString();
if (str === move.forward) {
console.log('move forward command received');
client.write('ACK', function () {
console.log('ACK sent');
});
} else if (str === move.backward) {
console.log('move backward command received: do nothing...');
} else {
console.log('unknown received message: ', str);
}
});
});
server.listen(port, function () { //'listening' listener
console.log('server bound on port: ' + port);
});
Here are also the outputs if needed:
Server:
server bound on port: 1850
client connected
move forward command received
ACK sent
move backward command received: do nothing...
client disconnected
Client:
connected to 127.0.0.1:1850
move forward command sent
ACK received
move backward sent
socket closed
I hope it answers the question. Feel free to ask if there is anything.
I have the following code in my node.js app to test the SSH connection to a server using ssh2 :
var Connection = require('ssh2');
var conn = new Connection();
conn.on('ready', function() {
console.log('Connection :: ready');
conn.exec('uptime', function(err, stream) {
if (err) throw err;
stream.on('exit', function(code, signal) {
console.log('Stream :: exit :: code: ' + code + ', signal: ' + signal);
}).on('close', function() {
console.log('Stream :: close');
conn.end();
}).on('data', function(data) {
console.log('STDOUT: ' + data);
}).stderr.on('data', function(data) {
console.log('STDERR: ' + data);
});
});
}).connect({
host: serverIp,
port: serverPort,
username: serverUsername,
privateKey: require('fs').readFileSync('./id_rsa')
});
It works fine, but if it returns an error, if I get a connection timeout for example, the node.js app crashes...
I would like to do the following :
if an error is returned, simply redirect to /servers page without crashing the app.
I tried to change the "if (err) throw err;" line with "if (err) res.redirect('/servers');" but it doesn't work...
Can someone help me with this noobie question ? I'm new to node.js :)
thanks a lot.
Listen to the error event:
conn.on('error', function(e) { /* do whatever */ });
Right now, your process crashes b/c that connection error is unhandled.
I'm using the below code to reset a password on a linux box using nodejs and ssh2 module:
// FILE * ./workflow/ssh.js
var Client = require('ssh2').Client;
var conn = new Client();
// var opts = require('optimist')
// .options({
// user: {
// demand: true,
// alias: 'u'
// },
// }).boolean('allow_discovery').argv;
// Definition of reset password;
var resetPassword = function(user, host, loginUser, loginPassword){
var command = 'echo -e "linuxpassword\nlinuxpassword" | passwd '+ user;
conn.on('ready', function() {
console.log('Client :: ready');
conn.exec(command, function(err, stream) {
if (err) throw err;
stream.on('close', function(code, signal) {
console.log('Stream :: close :: code: ' + code + ', signal: ' + signal);
conn.end();
return(code);
}).on('data', function(data) {
console.log('STDOUT: ' + data);
}).stderr.on('data', function(data) {
console.log('STDLOG: ' + data);
});
});
}).connect({
host: host,
port: 22,
username: loginUser,
password: loginPassword
});
};
exports.resetPassword = resetPassword;
I'm calling the resetPassword password function from an other module , say test.js as below.
var ssh = require('./workflow/ssh.js');
result = ssh.resetPassword('test122', '192.168.0.101', 'root' , 'password');
console.log(result)
But the console.log says "undefined". Tried using the process.nextTick, but no luck. Please help.
Welcome to the world of developing with asynchronous operations in node.js. This is a very common mistake when initially learning node.js (and a common question here on StackOverflow). An asynchronous operation completes some indeterminate time in the future and meanwhile, the rest of your code continues to run. In fact your resetPassword() function likely returns BEFORE the stream has finished and before the resetPassword result is available.
Thus you cannot return the result from the resetPassword function directly because the function returns before the result is ready. Instead, you will have to pass in a callback and get the result when the callback is called and the caller of this function will have to use the callback rather than a directly returned result. You can implement that like this:
// FILE * ./workflow/ssh.js
var Client = require('ssh2').Client;
var conn = new Client();
// var opts = require('optimist')
// .options({
// user: {
// demand: true,
// alias: 'u'
// },
// }).boolean('allow_discovery').argv;
// Definition of reset password;
var resetPassword = function(user, host, loginUser, loginPassword, callback){
var command = 'echo -e "linuxpassword\nlinuxpassword" | passwd '+ user;
conn.on('ready', function() {
console.log('Client :: ready');
conn.exec(command, function(err, stream) {
if (err) {
callback(err);
return;
}
stream.on('close', function(code, signal) {
console.log('Stream :: close :: code: ' + code + ', signal: ' + signal);
conn.end();
callback(null, code);
}).on('data', function(data) {
console.log('STDOUT: ' + data);
}).stderr.on('data', function(data) {
console.log('STDLOG: ' + data);
});
});
}).connect({
host: host,
port: 22,
username: loginUser,
password: loginPassword
});
};
exports.resetPassword = resetPassword;
And, then you can use that like this:
var ssh = require('./workflow/ssh.js');
ssh.resetPassword('test122', '192.168.0.101', 'root' , 'password', function(err, code) {
if (!err) {
console.log(code);
}
});
P.S. From your original code, it also doesn't really help you to throw from within an async callback. The exception only goes into whatever node.js infrastructure triggered the async callback, it does not get back to your original code. That's why your code has also been changed to communicate the error via the callback.
It is a node.js convention that callbacks contain at least two arguments. The first argument is an error code which should be null if no error and an appropriate error code or error object if there is an error. The second argument (and any other arguments you want to have) then communicate a successful result. It is useful to follow this convention since it is both consistent with how node.js does it elsewhere and it is compatible with some mechanical error handling such as done when interfaces are modified to use promises.
The return value from the on close event is only returning out of the closure.
You need to provide either a callback hook or a result object as a parameter from which to retrieve the result in the signature of the resetPassword method you export from the module.
As I have studied NodeJs in just recent times I agree this is late for the answer, I am building a monitoring tool and using the SSH-2 library of node.js to get the tasks done. Capturing the value in response for the API was troublesome but I thought to create the operations as Promise to capture then in Api call. I hope the below code will help.
//Operations file that I am calling from the API
var Poperation = (params)=>{
return new Promise((resolve, reject) => {
let result = "";
conn
.on("ready", function () {
conn.exec(params["command"], function (err, stream) {
if (err) {
reject(err);
};
stream
.on("close", function (code, signal) {
conn.end();
//callback(null, result);
resolve(result)
})
.on("data", function (data) {
result = data;
})
.stderr.on("data", function (data) {
});
});
})
.connect({
host: params["host"],
port: params["port"],
username: params["username"],
password: params["password"],
});
})};
Controler file for operations
const express = require('express');
const router = express.Router();
const Joi = require('joi');
const opers = require('../operations/ssh_test');
module.exports = router;
router.post('/operations',operations);
async function operations(req,res,next){
opers.Poperation(req.body).then((data)=>{res.send(data)}).catch((data)=>{res.send(data)})
console.log('finished')
}