Description
sometimes we want to execute elevated JavaScript script instead of external process.
if there is something like child_process.fork() call, it will be great.
documents about node.js native fork and ipc:
https://nodejs.org/api/child_process.html#child_process_child_process_fork_modulepath_args_options
note that spawn support IPC too, just specific it in stdio
https://nodejs.org/api/child_process.html#child_process_options_stdio
and in child process
https://nodejs.org/api/process.html#process_process_send_message_sendhandle_options_callback
https://nodejs.org/api/process.html#process_event_message
I've done something to simulate this ipc. it's ugly and not robust, but works for me.
it will break normal stdin/stdout since I'm using this to transfer messages. I don't know what does node.js native child_process ipc implement, maybe unixsock or named pipe?
here is my code:
import {remote} from "electron";
const Sudo = require('electron-sudo').default;
Sudo.prototype.fork = async function (modulePath, args, options) {
let child = await this.spawn(remote.app.getPath('exe'), ['-e', modulePath].concat(args), options);
readline.createInterface({input: child.stdout}).on('line', (line) => {
child.emit('message', JSON.parse(line));
});
child.send = (message, sendHandle, options, callback)=> {
child.stdin.write(JSON.stringify(message) + os.EOL);
if (callback) {
callback()
}
};
return child
};
in index.js
function handleElevate() {
if (process.argv[1] == '-e') {
app.dock.hide();
const os = require('os');
const readline = require('readline');
process.send = (message, sendHandle, options, callback)=> process.stdout.write(JSON.stringify(message) + os.EOL, callback);
process.stdin.on('end', ()=> process.emit('disconnect'));
readline.createInterface({input: process.stdin}).on('line', (line) => process.emit('message', JSON.parse(line)));
require("./" + process.argv[2]);
return true;
}
}
if (handleElevate()) {
return;
}
in my elevated module maotama.js
, I'm using this to send raw-socket
const raw = require("raw-socket");
let socket = raw.createSocket({protocol: raw.Protocol.UDP});
let connect = (local_port, remote_port, remote_address)=> {
let buffer = new Buffer(9);
buffer.writeUInt16BE(local_port, 0);
buffer.writeUInt16BE(remote_port, 2);
buffer.writeUInt16BE(buffer.length, 4);
socket.send(buffer, 0, buffer.length, remote_address, (error, bytes) => {
if (error) {
throw(error);
}
})
};
process.on('message', (message)=> {
connect(...message.arguments);
});
process.on('disconnect', process.exit);
and calling
let child = await new Sudo({name: 'MyCard'}).fork('maotama')
child.send({
action: 'connect',
arguments: [app.network.port, port, address]
})