Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Node.js readline with interactive child_process spawning

I'm working on simple CLI, one of it's function is to run ssh to remote servers. This is an example of what I mean:

#!/usr/bin/env node

var spawn = require('child_process').spawn;
var readline = require('readline');

var iface =readline.createInterface({
  input: process.stdin,
  output: process.stdout
});
iface.setPrompt("host> ");

iface.on("line", function(line) {
  if (line.trim() == "") {
    return iface.prompt();
  }
  var host = line.split()[0];
  iface.pause(); // PAUSING STDIN!
  var proc = spawn('ssh', [ host, '-l', 'root', '-o', 'ConnectTimeout=4' ], { stdio: [0,1,2] });
  proc.on("exit", function(code, signal) {
    iface.prompt();
  });
});
iface.prompt();

I use iface.pause() to make child process able to read from STDIN exclusively, if I remove this line, some symbols will be catched by remote server, another ones - by local readline. But this pause() really freezes stdin so if ssh waits for connect too long or trying to ask me for password, I can't send any character because stdin is paused. I don't really know how ssh handles (paused?) stdin after successful connect but somehow it works. The question is "How to detach stdin from readline without pausing it while interactive child process is working and to attach it back after child process is finished?"

like image 732
Pavel Vorobyov Avatar asked Sep 14 '25 08:09

Pavel Vorobyov


1 Answers

The problem was solved by adding setRawMode(false) right before spawning process. As far as I understand, this makes node release stdin from any transformations it makes to handle keypresses of functional keys. Solved version of code follows:

var spawn = require('child_process').spawn;
var readline = require('readline');

var iface =readline.createInterface({
  input: process.stdin,
  output: process.stdout
});
iface.setPrompt("host> ");

iface.on("line", function(line) {
  if (line.trim() == "") {
    return iface.prompt();
  }
  var host = line.split()[0];
  iface.pause(); // PAUSING STDIN!
  process.stdin.setRawMode(false); // Releasing stdin
  var proc = spawn('ssh', [ host, '-l', 'root', '-o', 'ConnectTimeout=4' ], { stdio: [0,1,2] });
  proc.on("exit", function(code, signal) {
    // Don't forget to switch pseudo terminal on again
    process.stdin.setRawMode(true); 
    iface.prompt();
  });
});
like image 166
Pavel Vorobyov Avatar answered Sep 15 '25 23:09

Pavel Vorobyov