Detecting dumped processes

As mentioned above, you can run dumper so that when a process dies, dumper writes the state of the process to a file.

You can also write your own dumper-type process to run instead of, or as well as, dumper. This way the terminating process doesn't have to be a child of yours.

To do this, write a resource manager that registers the name, /proc/dumper with type _FTYPE_DUMPER. When a process dies due to one of the appropriate signals, the process manager will open /proc/dumper and write the pid of the process that died—then it'll wait until you reply to the write with success and then it'll finish terminating the process.

It's possible that more than one process will have /proc/dumper registered at the same time, however, the process manager notifies only the process that's at the beginning of its list for that name. Undoubtedly, you want both your resource manager and dumper to handle this termination. To do this, request the process manager to put you, instead of dumper, at the beginning of the /proc/dumper list by passing _RESMGR_FLAG_BEFORE in the flags argument to resmgr_attach(). You must also open /proc/dumper so that you can communicate with dumper if it's running. Whenever your io_write handler is called, write the pid to dumper and do your own handling. Of course this works only when dumper is run before your resource manager; otherwise, your open of /proc/dumper won't work.

The following is a sample process that demonstrates the above:

/*
 *  dumphandler.c
 *
 *  This demonstrates how you get notified whenever a process
 *  dies due to any of the following signals:
 *
 *  SIGABRT
 *  SIGBUS
 *  SIGEMT
 *  SIGFPE
 *  SIGILL
 *  SIGQUIT
 *  SIGSEGV
 *  SIGSYS
 *  SIGTRAP
 *  SIGXCPU
 *
 *  To do so, register the path, /proc/dumper with type
 *  _FTYPE_DUMPER. When a process dies due to one of the above
 *  signals, the process manager will open /proc/dumper, and
 *  write the pid of the process that died - it will wait until
 *  you reply to the write with success, and then it will finish
 *  terminating the process.
 *
 *  Note that while it is possible for more than one process to
 *  have /proc/dumper registered at the same time, the process
 *  manager will notify only the one that is at the beginning of
 *  its list for that name.
 *
 *  But we want both us and dumper to handle this termination.
 *  To do this, we make sure that we get notified instead of
 *  dumper by asking the process manager to put us at the
 *  beginning of its list for /proc/dumper (done by passing
 *  _RESMGR_FLAG_BEFORE to  resmgr_attach()).  We also open
 *  /proc/dumper so that we can communicate with dumper if it is
 *  running.  Whenever our io_write handler is called, we write
 *  the pid to dumper and do our own handling.  Of course, this
 *  works only if dumper is run before we are, or else our open
 *  will not work.
 *
*/

#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <sys/iofunc.h>
#include <sys/dispatch.h>
#include <sys/neutrino.h>
#include <sys/procfs.h>
#include <sys/stat.h>

int io_write (resmgr_context_t *ctp, io_write_t  *msg,
              RESMGR_OCB_T *ocb);

static int  dumper_fd;

resmgr_connect_funcs_t  connect_funcs;
resmgr_io_funcs_t       io_funcs;
dispatch_t              *dpp;
resmgr_attr_t           rattr;
dispatch_context_t      *ctp;
iofunc_attr_t           ioattr;

char    *progname = "dumphandler";

main(int argc, char **argv)
{
    /* find dumper so that we can pass any pids on to it */
    dumper_fd = open("/proc/dumper", O_WRONLY);

    dpp = dispatch_create();

    memset(&rattr, 0, sizeof(rattr));
    rattr.msg_max_size = 2048;

    iofunc_func_init(_RESMGR_CONNECT_NFUNCS, &connect_funcs,
                     _RESMGR_IO_NFUNCS, &io_funcs);
    io_funcs.write = io_write;

    iofunc_attr_init(&ioattr, S_IFNAM | 0600, NULL, NULL);

    resmgr_attach(dpp, &rattr, "/proc/dumper", _FTYPE_DUMPER,
                  _RESMGR_FLAG_BEFORE, &connect_funcs,
                  &io_funcs, &ioattr);

    ctp = dispatch_context_alloc(dpp);

    while (1) {
        if ((ctp = dispatch_block(ctp)) == NULL) {
            fprintf(stderr, "%s:  dispatch_block failed: %s\n",
                             progname, strerror(errno));
            exit(1);
        }
        dispatch_handler(ctp);
    }
}

struct dinfo_s {
    procfs_debuginfo    info;
    char                pathbuffer[PATH_MAX]; /* 1st byte is
                                                 info.path[0] */
};

int
display_process_info(pid_t pid)
{
    char            buf[PATH_MAX + 1];
    int             fd, status;
    struct dinfo_s  dinfo;
    procfs_greg     reg;

    printf("%s: process %d died\n", progname, pid);

    sprintf(buf, "/proc/%d/ctl", pid);

    if ((fd = open(buf, O_RDONLY|O_NONBLOCK)) == -1)
        return errno;

    status = devctl(fd, DCMD_PROC_MAPDEBUG_BASE, &dinfo,
                    sizeof(dinfo), NULL);
    if (status != EOK) {
        close(fd);
        return status;
    }

    printf("%s: name is %s\n", progname, dinfo.info.path);

    /*
     * For getting other type of information, see sys/procfs.h,
     * sys/debug.h, and sys/dcmd_proc.h
     */
     
    close(fd);
    return EOK;
}

int
io_write(resmgr_context_t *ctp, io_write_t *msg,
         RESMGR_OCB_T *ocb)
{
    char *pstr;
    int status;

    if ((status = iofunc_write_verify(ctp, msg, ocb, NULL))
        != EOK)
        return status;

    if ( (msg->i.xtype & _IO_XTYPE_MASK) != _IO_XTYPE_NONE)
        return ENOSYS;


    if (dumper_fd != -1) {
        /* pass it on to dumper so it can handle it too */

        if (write(dumper_fd, msg+1, msg->i.nbytes) == -1) {
            close(dumper_fd);
            dumper_fd = -1; /* something wrong, no sense in
                               doing it again later */
        }
    }

    /* Proc writes us the pid as a string; get a pointer to the write data */
    pstr = (char *) (msg+1);

    /* Assume we have room for a null byte at the end of the pid in our
      (default) 1500 byte receive buffer */
    pstr[msg->i.nbytes] = '\0';

    if ((status = display_process_info(atoi(pstr))) != EOK)
        return status;

    _IO_SET_WRITE_NBYTES(ctp, msg->i.nbytes);

    return EOK;
}

For more information about getting process information (including using the DCMD_PROC_MAPDEBUG_BASE devctl() command), see Controlling processes via the /proc filesystem,” later in this chapter.