In order to use bashcov
in one of my projects, I need a way to pass on all file descriptors from node.js to a child process.
I have created these test programs:
$ head -n20 m.sh r.js s.sh
==> m.sh <==
#! /usr/bin/env bash
set -e
exec 3<>messages
echo see when we can see the file descriptor we created
echo from main bash program:
ls -l /proc/$$/fd/
echo
echo from bash subprogram called directly:
./s.sh
echo
node r.js ./s.sh
exec 3>&-
==> r.js <==
const run = function(cmd) {
return require('child_process').spawnSync(cmd[0], cmd.slice(1), {
stdio: 'inherit',
});
}
console.log('from javascript program:');
run(['ls', '-l', `/proc/${process.pid}/fd/`]);
console.log('\nfrom bash subprogram called from js:');
run(process.argv.slice(2))
==> s.sh <==
#! /usr/bin/env bash
set -e
ls -l /proc/$$/fd/
The main program m.sh
list all of its file descriptors, and then call a bash program that I have referred to as "subprogram" s.sh
which also lists all of its fds. Then I call a js program r.js
which lists its available fds, and then calls s.sh
again using the child_process
lib.
This is the result:
$ ./m.sh
see when we can see the file descriptor we created
from main bash program:
total 0
lrwx------ 1 me me 64 Apr 16 12:16 0 -> /dev/pts/2
l-wx------ 1 me me 64 Apr 16 12:16 1 -> pipe:[256648]
lrwx------ 1 me me 64 Apr 16 12:16 2 -> /dev/pts/2
lr-x------ 1 me me 64 Apr 16 12:16 255 -> /home/me/bashcov/m.sh
lrwx------ 1 me me 64 Apr 16 12:16 3 -> /home/me/bashcov/messages
from bash subprogram called directly:
total 0
lrwx------ 1 me me 64 Apr 16 12:16 0 -> /dev/pts/2
l-wx------ 1 me me 64 Apr 16 12:16 1 -> pipe:[256648]
lrwx------ 1 me me 64 Apr 16 12:16 2 -> /dev/pts/2
lr-x------ 1 me me 64 Apr 16 12:16 255 -> /home/me/bashcov/s.sh
lrwx------ 1 me me 64 Apr 16 12:16 3 -> /home/me/bashcov/messages
from javascript program:
total 0
lrwx------ 1 me me 64 Apr 16 12:16 0 -> /dev/pts/2
l-wx------ 1 me me 64 Apr 16 12:16 1 -> pipe:[256648]
lrwx------ 1 me me 64 Apr 16 12:16 10 -> anon_inode:[eventpoll]
lr-x------ 1 me me 64 Apr 16 12:16 11 -> pipe:[256661]
l-wx------ 1 me me 64 Apr 16 12:16 12 -> pipe:[256661]
lrwx------ 1 me me 64 Apr 16 12:16 13 -> anon_inode:[eventfd]
lrwx------ 1 me me 64 Apr 16 12:16 14 -> anon_inode:[eventpoll]
lr-x------ 1 me me 64 Apr 16 12:16 15 -> pipe:[258179]
l-wx------ 1 me me 64 Apr 16 12:16 16 -> pipe:[258179]
lrwx------ 1 me me 64 Apr 16 12:16 17 -> anon_inode:[eventfd]
lr-x------ 1 me me 64 Apr 16 12:16 18 -> /dev/null
lrwx------ 1 me me 64 Apr 16 12:16 19 -> anon_inode:[eventpoll]
lrwx------ 1 me me 64 Apr 16 12:16 2 -> /dev/pts/2
lr-x------ 1 me me 64 Apr 16 12:16 20 -> pipe:[258182]
l-wx------ 1 me me 64 Apr 16 12:16 21 -> pipe:[258182]
lrwx------ 1 me me 64 Apr 16 12:16 22 -> anon_inode:[eventfd]
lrwx------ 1 me me 64 Apr 16 12:16 3 -> /home/me/bashcov/messages
lrwx------ 1 me me 64 Apr 16 12:16 4 -> anon_inode:[eventpoll]
lr-x------ 1 me me 64 Apr 16 12:16 5 -> pipe:[258177]
l-wx------ 1 me me 64 Apr 16 12:16 6 -> pipe:[258177]
lr-x------ 1 me me 64 Apr 16 12:16 7 -> pipe:[258178]
l-wx------ 1 me me 64 Apr 16 12:16 8 -> pipe:[258178]
lrwx------ 1 me me 64 Apr 16 12:16 9 -> anon_inode:[eventfd]
from bash subprogram called from js:
total 0
lrwx------ 1 me me 64 Apr 16 12:16 0 -> /dev/pts/2
l-wx------ 1 me me 64 Apr 16 12:16 1 -> pipe:[256648]
lrwx------ 1 me me 64 Apr 16 12:16 2 -> /dev/pts/2
lr-x------ 1 me me 64 Apr 16 12:16 255 -> /home/me/bashcov/s.sh
You can see that inside the s.sh
when it is called from node.js, it does not have access to file descriptor 3
which I have created. bash passes it on to the bash program it calls, and the node program it calls, but node does not.
I have just run into this issue with python (bashcov - does not work if subprogram is called by python) and the solution turned out to be to add the param close_fds=False
. (os.system() vs. os.popen() when using bash process substitution: ls: cannot access '/dev/fd/63': No such file or directory)
If I replace the js program in this experiment with a ruby or make program, it works fine.
Is there a way to tell node.js's child_process
to pass all the open file descriptors on the the child_process?
Replace stdio: 'inherit'
with stdio: [0, 1, 2, 3]
. 'inherit'
is shorthand for [0, 1, 2]
, per the documentation, so if you want FD 3 to be passed too, you need to add it manually. I don't see a way to tell it to inherit every FD other than manually creating an array with all of their numbers.