How to set up node.js interaction with shell application? - shell

At:
node js interact with shell application
Trindaz linked to his YouTube video demonstrating how to interact with a bash shell (including interpreters available from the shell):
http://www.youtube.com/watch?v=16nFMucvwYQ
However I could not follow the frames from 20 seconds to 54 seconds. At 54 seconds the browser window shows:
....................................
connected
$ log in
$
.....................................
What are the steps required to get to this window? Any hints or guidance would be appreciated.
Thank you,
RP

I did not exactly replicate what is there in the video but I hope a sample like below should do it.
This is how I configured the server part.
var app = require('express').createServer(),
io = require('socket.io').listen(app),
sys = require('util'),
exec = require('child_process').exec;
app.listen(4990);
io.sockets.on('connection', function(socket) {
socket.on('console', function(command, callBack) {
// client sends {command: 'ls -al'}
function puts(error, stdout, stderr) {
socket.emit('commandresult', stdout);
}
exec(command.command, puts);
});
});​
I hope you can do the client part.
Hope this helps

Related

Using Rust to test telnet connection and log results

I am a Rust newbie. I've been wanting to learn Rust and decided my first project would be to build a connection testing tool packaged an executable for the tech support people at my work. Basically the tool needs to run by end-users on Windows computers and test three or more URLs using ping, tracert, and telnet. Also after running the tool, the results of the three commands should be logged into text files and lastly enclosed in a zip file at the end.
I put some code together and it's mostly working except for the telnet portion. I've been pulling my hair out trying to figure out why the telnet part is not working. I was able to compile my Rust code successfully and it runs, but no matter what the telnet part generates a telnet connection failed in the log indicating the telnet was not successful, even though I am able to run the telnet command on the same machine using the same command manually typed (telnet service1.somedomain.com 13101). So I can see telnet is installed and working...
Further down below is my code. I added some println! statements on lines 59-61 and the only clue I see so far is the status code that prints out when I run the tool says telnet command exited with status: exit code: 0xffffffff and nothing prints for telnet stdout/stderr. This seems to indicate telnet is aborting or not found, so I used the full path to the telnet.exe file on the Windows machine (C:\Windows\System32\telnet.exe) instead of just "telnet" and still got the same error.
use std::process::Command;
use std::fs::File;
use std::io::prelude::*;
use std::io::Error;
use std::path::{Path, PathBuf};
use std::io::Cursor;
//use zip::write::FileOptions;
//use zip::result::ZipWriter;
use zip::write::{FileOptions, ZipWriter};
use zip::result::ZipResult;
fn main() -> Result<(), Error> {
// Set the domains and ports to be tested
let domains_and_ports = [
("www.somedomain.com", "80"),
("serivce1.somedomain.com", "13101"),
("service2.somedomain.com", "13103")];
// Create a zip file to store the results
let zip_file_path = Path::new("results.zip");
let zip_file = File::create(zip_file_path)?;
let mut zip_writer = ZipWriter::new(zip_file);
// Set the password to encrypt the zip file
let password = b"password";
// Iterate over each domain and port combination
for (domain, port) in domains_and_ports {
// Run the ping command
let ping_output = Command::new("cmd")
.args(&["/C", format!("ping {}", domain).as_str()])
.output()?;
let ping_file_path = write_output_to_file(domain, "ping.txt", &ping_output)?;
// Run the tracert command
let tracert_output = Command::new("cmd")
.args(&["/C", format!("tracert {}", domain).as_str()])
.output()?;
let tracert_file_path = write_output_to_file(domain, "tracert.txt", &tracert_output)?;
let telnet_output = Command::new("cmd")
.args(&["/C", format!("telnet {} {}", domain, port).as_str()])
.output()?;
let telnet_file_path = write_output_to_file(domain, "telnet.txt", &telnet_output)?;
// Write the ping, tracert, and telnet results to the zip file
zip_writer.start_file(format!("{}_ping.txt", domain), FileOptions::default())?;
zip_writer.write_all(&read_file_contents(ping_file_path)?)?;
zip_writer.start_file(format!("{}_tracert.txt", domain), FileOptions::default())?;
zip_writer.write_all(&read_file_contents(tracert_file_path)?)?;
zip_writer.start_file(format!("{}_telnet.txt", domain), FileOptions::default())?;
println!("telnet command exited with status: {}", telnet_output.status);
println!("telnet stdout: {}", String::from_utf8_lossy(&telnet_output.stdout));
println!("telnet stderr: {}", String::from_utf8_lossy(&telnet_output.stderr));
if telnet_output.status.success() {
zip_writer.write_all(b"telnet connection successful")?;
} else {
zip_writer.write_all(b"telnet connection failed")?;
}
}
// Close the zip file
zip_writer.finish()?;
println!("Results saved in results.zip");
Ok(())
}
fn write_output_to_file(domain: &str, command: &str, output: &std::process::Output) -> Result<PathBuf, Error> {
let fname = format!("{}_{}", domain, command).as_str().to_owned();
//let file_path = Path::new(format!("{}_{}", domain, command).as_str());
let file_path = Path::new(&fname);
let mut file = File::create(file_path)?;
file.write_all(&output.stdout)?;
file.write_all(&output.stderr)?;
Ok(file_path.to_path_buf())
}
fn read_file_contents(file_path: PathBuf) -> Result<Vec<u8>, Error> {
let mut file = File::open(file_path)?;
let mut contents = Vec::new();
file.read_to_end(&mut contents)?;
Ok(contents)
}
This is what prints (one set shown for first domain, but it prints three sets with same error, for each domain/port combination)...
Does anyone have experience running telnet commands on Windows, plus logging telnet results using Rust? If anyone has tips or example code to fix my example above I would be truly grateful. As a side-note, I know something similar could be done with a Windows batch file or Python but I was really wanting to get the Rust project working so I could have a little "win" with this my first go around with it.
Thanks in advance for any help you can offer!
PS - the domains and ports are made-up for this post :).

