All files index.js

100% Statements 135/135
100% Branches 2/2
100% Functions 1/1
100% Lines 135/135

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 1361x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x  
/**
# cp-remote   [![Build Status](https://github.com/davedoesdev/cp-remote/actions/workflows/ci.yml/badge.svg)](https://github.com/davedoesdev/cp-remote/actions/workflows/ci.yml) [![Coverage Status](https://coveralls.io/repos/davedoesdev/cp-remote/badge.png?branch=master)](https://coveralls.io/r/davedoesdev/cp-remote?branch=master) [![NPM version](https://badge.fury.io/js/cp-remote.png)](http://badge.fury.io/js/cp-remote)
 
Node.js [`child_process`](http://nodejs.org/api/child_process.html) done remotely, IPC channel intact!
 
Example:
 
```javascript
var cp_remote = require('cp-remote');
var assert = require('assert');
var remote = cp_remote.run('host', '/path/on/host/to/sub.js', 'foo', { answer: 42 });
remote.on('message', function (msg)
{
    assert.deepEqual(msg, { foo: 'bar' });
});
remote.send({ hello: 'world' });
```
 
You might implement the remote script, `sub.js`, like this:
 
```javascript
var assert = require('assert');
assert.equal(process.argv[2], 'foo')
assert.deepEqual(process.argv[3], { answer: 42 });
process.on('message', function (msg)
{
    assert.deepEqual(msg, { hello: 'world' });
    process.disconnect();
});
process.send({ foo: 'bar' });
```
 
The API is described [here](#tableofcontents).
 
## Pre-requisites
 
Client:
 
- SSH client (e.g. [OpenSSH](http://www.openssh.com)), configured for password-less logon to the remote host (e.g. using a private key).
- [Node.js](http://www.nodejs.org) (of course)
- [Bash](https://www.gnu.org/software/bash/bash.html)
- [socat](http://www.dest-unreach.org/socat/)
 
Remote host:
 
- SSH server (e.g. [OpenSSH](http://www.openssh.com))
- Node.js (`node` command should be in the remote `PATH` of SSH sessions)
- [Python](http://www.python.org) (it provides access to [`socketpair`](http://pubs.opengroup.org/onlinepubs/009695399/functions/socketpair.html), Node does not)
- [socat](http://www.dest-unreach.org/socat/)
 
## Installation
 
```shell
npm install cp-remote
```
 
## Limitation
 
You can't pass handles to a remote child process like you can with local child processes.
 
## How it works
 
![How it works](http://rawgit.davedoesdev.com/davedoesdev/cp-remote/master/diagrams/how_it_works.svg)
 
- `cp-remote` calls [`child_process.spawn`](http://nodejs.org/api/child_process.html#child_process_child_process_spawn_command_args_options) to run a Bash script, [`cp-remote.sh`](cp-remote.sh). The IPC channel will be on `$NODE_CHANNEL_FD`.
- `cp-remote.sh` runs `socat`, telling it to relay data between `$NODE_CHANNEL_FD` and an SSH connection to the remote host.
- The SSH connection is told to start [`cp-remote.py`](cp-remote.py) on the remote host.
- `cp-remote.py` calls [`socket.socketpair`](http://docs.python.org/3/library/socket.html#socket.socketpair) to create a pair of connected file descriptors (Unix domain sockets).
- `cp-remote.py` starts `socat`, telling it to relay data between standard input (i.e. the SSH connection) and one of the connected file descriptors.
- `cp-remote.py` sets `NODE_CHANNEL_FD` to the other connected file descriptor and starts `node`, telling it to run the module you specified.
 
## Licence
 
[MIT](LICENCE)
 
## Test
 
To test creating and communicating with remote child processes:
 
```shell
grunt test --remote=<host1> --remote=<host2> ...
```
 
You can specify as many remote hosts as you like. The test will try to create a remote child process on each host and then communicate with each one.
 
It assumes the `cp-remote` module is installed at the same path on each host.
 
## Lint
 
```shell
grunt lint
```
 
## Code Coverage
 
```shell
grunt coverage --remote=<host1> --remote=<host2> ...
```
 
[c8](https://github.com/bcoe/c8) results are available [here](http://rawgit.davedoesdev.com/davedoesdev/cp-remote/master/coverage/lcov-report/index.html).
 
Coveralls page is [here](https://coveralls.io/r/davedoesdev/cp-remote).
 
# API
*/
 
/*jslint node: true, nomen: true */
"use strict";
 
var child_process = require('child_process'),
    path = require('path'),
    packed = require('./packed');
 
/**
Run a Node.js module on a remote host and return a [`child_process.ChildProcess`](http://nodejs.org/api/child_process.html#child_process_class_childprocess) object for communication with it.
 
@param {String} host The name (or IP address) of the remote host to run the module on.
@param {String} module_path The path to the module _on the remote host_. Any arguments following `module_path` will be made available to the module in its `process.argv` (starting at the third element).
@return {child_process.ChildProcess} The `ChildProcess` object for the remote process. You can do the same things with this object as a local `ChildProcess`, except send it handles (i.e. the optional `sendHandle` parameter to [`child.send`](http://nodejs.org/api/child_process.html#child_process_child_send_message_sendhandle) isn't supported).
*/
function run(host, module_path)
{
    var child = child_process.spawn(
            path.join(__dirname, 'cp-remote.sh'),
            [host, module_path],
            { stdio: [0, 1, 2, 'ipc'] });
 
    child.send(packed.toString());
    child.send(Buffer.from(JSON.stringify(
        Array.prototype.slice.call(arguments, 2))).toString('hex'));
 
    return child;
}
 
exports.run = run;