Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions examples/interact-via-stdin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,15 @@ Then to attach to it:
```
$ pm2 attach 0
```

Or:

```
$ pm2 logs --attach-input
```

Then send a message (e.g., *Lorem Ipsum*) to the app:

```
> 0 Lorem Ipsum
```
132 changes: 91 additions & 41 deletions lib/API/Extra.js
Original file line number Diff line number Diff line change
Expand Up @@ -321,74 +321,95 @@ module.exports = function(CLI) {
}

/**
* Description
* Send a line to stdin of a process
* @method sendLineToStdin
*/
CLI.prototype.sendLineToStdin = function(pm_id, line, separator, cb) {
CLI.prototype.sendLineToStdin = function(id, line, separator, cb) {
var that = this;

if (!cb && typeof(separator) == 'function') {
cb = separator;
separator = null;
}

var packet = {
pm_id : pm_id,
line : line + (separator || '\n')
};
function handleError(err) {
Common.printError(cst.PREFIX_MSG_ERR + err);
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
}

that.Client.executeRemote('sendLineToStdin', packet, function(err, res) {
if (err) {
Common.printError(cst.PREFIX_MSG_ERR + err);
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
}
return cb ? cb(null, res) : that.speedList();
});
function sendLine(pm_id) {
var packet = {
pm_id : pm_id,
line : line + (separator || '\n')
};

that.Client.executeRemote('sendLineToStdin', packet, function(err, res) {
if (err) return handleError(err);
return cb ? cb(null, res) : that.speedList();
});
}

if (isNaN(id)) {
that.Client.getUniqueProcessIdByName(id, function(err, pm_id) {
if (err) return handleError(err);
sendLine(pm_id);
});
return;
}
sendLine(id);
};

/**
* Description
* @method attachToProcess
* Attach to a process stdin and stdout
* @method attach
*/
CLI.prototype.attach = function(pm_id, separator, cb) {
CLI.prototype.attach = function(id, separator, cb) {
var that = this;
var readline = require('readline');

if (isNaN(pm_id)) {
Common.printError('pm_id must be a process number (not a process name)');
return cb ? cb(Common.retErr('pm_id must be number')) : that.exitCli(cst.ERROR_EXIT);
}

if (typeof(separator) == 'function') {
cb = separator;
separator = null;
}

var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
function handleError(err) {
Common.printError(cst.PREFIX_MSG_ERR + err);
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
}

rl.on('close', function() {
return cb ? cb() : that.exitCli(cst.SUCCESS_EXIT);
});
function attachProcess(pm_id) {
var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});

that.Client.launchBus(function(err, bus, socket) {
if (err) {
Common.printError(err);
return cb ? cb(Common.retErr(err)) : that.exitCli(cst.ERROR_EXIT);
}
rl.on('close', function() {
return cb ? cb() : that.exitCli(cst.SUCCESS_EXIT);
});

that.Client.launchBus(function(err, bus, socket) {
if (err) return handleError(err);

bus.on('log:*', function(type, packet) {
if (packet.process.pm_id !== parseInt(pm_id))
return;
process.stdout.write(packet.data);
bus.on('log:*', function(type, packet) {
if (packet.process.pm_id !== parseInt(pm_id))
return;
process.stdout.write(packet.data);
});
});
});

rl.on('line', function(line) {
that.sendLineToStdin(pm_id, line, separator, function() {});
});
rl.on('line', function(line) {
that.sendLineToStdin(pm_id, line, separator, function() {});
});
}

if (isNaN(id)) {
that.Client.getUniqueProcessIdByName(id, function(err, pm_id) {
if (err) return handleError(err);
attachProcess(pm_id);
});
return;
}
attachProcess(id);
};

/**
Expand Down Expand Up @@ -772,4 +793,33 @@ module.exports = function(CLI) {
that.exitCli(cst.SUCCESS_EXIT);
});
};

/**
* Attach stdin to the client. You could use this to send commands to a process
*
* Input `<id|name> [line] ...` to send stdin to <id|name>
*
* Any error will exit the CLI
* @method attachInput
*/
CLI.prototype.attachInput = function() {
var that = this;
var readline = require('readline');

var rl = readline.createInterface({
input: process.stdin,
output: process.stdout
});
rl.on('close', function() {
that.exitCli();
});
rl.on('line', function(input) {
var line = input.split(' ');
that.sendLineToStdin(line.shift(), line.join(' '), function(err) {
if (err) {
that.exitCli();
}
});
});
};
};
13 changes: 13 additions & 0 deletions lib/Client.js
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,19 @@ Client.prototype.getProcessIdByName = function(name, force_all, cb) {
});
};

