Spawning a child process with tty in node.js - javascript

I am trying to do some work on a remote server using ssh--and ssh is called on the local machine from node.js
A stripped down version of the script looks like this:
var execSync = require("child_process").execSync;
var command =
'ssh -qt user#remote.machine -- "sudo mv ./this.thing /to/here/;"';
execSync(command,callback);
function callback(error,stdout,stderr) {
if (error) {
console.log(stderr);
throw new Error(error,error.stack);
}
console.log(stdout);
}
I get the requiretty error sudo: sorry, you must have a tty to run sudo.
If I run ssh -qt user#remote.machine -- "sudo mv ./this.thing /to/here/;" directly from the command line--in other words, directly from a tty--I get no error, and this.thing moves /to/there/ just fine.
This is part of a deploy script where it really wouldn't be ideal to add !requiretty to the sudoers file.
Is there anyway to get node.js to run a command in a tty?

There's a few options:
If you don't mind re-using stdin/stdout/stderr of the parent process (assuming it has access to a real tty) for your child process, you can use stdio: 'inherit' (or only inherit individual streams if you want) in your spawn() options.
Create and use a pseudo-tty via the pty module. This allows you to create a "fake" tty that allows you to run programs like sudo without actually hooking them up to a real tty. The benefit to this is that you can programmatically control/access stdin/stdout/stderr.
Use an ssh module like ssh2 that doesn't involve child processes at all (and has greater flexibility). With ssh2 you can simply pass the pty: true option to exec() and sudo will work just fine.

ssh -qt user#remote.machine -- "sudo mv ./this.thing /to/here/;"
Per the ssh man page:
-t
Force pseudo-terminal allocation. This can be used to execute arbitrary
screen-based programs on a remote machine, which can be very
useful, e.g. when implementing menu services. Multiple -t options
force tty allocation, even if ssh has no local tty.
When you run ssh interactively, it has a local tty, so the "-t" option causes it to allocate a remote tty. But when you run ssh within this script, it doesn't have a local tty. The single "-t" causes it to skip allocating a remote tty. Specify "-t" twice, and it should allocate a remote tty:
ssh -qtt user#remote.machine -- "sudo mv ./this.thing /to/here/;"
^^-- Note

Some programs must be running tty. "child_process" library working bad when tty inputs.
I have tried for docker ( docker run command like ssh need tty), with microsoft node-pty library https://github.com/microsoft/node-pty
And this is worked me.
let command = "ssh"
let args = '-qt user#remote.machine -- "sudo mv ./this.thing /to/here/;"'
const pty = require("node-pty");
let ssh = pty.spawn( command , args.split(" ") )
ssh.on('data', (e)=>console.log(e));
ssh.write("any_input_on_runngin_this_program");

It seems that you can use FORCE_COLOR environment variable:
spawn('node', options, {
stdio: 'pipe',
cwd: process.cwd(),
env: {
...{ FORCE_COLOR: 1 },
...process.env
}
})

Related

Send MQTT from Browser with simple nodejs [duplicate]

