1. 8

I was wondering about how a shell script can’t really clean-up after itself. Certainly unprivileged one. This is (maybe silly) a request for comments in form of a hypothetical man page.

I am fully aware how little I know about kernel development, so take it with a grain of salt. I am willing to learn.

  1. [Comment removed by author]

    1. 4

      You don’t even need to kill everything in the namespace manually. Starting a process in a new PID namespace means that process acts like init in that namespace; killing it automatically kills everything in the namespace. You can use unshare(2) as a wrapper to run a process in a new PID namespace, to avoid writing new code.

      (I am in the middle of implementing a version of this at $DAYJOB to isolate tests from each other)

      1. 1

        However it would be nice to send SIGTERM first. Can such an “init” send signals to processes with different UID?

        Other than that it should be enough for most cases.

      2. 1

        Unix has had it a lot longer than since kernel 3.8: They’re called process groups.

        1. 1

          You said it yourself: setpgid. It can be called by descendant process and it’s no longer in parent’s process group. For session there is setsid.

          As stated earlier: if you have a bit more complicated script you will have processes that:

          • double fork
          • setpgid
          • setsid
          • ignore signals that can be ignored

          Then the only robust way to kill them all by unprivileged ancestor process seem to be pid namespaces on Linux.

          1. 1

            Sorry, I really misunderstand what you’re trying to do then.

            Why exactly do you want to kill -9 a process that you did not start and don’t know what it’s doing?

            1. 1

              I want to leave clean state after I finish. No background processes left-behind. I might have not started a certain process directly, but it is a descendant of my process. It would not be there if not for my process so in a way I did start it.

              I want to ask it politely first with SIGTERM and give it a bit time. But when it will not listen in reasonable time I want to have an option to SIGKILL it.

              System services should probably be spawned by a daemon that was started earlier by the init. Then whatever I’m doing can’t harm the system. If descendant of my process starts a system service it probably should not be touchable by my process (but it depends).

              1. 1

                I’ve been using unix for over 30 years; I’ve managed mail clusters handling 100mm emails a month, and ad servers handling 10bn impressions a day. I’ve never wanted this.

                I’d really suggest trying to figure out why you think you want it – what problem you’re actually trying to solve. A program that tries to create its' own process group either has a bug (that something like fghack or another hotpatch can fix) or has a good reason (in which case, are you sure you want to kill it?)

      3. 2

        The condemned title doesn’t make sense to me, but I like where you’re going with this.

        I’d mention that you should be able to hold on to PIDs of all first order child processes spawned from your shell script. Additionally, if you kill your shell script and none of the child processes were started with nohup, wouldn’t they die as well?

        1. 2

          The condemned title is a self deprecating joke made from Henry Spencer’s quote:

          Those who do not understand Unix are condemned to reinvent it, poorly.

          as I am not entirely confident that my proposition makes any sense.

          Problem is that most of the time if your script is complicated it will not be that easy. Processes can:

          • daemonize: double fork and parent quit
          • ignore signals, except SIGKILL
          • create a new process group
          • create a new session

          And it does not have to be a shell script. AFAIK it’s just a shell convention that shell sends SIGHUP to children spawned by it. Also your process can have children that it did not spawn - fork multiple children and exec in the parent. And going through /proc is racy.

          1. 1

            Yes you raise good points. Does any *nix track the lineage of a process? Can I trace every process back to PID 1 and know every mid point along the way?

            1. 1

              That won’t make sense as Unix can reuse PIDs.

        2. 1

          I think this is mostly covered by process groups. Take a look at what kill(-pid) does.

          1. 1

            As I said earlier: process can easily depart from process group or even session.

          2. 1

            This seems more like the desire for a transaction? “Run this and then roll everything back”. At the system level, are docker images (or other favourite container technology) sufficiently lightweight for your use case? “Run a bunch of processes and then ensure they’ve gone away”

            1. 1

              Docker has few problems. It needs a running daemon with higher privileges and a user that wants to interface with it has to be in group docker by default. It means that on ordinary installation that you may not have control over you can’t do anything until docker is installed. However it is heavy weight solution also, because AFAIK your script then has to be a container. Instead just a normal user program,

              I also don’t think that Docker will stay with us long. Syscall would stay with us.

              I’m seeking for a solution that you could use just by having a proper kernel. It’s just an unprivileged syscall. PID namespaces on Linux seem to work that way. And indeed there are something like a thin unprivileged Docker.

              I don’t necessarily want to have a full rollback. It would be good enough if a shell script could kill everything that it spawned. You may want to interface file-system or other processes so namespaces and containers would also not cover the use case entirely. However isolating only on processes is often useful and should be simple. Without being required to do some kind of manifest or Dockerfile dance.

              1. 2

                I guess I was wondering about the surrounding motivation/use case for “kill all child processes”.

                It sounds like it may be related to having a repeatable, controlled environment for a test setup or similar, which made me think that there may be related to also doing things like controlling the system configuration etc which are also part of a container technology.

            2. 1

              The NAME and SYNOPSIS sections have typos - the devil is in the detail.

              1. 1

                Thanks for letting me know! Fixed them.