/**
* Get process id by name and check if only one process is hitted
* @param {string} name Name of the process to search for
* @param {(err: string, id: string) => void} cb Callback function to handle the result
*/
Client.prototype.getUniqueProcessIdByName = function(name, cb) {
this.getProcessIdByName(name, function(err, list) {
if (err) return cb(err);
if (list.length === 1) return cb(null, list[0]);
cb(`Expected exactly one process with name "${name}", but found ${list.length}.`);
});
};

Client.prototype.getProcessIdsByNamespace = function(namespace, force_all, cb) {
var found_proc = [];
var full_details = {};
Expand Down
17 changes: 11 additions & 6 deletions lib/binaries/CLI.js
Original file line number Diff line number Diff line change
Expand Up @@ -672,18 +672,18 @@ commander.command('cleardump')
//
// Save processes to file
//
commander.command('send <pm_id> <line>')
.description('send stdin to <pm_id>')
.action(function(pm_id, line) {
pm2.sendLineToStdin(pm_id, line);
commander.command('send <id|name> <line>')
.description('send stdin to <id|name>')
.action(function(id, line) {
pm2.sendLineToStdin(id, line);
});

//
// Attach to stdin/stdout
// Not TTY ready
//
commander.command('attach <pm_id> [command separator]')
.description('attach stdin/stdout to application identified by <pm_id>')
commander.command('attach <id|name> [command separator]')
.description('attach stdin/stdout to <id|name>')
.action(function(pm_id, separator) {
pm2.attach(pm_id, separator);
});
Expand Down Expand Up @@ -892,6 +892,7 @@ commander.command('logs [id|name|namespace]')
.option('--timestamp [format]', 'add timestamps (default format YYYY-MM-DD-HH:mm:ss)')
.option('--nostream', 'print logs without launching the log stream')
.option('--highlight [value]', 'highlights the given value')
.option('--attach-input', 'input `<id|name> [line] ...` to send stdin to <id|name>')
.description('stream logs file. Default stream all logs')
.action(function(id, cmd) {
var Logs = require('../API/Log.js');
Expand Down Expand Up @@ -931,6 +932,10 @@ commander.command('logs [id|name|namespace]')
Logs.formatStream(pm2.Client, id, false, 'YYYY-MM-DD-HH:mm:ssZZ', exclusive, highlight);
else
pm2.streamLogs(id, line, raw, timestamp, exclusive, highlight);

if (cmd.attachInput) {
pm2.attachInput();
}
});


Expand Down
41 changes: 41 additions & 0 deletions types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,47 @@ export function startup(platform: Platform, errback: ErrResultCallback): void;
*/
export function sendDataToProcessId(proc_id: number, packet: object, cb: ErrResultCallback): void;

/**
* Send a line to stdin of a process
* @param id id or name of the process
* @param line line to send to stdin
* @param cb if success, the id and the line will be returned
*/
export function sendLineToStdin(id: string, line: string, cb: ErrResultCallback): void

/**
* Send a line to stdin of a process
* @param id id or name of the process
* @param line line to send to stdin
* @param separator separator of the line, default is '\n'
* @param cb if success, the id and the line will be returned
*/
export function sendLineToStdin(id: string, line: string, separator: string, cb: ErrResultCallback): void

/**
* Attach to stdio of a process
* @param id id or name of the process
* @param cb if success, null will be returned
*/
export function attach(id: string, cb: ErrResultCallback): void

/**
* Attach to stdio of a process
* @param id id or name of the process
* @param separator separator of the line, default is '\n'
* @param cb if success, null will be returned
*/
export function attach(id: string, separator: string, cb: ErrResultCallback): void

/**
* Attach stdin to the client. You could use this to send commands to a process
*
* Input `<id|name> [line] ...` to send stdin to <id|name>
*
* Any error will exit the CLI
*/
export function attachInput(): void

// Interfaces

export interface Proc {
Expand Down