I have a virtual machine that is supposed to be the host, which can receive and send data. The first picture is the error that I'm getting on my main machine (from which I'm trying to send data from). The second picture is the mosquitto log on my virtual machine. Also I'm using the default config, which as far as I know can't cause these problems, at least from what I have seen from other examples. I have very little understanding on how all of this works, so any help is appreciated.
What I have tried on the host machine:
Disabling Windows defender
Adding firewall rules for "mosquitto.exe"
Installing mosquitto on a linux machine
Starting with the release of Mosquitto version 2.0.0 (you are running v2.0.2) the default config will only bind to localhost as a move to a more secure default posture.
If you want to be able to access the broker from other machines you will need to explicitly edit the config files to either add a new listener that binds to the external IP address (or 0.0.0.0) or add a bind entry for the default listener.
By default it will also only allow anonymous connections (without username/password) from localhost, to allow anonymous from remote add:
allow_anonymous true
More details can be found in the 2.0 release notes here
You have to run with
mosquitto -c mosquitto.conf
mosquitto.conf, which exists in the folder same with execution file exists (C:\Program Files\mosquitto etc.), have to include following line.
listener 1883 ip_address_of_the_machine(192.168.1.1 etc.)
By default, the Mosquitto broker will only accept connections from clients on the local machine (the server hosting the broker).
Therefore, a custom configuration needs to be used with your instance of Mosquitto in order to accept connections from remote clients.
On your Windows machine, run a text editor as administrator and paste the following text:
listener 1883
allow_anonymous true
This creates a listener on port 1883 and allows anonymous connections. By default the number of connections is infinite. Save the file to "C:\Program Files\Mosquitto" using a file name with the ".conf" extension such as "your_conf_file.conf".
Open a terminal window and navigate to the mosquitto directory. Run the following command:
mosquitto -v -c your_conf_file.conf
where
-c : specify the broker config file.
-v : verbose mode - enable all logging types. This overrides
any logging options given in the config file.
I found I had to add, not only bind_address ip_address but also had to set allow_anonymous true before devices could connect successfully to MQTT. Of course I understand that a better option would be to set user and password on each device. But that's a next step after everything actually works in the minimum configuration.
For those who use mosquitto with homebrew on Mac.
Adding these two lines to /opt/homebrew/Cellar/mosquitto/2.0.15/etc/mosquitto/mosquitto.conf fixed my issue.
allow_anonymous true
listener 1883
you can run it with the included 'no-auth' config file like so:
mosquitto -c /mosquitto-no-auth.conf
I had the same problem while running it inside docker container (generated with docker-compose).
In docker-compose.yml file this is done with:
command: mosquitto -c /mosquitto-no-auth.conf

start node and pm2 application from bat file

I have a chat-bot application running on node and I keep it always active thanks to pm 2.
I would like to improve the way I launch the application. Instead of running the start command from the console, it would be nice to double click a .bat file.
I am trying to develop the bat file, but I lack knowledge.
I am grateful for any help.
#echo off
SET PM2_HOME=C:\Users\Usuario\.pm2
pm2 start C:\Users\Usuario\Desktop\ROBOTs\Chatbot SCTR\app.js
echo servicio ejecutado
This bat file that I developed does not work. I know that I am not calling the variables, because I don't know how to include it, since I always execute the pm2 start app.js command.
my application does not use ports like 8080 and others, because the same library allows me to establish a connection and with pm 2 I keep it always active.
add the start command to your package.json for launching your app with pm2, then with your bat file just direct it to run with npm or yarn, whatever your default package manager is
edit:
here is a sample of a script in bash, but the concept will be the same for batch
#!/bin/bash
## detect operating system machine so we can setup some environment variables
UNAME="$(uname -s)"
case "${UNAME}" in
Linux*) OS='linux';;
Darwin*) OS='mac';;
CYGWIN*) OS='cygwin';;
MINGW*) OS='mingw';;
*) OS="UNKNOWN:${UNAME}"
esac
## if OS is Ubuntu (IE Production Box) set the path of variables
if [ $OS == 'linux' ]
then
YARN=/usr/bin/yarn
PM2=/usr/bin/pm2
fi
## if OS is Mac (IE Development Box) set the path of variables
if [ $OS == 'mac' ]
then
YARN=/usr/local/bin/yarn
PM2=/usr/local/bin/pm2
fi
## run the app
cd /var/www/application || exit
$YARN run productionStart
$PM2 save
exit $?
here is the line of code for starting the app on mac/linux we use from our package.json
"productionStart": "pm2 start ecosystem.config.js --env=production",
for more information about starting your app with an ecosystem file with pm2, see the docs here

Bot shuts down when putty window is closed

