4

POSIX systems expose family of exec functions, that allow one to load something maybe different into current process, keeping open file descriptors, process identifier and so on.

This can be done for variety of reasons, and in my case this is bootstrapping — I want to change command line options of my own process, and then reload it over existing process, so there would be no child process.

Unfortunately, to much of my surprise, I could not find the way to call any of exec* functions in Node.js. So, what is the correct way to replace currently running Node.js process with other image?

toriningen
  • 7,196
  • 3
  • 46
  • 68
  • Consider this question: http://stackoverflow.com/questions/4018154/node-js-as-a-background-service – clay Dec 15 '15 at 14:57
  • Consider this https://nodejs.org/api/child_process.html – mkinawy Dec 15 '15 at 15:54
  • @mkinawy child_process does not overwrite existing process. It spawns a new one, and it will have new PID. – toriningen Dec 15 '15 at 16:56
  • @clay I don't see how is it related. – toriningen Dec 15 '15 at 16:57
  • What are you trying to do, that you need to overwrite the exact process again? – clay Dec 15 '15 at 18:45
  • @clay, (a) bootstrapping. `node` started with no options, I need to add options like `--expose_gc_as=V8GC` or `--harmony`, (b) or I just want to open some files and leave their descriptors for the next application that I am going to overload, (c) or I am going through daemonization step and want to `fork()` (btw, there is no way to call `fork()` either), (d) or anything else people use `exec` for in other languages and platforms. – toriningen Dec 16 '15 at 10:06
  • Cool stuff. Never needed that type of functionality in Nodejs. Should you find an answer, be sure to post an answer back here. – clay Dec 16 '15 at 17:26
  • @toriningen have you found a solution? – Oleksii Rudenko Jun 28 '17 at 19:18
  • @OleksiiRudenko, yes. I have used [`ffi`](https://github.com/node-ffi/node-ffi) module, and exposed `execvp` to the process. Please keep in mind `fork` won't work, because V8 has threads, and `fork()` syscall only copies main thread, so forked interpreter crashes immediately. – toriningen Jun 28 '17 at 20:22
  • I see. I didn't find a ready solution myself so I created this module https://github.com/OrKoN/native-exec to invoke `execvp`. It's without ffi, compiled as a native node addon instead. – Oleksii Rudenko Jun 29 '17 at 19:18
  • 1
    @OleksiiRudenko please make this into an answer, I will accept it. – toriningen Jun 29 '17 at 19:51

3 Answers3

3

I have created a module to invoke execvp function from NodeJS: https://github.com/OrKoN/native-exec

It works like this:

var exec = require('native-exec');

exec('ls', {
  newEnvKey: newEnvValue,
}, '-lsa'); // => the process is replaced with ls, which runs and exits

Since it's a native node addon it requires a C++ compiler installed. Works fine in Docker, on Mac OS and Linux. Probably, does not work on Windows. Tested with node 6, 7 and 8.

Oleksii Rudenko
  • 1,277
  • 12
  • 23
3

Here is an example using node-ffi that works with node v10. (alas, not v12)

#!/usr/bin/node

"use strict";

const ffi = require('ffi');
const ref = require('ref');
const ArrayType = require('ref-array');
const stringAry = ArrayType('string');

const readline = require("readline");
const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout
});

rl.question('Login: ', (username) => {
    username = username.replace(/[^a-z0-9_]/g, "");
    rl.close();
    execvp("/usr/bin/ssh", "-e", "none", username+'@localhost');
});



function execvp() {
    var current = ffi.Library(null, 
                              { execvp: ['int', ['string',
                                                 stringAry]],
                                dup2: ['int', ['int', 'int']]});
    current.dup2(process.stdin._handle.fd, 0);
    current.dup2(process.stdout._handle.fd, 1);
    current.dup2(process.stderr._handle.fd, 2);
    var ret = current.execvp(arguments[0], Array.prototype.slice.call(arguments).concat([ref.NULL]));    
}
user1629060
  • 566
  • 6
  • 6
2

I ended up using ffi module, and exported execvp from libc.

toriningen
  • 7,196
  • 3
  • 46
  • 68