Why does the first response takes more time in GAS web app?

Edit: I am using clasp. Updated the code to the actual GAS!
I have a GAS deployed as a web app. We send POST requests from Slack via a slash command and it needs a response in less than 3000ms because GAS can't handle asynchronous code.
At the first request, it takes more than 3000ms to send a response but on the following requests, it is around 1500ms.
The doPost function looks like the following.
var exports = exports || {};
var module = module || { exports: exports };
Logger = BetterLog.useSpreadsheet('spreadsheetId');
function doPost(request) {
var startExecutionDate = new Date();
var path = request.parameter.path;
Logger.log("Request received with path: " + path);
var response = Responses.Error;
var token = request.parameter.token;
if (path.startsWith('/slack')) {
Logger.log("Slack request");
var slackRouter = new SlackRouter();
response = slackRouter.post(request);
// ...
}
// ...
}
And this is the code for the Slack Router.
var exports = exports || {};
var module = module || { exports: exports };
var SlackRouter = (function () {
function SlackRouter() {
}
SlackRouter.prototype.post = function (request) {
var path = request.parameter.path;
switch (path) {
case Routes.Team:
Logger.log("For team");
// ...
}
};
return SlackRouter;
}());
exports.SlackRouter = SlackRouter;
I have the timestamps for each log.
First attempt
| Timestamp | Delta in ms | Log Message |
|--------------|-------------|---------------|
| 11:22:34:164 | 0 | Path: ... |
| 11:22:35:354 | 1190 | Slack request |
| 11:22:35:462 | 108 | For team |
Second attempt
| Timestamp | Delta in ms | Log Message |
|--------------|-------------|---------------|
| 11:22:45:047 | 0 | Path: ... |
| 11:22:45:164 | 117 | Slack request |
| 11:22:45:350 | 186 | For team |
I had several ideas already like the web app goes to a sleep state but since we calculate delta from the first log message it doesn't make sense.
So what is going on behind the scenes? Are you aware of any easy workarounds? If possible I don't want to build a microservice to send a response to Slack in time and later send the actual response.
The Apps Script servers don't keep every script ever written or deployed loaded in memory, and so scripts that haven't been run in a while need to be loaded from disk first. This is usually referred to as a "cold start time" in Cloud providers.
Answered by Eric Koleda on Google Apps Script Community forum
The most glaring issue is your use of ES6 syntax in your doPost() method.
Google Apps Script does not support ES6 template string syntax and only partially supports destructuring assignments. So that might be your issue. Your doPost() probably fails to return a value as a result so Slack likely repeats the request until it times out.
Try removing the BetterLog library. That may be causing the initial first-time delay.
https://developers.google.com/apps-script/guides/libraries
Warning: A script that uses a library does not run as quickly as it would if all the code were contained within a single script project. Although libraries can make development and maintenance more convenient, you should use them sparingly in projects where speed is critical. Because of this issue, library use should be limited in add-ons.