I created a discord bot and am now attempting to run it off an Ubuntu Machine.
I installed the folders of the bot and NodeJs, here is what I used to install NodeJS:
sudo apt-get install -y nodejs
Then I used cd to select the directory, and started my bot using node index.js
The bot started, however when I went to close the putty and keep it running on the VPS the bot shutdown. Here is what the directory looks like.
I think the problem is that when you start the app in the putty window, that process is linked to the window and gets terminated when that is closed.
To avoid that you can use a host service like screen, tmux, nohup, bg and so on...
If you want to know which is the best, try looking at this question from the askUbuntu Stack Exchange.
The key concept is that you open a new window using the tmux command (or screen, ...), then run your bot like you always do. When you want to leave but keep the process runing, you can detach the session with a key combination, that changes from service to service.
If you want to access that window again, you can run a command that will "restore" your session, like
tmux list-sessions
tmux attach-session -t 0
The NodeJS instance is terminated when putty is closed. You need something to keep the instance alive. Try:
PM2: http://pm2.keymetrics.io/
or,
Forever: https://github.com/foreverjs/forever#readme
Recommended though is to run the node instance as a service that can reboot on startup. Try looking at this:
https://stackoverflow.com/a/29042953/7739392
The shell runs in the foreground. This means any scripts you start there will end once you end your session. A simple solution would be to run your script in the background by adding the & after the call:
node index.js &
A better solution would be to create a service you can ask the service daemon to run for you. However, adding the & should get you what you want for now.
I recommend using one of these two node modules - ForeverJS or PM2. I'll show you how to quickly get started with ForeverJS but PM2 would be very similar.
You can easily install ForeverJS by typing the following in your terminal:
$ npm install forever -g
You may need to use SUDO depending on your user's privileges to get this working properly. It is NOT recommended to use it in production due to the security risks.
Once installed CD to your projects file directory and like you typed 'node index.js' you will do something similar with ForeverJS.
$ forever start index.js
Now when you exit the terminal your NodeJS application will remain as a running process.

Node's spawn() silently failing when called from a forever script scheduled on boot

