Search code examples
linuxprocessdexit

How do I kill linux spawnProcess when the main process suddenly dies?


I have come across a problem with my application and the spawnProcess. If the main application for some reason dies/is killed then the spawned processes seem to live on and I can't reach them unless I use terminal to kill them via their PIDs. My goal is if the main application dies then the spawned processes should be killed also, somehow.

My code is like this

auto appPid = spawnProcess("path/to/process");
scope(exit){ auto exitcode = wait(appPid); 
stderr.writeln(...);}

And if I use the same approach when the main process dies, using wait(thisProcessID) I get an error. "No overload matches". Any ideas how to solve this problem?


Solution

  • Here's some code that will do it on Linux. It doesn't have all the features of the stdlib's spawnProcess, it just shows the bare basics, but expanding it from here isn't hard if you need more.

    import core.sys.posix.unistd;
    
    version(linux) {
            // this function is Linux-specific
            import core.stdc.config;
            import core.sys.posix.signal;
            // we can tell the kernel to send our child process a signal
            // when the parent dies...
            extern(C) int prctl(int, c_ulong, c_ulong, c_ulong, c_ulong);
            // the constant I pulled out of the C headers
            enum PR_SET_PDEATHSIG = 1;
    }
    
    pid_t mySpawnProcess(string process) {
            if(auto pid = fork()) {
                    // this branch is the parent, it can return the child pid
                    // you can:
                    // import core.sys.posix.sys.wait;
                    // waitpid(this_ret_value, &status, 0);
                    // if you want the parent to wait for the child to die
                    return pid;
            } else {
                    // child
    
                    // first, tell it to terminate when the parent dies
                    prctl(PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0);
    
                    // then, exec our process
                    char*[2] args;
                    char[255] buffer;
                    // gotta copy the string into another buffer
                    // so we zero terminate it and have a C style char**...
                    buffer[0 .. process.length] = process[];
                    buffer[process.length] = 0;
                    args[0] = buffer.ptr;
    
                    // then call exec to run the new program
                    execve(args[0], args.ptr, null);
                    assert(0); // never reached
            }
    }
    
    void main() {
            mySpawnProcess("/usr/bin/cat");
            // parent process sleeps for one second, then exits
            usleep(1_000_000);
    }
    

    So the lower level functions need to be used, but Linux does have a function that does what you need.

    Of course, since it sends a signal, your child might want to handle that to close more gracefully than the default termination, but try this program and run ps while it sleeps to see cat running, then notice the cat dies when the parent exits.