nodejs decode (child processes use)

I use spawn for JScript (from windows)
My problem with decode text
???? ??? T-SQL Microsoft SQL Server 2012
???? ??? - russian text.
var worker,path = require('path'), spawn = require('child_process').spawn, cscript = path.join(process.env.SystemRoot, 'system32', 'cscript.exe')
function startCscript() {
worker = spawn( cscript, [ '/nologo', 'testwsh.js' ] );
worker.stdout.setEncoding('utf8');
worker.stdout.on( 'data', onData );
}
function onData (data) {
console.log(data.toString());
}
startCscript();
How do I get Russian letters? Thanks all )
Can you try to set the worker encoding as ucs2 or utf16le.
Change this
worker.stdout.setEncoding('utf8');
to this
worker.stdout.setEncoding('ucs2');
Tell if it solves the issue.
look at issue 2190 and issue 2196:
Node.js does always expect UTF-8 output from a child process, but
Windows with Russian locale defaults to CP866
you need to execute chcp 65001 to change current console code page.
My solution: use iconv-lite, to get the Russian text in the console.

Node.js: How to send control C to child process

I am writing one web-like linux shell using node.js + socket.io. Simple command like, ls, cd are working well.
But when issue command like ping google.com, the stdout is printing endlessly.
I tried to send Ctrl +C to stdin, but no luck.
1) spawn 'bash' process
spawn = require('child_process').spawn;
var sh = spawn('bash');
2) send bash stdout to socket.io
sh.stdout.on('data', function(data) {
console.log('stdout' + data);
listener.sockets.emit("stdout",new Buffer(data));
});
3) Sending Ctl C (\x03) to bash's stdin.
var listener = io.listen(server);
listener.set('log level',1);
listener.sockets.on('connection', function(client){
client.on('message', function(data){
if(data === "KILL") {
console.log('!!!!' + data);
sh.stdin.write('\x03');
client.broadcast.send(new Buffer("KILLING "));
//return;
};
console.log(data);
sh.stdin.write(data+"\n");
client.broadcast.send(new Buffer("> "+data));
});
});
I am stuck at this point. Seems like
Try process.kill(sh.pid). I use this to kill workers in a cluster when my master process shuts down. sh.signalCode should be equal to SIGTERM. Of course, I have no idea if this works on Windows.
try stdout.push(null).
I tried it on node 12. This will not kill your child process instead it will tell you output stream that there is no more input stream.

pushing tty output to node

For the satisfaction of my curiosity gene i'd like to play with bash/node combo.
I don't know how to make those 2 talk together. I just had a great smile on my face finding about TTY.JS ;-)
How do I feed terminal's output (sdtout?) to node? I thought about redirecting the stream to file and read it with node through 'fs' module. But there must be some prettier way I bet
thanks in advance.
Something like this should send terminal output to node
var app = require('express').createServer(),
io = require('socket.io').listen(app),
sys = require('util'),
exec = require('child_process').exec;
app.listen(4990);
io.sockets.on('connection', function(socket) {
socket.on('console', function(command, callBack) {
// client sends {command: 'ls -al'}
function puts(error, stdout, stderr) {
socket.emit('commandresult', stdout);
}
exec(command.command, puts);
});
});​
Hope this helps

Resources