This is kind of a doozy. This issue is most likely server related and so my first recourse was AskUbuntu over here.
I'm trying to have crontab or rc.local or init.d to start a forever script on boot. It attaches a server to a port I can ping with some information and have it run a headless browser for me.
That said, it seems that I'm unable to get a response from Node.js's spawn():
var CASPER_PATH = '/home/ubuntu/dev/casperjs/bin/casperjs'; // actual binary location, not a symlink
var SCRIPTS_PATH = '/home/custom_user/endpoints/server.js';
var fileName = req.body.source + '_' + req.body.type + '.coffee'; // looks like: mysource_my_scrape_type.coffee
var scrapeId = 'test_scrape';
var user = 'user123';
var pass = 'pass123';
if (fs.existsSync(SCRIPTS_PATH + fileName)) {
// If file is in place, spawn casperjs
var sP = spawn(CASPER_PATH,
[SCRIPTS_PATH + fileName, '--ssl-protocol=any', '--user='+user, '--scrapeId='+scrapeId, '--pass='+pass],
{ detached: true },
function (err, stdout, stderr) {});
sP.stdout.on('data', function(data) { console.log('stdout', data.toString('utf8')); });
sP.stderr.on('data', function(data) { console.log('stderr', data.toString('utf8')); });
sP.stdout.on('close', function(code) { console.log('close', code); });
res.send({ scheduled: true, key: scrapeId });
} else {
res.send({ scheduled: false, error: 'Incorrect source, type or the script is missing.' });
}
Before I added the PHANTOMJS_EXECUTABLE env to crontab or rc.local (doesnt seem to matter no matter the user level), stdout was useful:
stdout Fatal: [Errno 2] No such file or directory; did you install
phantomjs?
close false
Now that the environment var is there, there is no output at all after spawn().
Mind you, Casper starts up just fine if a user (of any privilege level) runs node/forever from bash.
How can I see why spawn() is failing?
This actually looks like a combo-bug between forever, spawn and casperjs (maybe phantomjs).
I was able to reproduce your problem, here is the full code of my test application.
You didn't show the full code, so my guess is that you have an express application and there is a special URL to run the casperjs script.
I build a simple app like this and it behaved this way:
Just start app with node script.js (script.js is the express app which runs the casperjs script in server.js) - it works OK, renders response and writes output from the child process event handlers to console
Start app as root with init.d script - doesn't work, once the child is spawned, no event handlers are triggered
Start app as root with init.d script, replace casperjs with echo - the same, doesn't work (see, here we have this problem with just forever running as root, spawn and echo)
Start app as a regular user (not root) with init.d, replace casperjs with 'echo' - it works, event handlers are triggered, here I was almost sure the issue is solved, but ... :(
Start app as a regular user (not root) with init.d, put back casperjs - it doesn't work again, event handlers are not triggered
The practical solution to this it to use pm2, I did this:
# install pm2
sudo npm install -g pm2
# generate init.d scripts for pm2
# this command will fail, but hint about the correct format with sudo
pm2 startup ubuntu
# do this in the folder with your application
pm2 start script.js
# remember your application
pm2 save
# also useful
# sudo service stop/start/restart pm2
# pm2 stop/start/restart script
Now pm2 will start automatically with the system and it will launch your application. Everything works, child process event handlers are triggered.
I did not understand your requirement completely. But i do have a similar situation with Ubuntu headless server.
what i am trying to do here is what i did
First, How is my crontab ?
crontab -u USER -e
#reboot exec sudo -u USER /bin/bash /home/USER/SHELL_SCRIPT.sh
See, here i am actually starting a shell script, and not a node server
Now inside this shell script(SHELL_SCRIPT.sh)
#! /bin/bash
# SHELL_SCRIPT.sh
cd /home/USER/
/home/USER/.npm-packages/bin/forever start -p /home/USER -a -d --watch false --pidFile /home/USER/forever.pid -l /home/USER/forever.log -o /home/USER/forever.out -e /home/USER/forever.err /home/USER/MY_NODE.js
and even inside my MY_NODE.js i follow absolute path, i just ignore $PATH, and don't use that.
Inside this node server, I do 100's of spawn
Now, i did this around 2 years back, so if you ask me why do this way, which i cannot answer

How to access chromedriver logs for Protractor test

I have seen that chromedriver can output a logfile (https://sites.google.com/a/chromium.org/chromedriver/logging)
This page shows how to set this up when executing the exe directly:
chromedriver.exe --verbose --log-path=chromedriver.log
I cannot figure out how to set this up in Protractor however
My current protractor.conf.js
require('babel/register');
exports.config = {
framework: 'jasmine2',
seleniumServerJar: './node_modules/protractor/selenium/selenium-server-standalone-2.45.0.jar'
};
From #alecxe's answer below and protractor's browser setup docs I tried adding the following (with and without --s) but with no apparent effect:
capabilities: {
browserName: "chrome",
chromeOptions: {
args: [
"--verbose",
"--log-path=chromedriver.log"
]
}
}
I also tried specifying an absolute path (log-path=/chromedriver.log) which also didn't work.
You can always start up your own instance of chromedriver in a separate process and tell Protractor to connect to that. For example, if you start chromedriver with:
chromedriver --port=9515 --verbose --log-path=chromedriver.log
Then you could use a configuration file for Protractor like so:
exports.config = {
seleniumAddress: 'http://localhost:9515',
capabilities: {
'browserName': 'chrome'
},
specs: ['example_spec.js'],
};
We use a shell script to add chromedriver logging, among other checks. You can then point protractor at the shell script:
protractor config:
// When running chromedriver, use this script:
chromeDriver: path.resolve(topdir, 'bin/protractor-chromedriver.sh'),
bin/protractor-chromedriver.sh
TMPDIR="/tmp"
NODE_MODULES="$(dirname $0)/../node_modules"
CHROMEDRIVER="${NODE_MODULES}/protractor/selenium/chromedriver"
LOG="${TMPDIR}/chromedriver.$$.log"
fatal() {
# Dump to stderr because that seems reasonable
echo >&2 "$0: ERROR: $*"
# Dump to a logfile because webdriver redirects stderr to /dev/null (?!)
echo >"${LOG}" "$0: ERROR: $*"
exit 11
}
[ ! -x "$CHROMEDRIVER" ] && fatal "Cannot find chromedriver: $CHROMEDRIVER"
exec "${CHROMEDRIVER}" --verbose --log-path="${LOG}" "$#"
According to the protractor's source code, chromedriver service is started without any arguments and there is no direct way to configure the arguments. Even though the chromedriver's Service Builder that protractor uses actually has an ability to specify the verbosity and the log path:
var service = new chrome.ServiceBuilder()
.loggingTo('/my/log/file.txt')
.enableVerboseLogging()
.build();
Old (incorrect) answer:
You need to set the chrome arguments:
capabilities: {
browserName: "chrome",
chromeOptions: {
args: [
"verbose",
"log-path=chromedriver.log"
]
}
},
See also:
Viewing outstanding requests
Since, the previous answer by #P.T. didn't work for me on Windows 7, I started with his suggestions and got it working on Windows. Here is a working solution for Windows 7 users.
STEP 1: Install BASH and JQ and confirm they are working on your Windows box
Download bash (for Windows 10
https://itsfoss.com/install-bash-on-windows/ ; for Windows 7
download latest here:
https://sourceforge.net/projects/win-bash/files/shell-complete/latest/ ; for Windows Server 2012 or any Windows OS that already has Git installed on it, you already have a bash.exe and sh.exe installed at C:\Program Files\Git\usr\bin or C:\Program Files (x86)\Git\usr\bin already
)
Install bash - For Windows 7/ download it and extract the zip files to a directory.
Download jq (https://stedolan.github.io/jq/) and install it in the same directory location as bash
Make SURE that you add your above directory (for Windows 7- where you extracted the bash zip files to; for other applicable OSes that have git, the path it is installed at) to your PATH system environment variable.
Once the above is installed and added to your PATH, close ALL and reopen Webstorm and any CMD windows you wish to run your work in.
Test that bash is actually installed by simply typing it on a windows command prompt
C:\git\> bash .
Doing so should produce a bash cmd prompt like this
bash$
STEP 2: Add Custom Files for Redirecting Chromedriver to user Debug Logging
Add the following files to the top level of the project (wherever your protractor-conf.js is located). These files allow us to add custom debug switches to the chromedriver.exe execution.
Note that this is necessary because these switches are not exposed through protractor and cannot be done directly in the protractor.conf.js file via the chromeOptions/args flags as you would normally expect
chromedriver.cmd -- exact source shown below:
bash protractor-chromedriver.sh %*
protractor-chromedriver.sh -- exact source shown below:
TMPDIR="$(dirname $0)/tmp"
NODE_MODULES="$(dirname $0)/node_modules"
SELENIUM="${NODE_MODULES}/protractor/node_modules/webdriver-manager/selenium"
UPDATECONFIG="${SELENIUM}/update-config.json"
EXEFILENAME="$(cat ${UPDATECONFIG} | jq .chrome.last | tr -d '""')"
CHROMEDRIVER="${SELENIUM}/${EXEFILENAME##*'\\'}"
LOG="${TMPDIR}/chromedriver.$$.log"
fatal() {
# Dump to stderr because that seems reasonable
echo >&2 "$0: ERROR: $*"
# Dump to a logfile because webdriver redirects stderr to /dev/null (?!)
echo >"${LOG}" "$0: ERROR: $*"
exit 11
}
[ ! -x "$CHROMEDRIVER" ] && fatal "Cannot find chromedriver: $CHROMEDRIVER"
exec "${CHROMEDRIVER}" --verbose --log-path="${LOG}" "$#"
/tmp -- create this directory at the top level of your project (same as the location of the protractor.conf.js file.
STEP 3: Update protractor.conf.js file.
In the protractor.conf.js file, add the following line as a property in the exports.config object. As in:
exports.config = {
.. ..
chromeDriver: 'chromedriver.cmd',
.. ..
STEP 4: Launch your tests
your test should now run and if the chrome driver outputs any log information it will appear in a file called chromedriver.???.log in the tmp directory under your project.
Important caveats
This script set up assumes you install and run protractor (and the chrome driver under it) within the local node_modules directory inside your project. That is how I run my code, because I want it complete self-contained and re-generated in the build process/cycle. If you have protractor/chromedriver installed globally you should change the CHROMEDRIVER variable within the protractor-chromedriver.sh file to match your installation of protractor/chrome driver.
hope that helps.
If you're using the seleniumServerJar, in protractor.conf.js set the logfile path to wherever you want it to write the file:
seleniumArgs: [
'-Dwebdriver.chrome.logfile=/home/myUsername/tmp/chromedriver.log',
]
If you're using webdriver-manager start to run a local selenium server, you'll need to edit the webdriver-manager file:
// insert this line
args.push('-Dwebdriver.chrome.logfile=/home/myUsername/tmp/chromedriver.log');
// this line already exists in webdriver-manager, add the push to args before this line
var seleniumProcess = spawnCommand('java', args);
In case you use webdriver-manager: webdriver manager has the chrome_logs option (you can find it in its source code (in opts.ts or opts.js in the compiled code)), so you can use it something like:
webdriver-manager start --chrome_logs /path/to/logfile.txt
I'm using this as a global afterEach hook (mocha):
afterEach(() => {
browser.manage().logs().get('browser').then(function(browserLog) {
if(browserLog && browserLog.length) {
console.log('\nlog: ' + util.inspect(browserLog) + '\n');
}
});
});

Categories