I'm building a node.js application to manage servers.
In this application, I can create some shell scripts and execute them on a server by clicking on a button.
Here is what I have in my routes.js file for this action :
app.get('/exec', isLoggedIn, function(req, res) {
var url = require('url');
var url_parts = url.parse(req.url, true);
var query = url_parts.query;
var Server = require('../app/models/server');
var Script = require('../app/models/script');
var async = require('async');
var scriptId = query.scriptId;
var userId = query.userId;
var serverId = query.serverId;
var stdout = "";
var stderr = "";
async.parallel({
servers: function(callback){
Server.find({"_id" : serverId}).exec(callback);
},
scripts: function(callback){
Script.find({"_id" : scriptId}).exec(callback);
}
}, function(err, results) {
if (err)
res.send(err);
var selectedServer = results.servers;
var selectedScript = results.scripts;
var serverIp = selectedServer[0].serverDetails.serverAddress;
var serverPort = selectedServer[0].serverDetails.serverPort;
var serverUsername = selectedServer[0].serverDetails.serverUsername;
var scriptContent = selectedScript[0].scriptDetails.scriptContent;
var Connection = require('ssh2');
var conn = new Connection();
conn.on('ready', function() {
//console.log('Connection :: ready');
conn.exec(scriptContent, 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_out) {
console.log('STDOUT: \n' + data_out);
stdout = stdout + data_out;
res.render('execresults.ejs', {
stdout : stdout
});
}).stderr.on('data', function(data_err) {
console.log('STDERR: \n' + data_err);
});
});
}).connect({
host: serverIp,
port: serverPort,
username: serverUsername,
privateKey: require('fs').readFileSync('id_rsa')
});
conn.on('error', function(e) { console.log("Connection failed or timed out")});
});
});
It works fine and I have the results of the execution in the console. But I would like to display both stdout and stderr results in two textarea of an ejs page.
The problem is that when I run a simple command like "uptime", it works, I will have the stdout result displayed in my textarea.
But when the shell script returns both stdout and stderr, or if stdout is a large amount of text, I don't have everything passed to my ejs page because of this error I think : Error: Can't set headers after they are sent.
My question is : How can I send both stdout and stderr to my ejs page, only when the script is completely executed ? I tried to build a "stdout" variable by concatenating all stdout output but it doesn't seem to work...
Can someone help me ?
thanks a lot
Assuming the process eventually ends on its own and it does not output a lot of data, you could probably just buffer the data and then render when the process closes:
conn.exec(scriptContent, function(err, stream) {
if (err)
return res.send(err);
var stdout = '',
stderr = '';
stream.on('close', function() {
conn.end();
res.render('execresults.ejs', {
stdout: stdout,
stderr: stderr
});
}).on('data', function(data) {
stdout += data;
}).stderr.on('data', function(data) {
stderr += data;
});
});
Secondly,
if (err)
res.send(err);
should really have a return:
if (err)
return res.send(err);
to prevent further execution on errors.
Related
I have a file that I am trying to write to from a post. Currently, the FS does not error nor does it write to file. However, when taking the same code from the deployed build and running it locally, it works. I even kept the file path consistent since it was throwing no permissions error at first. I ensured this file wrote to the same directory so each Filewrite Stream process would look at the same directory and file.
Local build:
var fs = require('fs');
const path = require('path');
var user_name = 'Password';
var password = 'test';
var errSTR = ""
fs.writeFile('C:\\hi.txt', 'Content to write', { flag: 'w' }, function(err) {
if (err)
return console.error(err);
fs.readFile('C:\\hi.txt', 'utf-8', function (err, data) {
if (err)
return console.error(err);
console.log(data);
});
});
Deployed Build:
app.route('/test')
.get(function(req, res) {
res.send('GET test');
})
.post(function(req, res) { // Start Post
var boolTry = false;
try {
boolTry = true;
var bool = false
var user_name = "Password"//req.body.user;
var password = "test"//req.body.password;
var errSTR = ""
fs.writeFile('C:\\hi.txt', user_name + password, { flag: 'w' }, function(err) {
if (err)
return console.error(err);
fs.readFile('C:\\hi.txt', 'utf-8', function (err, data) {
if (err)
return console.error(err);
res.send(500 + err);
console.log(data);
});
})
} catch (error) {
bool = true
errSTR = error
}
res.send('POST test' + " " + boolTry + " " + bool + " " + errSTR + ";")
})//END POST
.put(function(req, res) {
res.send('PUT test');
});
The local build will properly write to the file, while the dev build appears to do nothing. It should be noted by booleans were being used to understand how the file writer works but here is the server response from build: successful response POST test true false ;
Using:
IISNODE for iis: 7.x
Express: 4.16.2
node.js: v8.9.4
cors: 2.8.4
body-parser: 1.17.2
Sidenote: If you are confused by the writing portion of code, the intention was to write, check error then read, check error for assurance.
Update
Reoccurring error based on certain filewrite methods Error: EPERM: operation not permitted, open. Yes, all permissions for the directory are enabled along with ensuring read and write are checked.
Using node js I'm calling external script in MATLAB and Python
That is working well using terminal commands to run the scripts using those application ('start')
But when I'm trying to close them using kill() ('stop') I get an error:
TypeError: exec.kill is not a function
I'm using MAC OS and this is my code:
var app = require('express')();
var http = require('http').Server(app);
var io = require('socket.io')(http);
var fs = require('fs');
var path = require('path');
const exec = require('child_process').exec;
var cmd1 =
'/Applications/MATLAB_R2016b.app/bin/matlab -nojvm < /Users/dorsimon/Desktop/liftrack/Testing_Classifier.m';
var cmd2 = 'python /Users/dorsimon/Desktop/liftrack/arduino_sampling_for_workout.py';
app.get('/', function(req, res) {
res.sendfile('index.html');
});
//Whenever someone connects this gets executed
io.on('connection', function(socket) {
console.log('A user connected');
fs.watch('/Users/dorsimon/Desktop/liftrack/result/', function(event, test) {
console.log('event is: ' + event);
fs.readFile('/Users/dorsimon/Desktop/liftrack/result/results.csv', 'utf-8', function read(
err,
data
) {
if (err) {
console.log('err');
throw err;
}
console.log(data);
socket.send(data);
});
});
fs.watch('/Users/dorsimon/Desktop/liftrack/go', function(event, test) {
console.log('event is: ' + event);
fs.readFile('/Users/dorsimon/Desktop/liftrack/go/go.csv', 'utf-8', function read(err, data) {
if (err) {
console.log('err');
throw err;
}
console.log(data);
socket.send(data);
});
});
//Whenever someone disconnects this piece of code executed
socket.on('disconnect', function() {
console.log('A user disconnected');
});
socket.on('start', function() {
exec(cmd1, function(error, stdout, stderr) {
// command output is in stdout
});
exec(cmd2, function(error, stdout, stderr) {
// command output is in stdout
});
});
socket.on('stop', function() {
exec.kill();
});
});
http.listen(3000, function() {
console.log('listening on *:3000');
});
How can I kill those child_process that I started?
Thanks
Dor
You need to store the return value of exec and call kill on that. You can store those values as properties on socket for convenience:
socket.on('start', function() {
socket.child1 = exec(cmd1, function(error, stdout, stderr) {
// command output is in stdout
});
socket.child2 = exec(cmd2, function(error, stdout, stderr) {
// command output is in stdout
});
});
socket.on('stop', function() {
socket.child1.kill();
socket.child2.kill();
});
FWIW, you probably have to do the same with the return values of fs.watch(), and call close() on them when the socket gets closed, otherwise you'll probably run into issues after your server has been running for a while (it creates two watchers for each socket.io connection, but doesn't remove them, so they'll linger).
Following is the Server.js File, here I am Fetching The Details From Table which working Good. I need to Get a Variable From k.php which is in the Same Folder.Iam using npm exec-php module to get the Values From Php File. But The Variable is Showing Undefined.
var app = require('http').createServer(handler),
io = require('socket.io').listen(app),
fs = require('fs'),
express=require('express'),
session=require('express-session'),
mysql = require('mysql'),
execPhp = require('exec-php'),
connectionsArray = [],
connection = mysql.createConnection({
host: 'localhost',
user: 'root',
password: '',
database: 'test',
port: 3306
}),
POLLING_INTERVAL = 3000,
pollingTimer;
// If there is an error connecting to the database
connection.connect(function(err) {
// connected! (unless `err` is set)
if (err) {
console.log(err);
}
});
// creating the server ( localhost:8000 )
app.listen(8000);
// on server started we can load our client.html page
function handler(req, res) {
fs.readFile(__dirname + '/client.php', function(err, data) {
if (err) {
console.log(err);
res.writeHead(500);
return res.end('Error loading client.php');
}
res.writeHead(200);
res.end(data);
});
}
execPhp('k.php', function(error, php, outprint){
// Here I expected The outprint Will be 'One' but it print undefined
console.log(outprint);
php.my_function(1, 2, function(err, result, output, printed){
//this my_function is also showing Error
});
});
var pollingLoop = function() {
// Doing the database query
var query = connection.query('SELECT * FROM users where user_id=1'),
users = []; // this array will contain the result of our db query
// setting the query listeners
query
.on('error', function(err) {
// Handle error, and 'end' event will be emitted after this as well
console.log(err);
updateSockets(err);
})
.on('result', function(user) {
// it fills our array looping on each user row inside the db
users.push(user);
})
.on('end', function() {
// loop on itself only if there are sockets still connected
if (connectionsArray.length) {
pollingTimer = setTimeout(pollingLoop, POLLING_INTERVAL);
updateSockets({
users: users
});
} else {
console.log('The server timer was stopped because there are no more socket connections on the app')
}
});
};
// creating a new websocket to keep the content updated without any AJAX request
io.sockets.on('connection', function(socket) {
console.log('Number of connections:' + connectionsArray.length);
// starting the loop only if at least there is one user connected
if (!connectionsArray.length) {
pollingLoop();
}
socket.on('disconnect', function() {
var socketIndex = connectionsArray.indexOf(socket);
console.log('socketID = %s got disconnected', socketIndex);
if (~socketIndex) {
connectionsArray.splice(socketIndex, 1);
}
});
console.log('A new socket is connected!');
connectionsArray.push(socket);
});
var updateSockets = function(data) {
// adding the time of the last update
data.time = new Date();
console.log('Pushing new data to the clients connected ( connections amount = %s ) - %s', connectionsArray.length , data.time);
// sending new data to all the sockets connected
connectionsArray.forEach(function(tmpSocket) {
tmpSocket.volatile.emit('notification', data);
});
};
console.log('Please use your browser to navigate to http://localhost:8000');
the main Problem is in these Lines
execPhp('k.php', function(error, php, outprint){
// Here I expected The outprint Will be 'One' but it print undefined
console.log(outprint);
php.my_function(1, 2, function(err, result, output, printed){
//this my_function is also showing Error
});
});
The Following is k.php in the same folder
<?php
echo "One";
function my_function($arg1, $arg2){
echo "Two";
return $arg1 + $arg2;
}
?>
This is the Error
I am running this script with node child_process.fork api.
That is my express application script, from where I start my application:
/**
* Module dependencies.
*/
var express = require('express')
, routes = require('./routes')
, http = require('http')
, path = require('path');
var app = express();
//database connection
var connection = require('express-myconnection');
var mysql = require('mysql');
//all environments
app.set('port', process.env.PORT || 3000);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(express.favicon());
app.use(express.logger('dev'));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.static(path.join(__dirname, 'public')));
//development only
if ('development' == app.get('env')) {
app.use(express.errorHandler());
}
app.use(
connection(mysql,{
host: 'localhost',
user: 'root',
password : '',
port : 3306, //port mysql
database:'test-db'
},'pool')
);
//routes
//app.get('/', routes.index);
app.get('/', routes.list);
app.use(app.router);
//run script
var cp = require('child_process');
var child = cp.fork('dataGrabber/pusherMysql');
child.on('message', function(m) {
// Receive results from child process
console.log('received: ' + m);
});
http.createServer(app).listen(app.get('port'), function(){
console.log('Express server listening on port ' + app.get('port') + ' http://localhost:' + app.get('port'));
});
That`s the part where I run my script:
//run script
var cp = require('child_process');
var child = cp.fork('dataGrabber/pusherAPI');
child.on('message', function(m) {
// Receive results from child process
console.log('received: ' + m);
});
As you can see I load my script and want to receive a message from the child.
That is my pusherAPI.js script:
var mysql = require('mysql');
var Pusher = require('pusher-client');
/**
* connect with mysql db
*/
var connection = mysql.createConnection({
host : 'localhost',
user : 'root',
database : 'test-db',
port : '3306',
password : ''
});
connection.connect(function(err) {
if (err) {
console.error('error connecting: ' + err.stack);
return;
}
console.log('connected as id ' + connection.threadId);
});
//connect with the server
var API_KEY = 'cb65d0a7a72cd94adf1f';
var pusher = new Pusher(API_KEY, {
encrypted: true
});
var channel = pusher.subscribe("ticker.160");
channel.bind("message", function(data) {
console.log(data);
this.data = data;
/**
* save data to db
*/
var trade = {
timestamp : data.trade.timestamp,
price : data.trade.topbuy.price,
};
var query = connection.query('INSERT INTO trades SET ?', trade, function(err, result) {
if (err) {
connection.rollback(function() {
throw err;
});
}
//push message back to the app.js
process.on('message', function(m) {
// Pass results back to parent process
m = "insert happened";
process.send(m);
});
connection.commit(function(err) {
if (err) {
connection.rollback(function() {
throw err;
});
}
});
});
console.log(query.sql);
});
I want to send a message back to my app.js, whenever an insertion happened to my sql db
My script starts and runs my queries. However, process.send(m); does not send anything back.
Any recommendations what I am doing wrong?
I appreciate your answer!
Update
When changing my pusherAPI.js to, I get nothing back in the console.
var channel = pusher.subscribe("ticker.160");
process.on('insert_message', function(m) {
channel.bind("message", function(data) {
console.log(data);
this.data = data;
/**
* save data to db
*/
var trade = {
timestamp : data.trade.timestamp,
price : data.trade.topbuy.price,
};
var query = connection.query('INSERT INTO trades SET ?', trade, function(err, result) {
if (err) {
connection.rollback(function() {
throw err;
});
}
//push message back to the app.js
// Pass results back to parent process
m = "insert happened";
process.send(m);
connection.commit(function(err) {
if (err) {
connection.rollback(function() {
throw err;
});
}
});
});
console.log(query.sql);
});
});
In my app.js I changed my code like that:
//run script
var cp = require('child_process');
var child = cp.fork('dataGrabber/pusherAPI');
child.on('insert_message', function(m) {
// Receive results from child process
console.log('received: ' + m);
});
Move your process.send(m); outside of the message event handler. Otherwise you're adding a new message event handler for every query and those event handlers only fire when the parent process sends it a message. Example:
var channel = pusher.subscribe("ticker.160");
channel.bind("message", function(data) {
this.data = data;
/**
* save data to db
*/
var trade = {
timestamp : data.trade.timestamp,
price : data.trade.topbuy.price,
};
var query = connection.query('INSERT INTO trades SET ?',
trade,
function(err, result) {
if (err) {
connection.rollback(function() {
throw err;
});
return;
}
//push message back to the app.js
m = "insert happened";
process.send(m);
connection.commit(function(err) {
if (err) {
connection.rollback(function() {
throw err;
});
}
});
});
});
I am having trouble getting face tracking and detection from the npm opencv package. I'm currently just trying to drawing a circle each face.
I have listed below the error and the files below that. I'm not sure if its just a binding issue, if so what steps should I take to resolve it.
OpenCV (2.4.8.2) was installed via brew FYI - It could be a versions thing I guess
Error:
/Users/gwilliams2/Sites/facetracking/node_modules/opencv/lib/opencv.js:29
var face_cascade = new cv.CascadeClassifier(classifier);
^
TypeError: Error loading file
at Matrix.matrix.detectObject (/Users/gwilliams2/Sites/facetracking/node_modules/opencv/lib/opencv.js:29:23)
at /Users/gwilliams2/Sites/facetracking/server.js:170:8
at Socket.<anonymous> (/Users/gwilliams2/Sites/facetracking/server.js:161:11)
at Socket.EventEmitter.emit [as $emit] (events.js:95:17)
at SocketNamespace.handlePacket (/Users/gwilliams2/Sites/facetracking/node_modules/socket.io/lib/namespace.js:335:22)
at Manager.onClientMessage (/Users/gwilliams2/Sites/facetracking/node_modules/socket.io/lib/manager.js:488:38)
at WebSocket.Transport.onMessage (/Users/gwilliams2/Sites/facetracking/node_modules/socket.io/lib/transport.js:387:20)
at Parser.<anonymous> (/Users/gwilliams2/Sites/facetracking/node_modules/socket.io/lib/transports/websocket/hybi-16.js:39:10)
at Parser.EventEmitter.emit (events.js:95:17)
at finish (/Users/gwilliams2/Sites/facetracking/node_modules/socket.io/lib/transports/websocket/hybi-16.js:288:16)
client.js
var APP = {
// setup the web socket
socket: io.connect('http://gaz.local:3000'),
video: null,
canvas: null,
imgData: null,
init: function (){
// get elements
APP.video = $('#myVideo')[0];
APP.canvas = $('#myCanvas')[0];
// start app
APP.main();
},
main: function(){
// if we can get a webcam
if(APP.hasGetUserMedia()){
navigator.webkitGetUserMedia(
{video:true, audio:false},
APP.videoSuccess,
APP.videoError
);
} else {
alert('Sorry, you do not have webcam access');
}
},
hasGetUserMedia: function (){
return !!(navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia || navigator.msGetUserMedia);
},
videoSuccess: function (stream){
// set the video to stream
APP.video.src = window.URL.createObjectURL(stream);
// set canvas size
APP.canvas.width = APP.video.width;
APP.canvas.height = APP.video.height;
// draw video
var i = 0;
setTimeout(function(){
// darw the image to the canvas
APP.canvas.getContext('2d').drawImage(APP.video, 0, 0);
APP.imgData = APP.canvas.toDataURL('image/jpeg');
APP.imgData = APP.imgData.replace('data:image/jpeg;base64,', '');
// send the file in json format to the server
var jsonData = JSON.stringify({imgData: APP.imgData, count: i});
APP.socket.emit('update', jsonData);
// APP.socket.on('put_down', function (data) {
// console.log(data);
// });
// update the counter
i++;
}, 50);
},
videoError: function (err){
alert('Error: ' + err);
},
};
$(function (){ APP.init(); });
Server.js
/*
Setup a server, RTC and face tracking
Install opencv
brew install opencv
NPM
https://www.npmjs.org/package/fs
https://www.npmjs.org/package/http
https://www.npmjs.org/package/express
https://www.npmjs.org/package/socket.io
https://www.npmjs.org/package/opencv
*/
var APP = {
// include some scripts
server: null,
port: 3000,
express: require('express'),
app: null,
mdb: require('mongodb'),
dbUrl: 'mongodb://127.0.0.1:27017/test',
db: null,
io: null,
cv: require('opencv'),
fs: require("fs"),
init: function (){
// connect stuff up
APP.app = APP.express();
APP.server = require('http').createServer(APP.app).listen(APP.port, '0.0.0.0');
APP.io = require('socket.io').listen(APP.server);
// http routing
APP.routing();
// connect the websocket
APP.io.on('connection', function (socket){
console.log('Server started on port ' + APP.port);
APP.main(socket);
});
},
// open the db
openDB: function (){
APP.mdb.connect(APP.dbURL, function(err, db){
if(err)
throw err;
APP.db = db;
});
},
// close the db
closeDB: function (){
APP.db.close();
},
// insert a file to the db
dbInsert: function (col, data){
// open the db
APP.openDB();
var collection = APP.db.collection(col);
collection.insert(data, function(err, docs){
if(err){
console.warn(err.message);
} else {
console.log('Successfully inserted record');
}
});
// close the db
APP.closeDB();
},
// insert a file to the db
dbUpdate: function (col, crit, data){
// open the db
APP.openDB();
var collection = APP.db.collection(col);
collection.update(crit, {$set: {hi: 'there'}}, {w:1}, function (){
if(err){
console.warn(err.message);
} else {
console.log('Successfully updated record');
}
});
// close the db
APP.closeDB();
},
// find a file in the db
dbFind: function (col){
// open the db
APP.openDB();
var collection = APP.db.collection(col);
collection.find().toArray(function(err, results) {
console.dir(results);
});
// close the db
APP.closeDB();
},
// routing files
routing: function (){
// set directory to use for files prefixed with /library
APP.app.use('/library', APP.express.static(__dirname + '/library'));
// index page
APP.app.get('/*', function (req, res){
// get query
// console.log(req.query);
// get file
res.sendfile(__dirname + '/' + req.route.params);
});
},
main: function (socket){
// APP.io.sockets.emit('put_down', {'fingers': 's'});
socket.on('update', function (data) {
// console.log(data);
// get the data
data = JSON.parse(data);
// create the file
var filename = 'file' + data.count + '.jpg';
APP.fs.writeFile(filename, data.imgData, 'base64', function(err) {
if(err)
console.log(err);
});
// open file with open cv
APP.cv.readImage(filename, function(err, im){
// handle errors
if(err)
return err;
console.log(im);
// /Users/gwilliams2/Sites/facetracking/node_modules/opencv/data/haarcascade_frontalface_default.xml
im.detectObject('./haarcascade_frontalface_default.xml', {}, function(err, faces){
for (var i=0;i<faces.length; i++){
var x = faces[i];
im.ellipse(x.x + x.width/2, x.y + x.height/2, x.width/2, x.height/2);
}
im.save('./out.png');
});
});
});
}
};
// run the script
APP.init();
Solved, firstly, I had linked to the xml file incorrectly, as the path was incorrect as suggested in the comments above. I then noticed the buffer for the open file had been close. Since everything is asynchronous, the code in the server for analysing the file needs to be nested in the file saving callback.