2

I'd like to set the PR_SET_CHILD_SUBREAPER process flag for the Bash process running my script, so that I can reap the tree of child processes that gets created (and can get killed in a non-orderly way) during its lifetime. Basically, I'd like Bash to call prctl(PR_SET_CHILD_SUBREAPER, 1, 0, 0, 0); on itself, but I haven't found a way to achieve that. Any suggestion?

I'd be fine even with dodgy solutions that involve invoking libc.so.6 prctl() directly (a-la Python ctypes) using Bash builtins, if any exist.

2 Answers2

1

Since you mention it's fine with you, I feel obliged to introduce bash's equivalent...

ctypes.sh, a foreign function interface for bash

It's a shared object plugin for bash that is loaded with bash's enable -f mechanism:

enable [-a] [-dnps] [-f filename] [name ...]

The -f option means to load the new builtin command name from shared object filename, on systems that support dynamic loading.

and implemented in C language. It works at least on most Linux distributions and on FreeBSD.

You'll have to compile and install it first. The main feature is the ability to use almost any library call or system call from the shell. Though calls requiring structures might become way more complex to use when the builtin struct fails to reconstruct them automatically.

Example typed in current bash shell, on amd64 (x86_64) architecture and Linux kernel 5.6 (in some cases constants depend on architecture and (more uncommonly) kernel version):

$ source /usr/local/bin/ctypes.sh
$ dlcall -r int prctl int:36 ulong:1 ulong:0 ulong:0 ulong:0
int:0
$ echo $DLRETVAL # you can't use $() above to get the result since that would be a subshell
int:0
$ echo $$; bash -c 'echo $$; sleep 99 & echo $!; disown -a'
14767
16761
16762
$ pstree -p $$
bash(14767)─┬─pstree(16778)
            └─sleep(16762)

The sleep process having lost its parent process (bash pid 16761), has been inherited by the current shell instead of the init process: it worked.

Note that PR_SET_CHILD_SUBREAPER had to be replaced by its value (and type) as found in /usr/include/linux/prctl.h on this system:

#define PR_SET_CHILD_SUBREAPER    36

You'll have to check the documentation to use it properly.

Also, the shell's standard wait might not work as expected for this: the shell didn't spawn that sleep command so the wait command won't do anything. You might have to invest into dlcalling wait(), waitpid() & co. This could be difficult, because bash itself alters settings and uses wait()-like calls each time it runs a command, so some unforeseen interactions to handle those inherited processes are likely.


Using gdb

This would achieve the same result as before (there must be some options to get it less verbose):

$ gdb -ex 'call (int)prctl((int)36,(long)1,(long)0,(long)0,(long)0)' -ex detach -ex quit -p $$
A.B
  • 36,364
  • 2
  • 73
  • 118
  • An other method would be to use gdb from the shell to inject a prctl() call to itself. Nothing to compile anymore – A.B May 27 '20 at 18:55
  • Something like: gdb --pid $$ -ex 'call prctl(36, 1, 0, 0, 0, 0)' -ex 'quit $1'? I'll test it and report, I actually like it more as it doesn't involve installing anything (which could be tricky in the environment I work in). – Giuseppe Musumeci May 27 '20 at 19:01
  • I was adding it. Yes. Just in case I specified all types. – A.B May 27 '20 at 19:04
  • Anyway my remark at the end of ctypes.sh stands true: attempting to handle processes from bash commands (somehow...) might interact with bash's own handling of them which happens at least for each external command run. – A.B May 27 '20 at 19:05
  • The gdb solution works like a charm and I accepted it as it just works out the box. Thanks! – Giuseppe Musumeci May 27 '20 at 19:08
0

Non-C-programmers can use some simple chain-loading tools. I wrote one such for the nosh toolset.

  • For a script invoked as sh ./wibble (or some other shell) simply run
    local-reaper true sh ./wibble
  • For a script invoked as ./wibble simply run
    local-reaper true ./wibble

This tool works the same on FreeBSD. It hides all of the system call differences.

Further reading

JdeBP
  • 68,745
  • The problem with a local-reaper process solution is that it doesn't scale well, as one more process has to stay alive to deal with the reaping (in my case I have a swarm of thousands process trees per machine, rooted in the Bash script). This is why I was specifically asking how to prctl the very Bash process running the root script. – Giuseppe Musumeci May 28 '20 at 10:34