8

using c how do I wait for PID X to exit when it is not a child of my current process?

Kill(pid, SIGTERM);
waitpid(pid,NULL,0);

The above does not work as 'pid' is not a child process.

Andrew
  • 317
  • 1
  • 5
  • 11

3 Answers3

5

This is working sample how to subscribe and use PROC_EVENT_EXIT / PROC_EVENT_FORK events. Tested on kernel 3.3.8

#include <sys/types.h>
#include <sys/socket.h>
#include <linux/netlink.h>
#include <linux/connector.h>
#include <linux/cn_proc.h>

#include <stdio.h>
#include <unistd.h>
#include <strings.h>
#include <errno.h>

#define NL_MESSAGE_SIZE (sizeof(struct nlmsghdr) + sizeof(struct cn_msg) + \
                     sizeof(int))

static int nl_sock;

int connect_to_netlink()
{
    struct sockaddr_nl sa_nl; /* netlink interface info */
    char buff[NL_MESSAGE_SIZE];
    struct nlmsghdr *hdr; /* for telling netlink what we want */
    struct cn_msg *msg;   /* the actual connector message */

    /* connect to netlink socket */
    nl_sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR);

    if (-1 == nl_sock) {
         perror("socket failed");
         return errno;
    }

    bzero(&sa_nl, sizeof(sa_nl));
    sa_nl.nl_family = AF_NETLINK;
    sa_nl.nl_groups = CN_IDX_PROC;
    sa_nl.nl_pid    = getpid();

    if (-1 == bind(nl_sock, (struct sockaddr *)&sa_nl, sizeof(sa_nl))) {
        perror("bind failed");
        return errno;
    }

    /* Fill header */
    hdr = (struct nlmsghdr *)buff;
    hdr->nlmsg_len = NL_MESSAGE_SIZE;
    hdr->nlmsg_type = NLMSG_DONE;
    hdr->nlmsg_flags = 0;
    hdr->nlmsg_seq = 0;
    hdr->nlmsg_pid = getpid();

    /* Fill message */
    msg = (struct cn_msg *)NLMSG_DATA(hdr);
    msg->id.idx = CN_IDX_PROC;  /* Connecting to process information */
    msg->id.val = CN_VAL_PROC;
    msg->seq = 0;
    msg->ack = 0;
    msg->flags = 0;
    msg->len = sizeof(int);
    *(int*)msg->data = PROC_CN_MCAST_LISTEN;

    if (-1 == send(nl_sock, hdr, hdr->nlmsg_len, 0)) {
        perror("send failed");
        return errno;
    }

    return 0;
}

void handle_events()
{
    char buff[CONNECTOR_MAX_MSG_SIZE];
    struct nlmsghdr *hdr;
    struct proc_event *event;

    fd_set fds;

    while (1) {
        FD_ZERO(&fds);
        FD_SET(nl_sock, &fds);

        if (0 > select(nl_sock + 1, &fds, NULL, NULL, NULL)) {
            perror("select failed");
            return ;
        }

        /* If there were no events detected, return */
        if (! FD_ISSET(nl_sock, &fds)) {
            return ;
        }

        /* if there are events, make calls */
        if (-1 == recv(nl_sock, buff, sizeof(buff), 0)) {
            perror("recv failed");
            return ;
        }

        hdr = (struct nlmsghdr *)buff;

        if (NLMSG_ERROR == hdr->nlmsg_type) {
            perror("NLMSG_ERROR");
        } else if (NLMSG_DONE == hdr->nlmsg_type) {

            event = (struct proc_event *)((struct cn_msg *)NLMSG_DATA(hdr))->data;

            switch(event->what) {
                case proc_event::PROC_EVENT_EXIT:
                    printf("Process %d (tgid %d) exit with code %d, signal %d\n",
                        event->event_data.exit.process_pid,
                        event->event_data.exit.process_tgid,
                        event->event_data.exit.exit_code,
                        event->event_data.exit.exit_signal);
                    break;

                case proc_event::PROC_EVENT_FORK:
                    printf("New process %d (tgid %d), parent %d (tgid %d)\n",
                        event->event_data.fork.child_pid,
                        event->event_data.fork.child_tgid,
                        event->event_data.fork.parent_pid,
                        event->event_data.fork.parent_tgid);
                    break;

                default:
                    break;
            }
        }
    }
}

int main(int argc, char *argv[])
{
    if (!connect_to_netlink()) {
        handle_events();
    }
    return 0;
}

Compile & run:

# g++ -o psev psev.cpp
# ./psev

Output:

New process 27465 (tgid 27465), parent 2351 (tgid 2351)
Process 27465 (tgid 27465) exit with code 0, signal 17
agile
  • 81
  • 1
  • 2
  • 3
    It should be noted that this requires CAP_NET_ADMIN, and won't work in an unprivileged user process. – the paul Jul 21 '15 at 23:29
4

See this answer by chaos:

The usual practice is to poll using kill(0, pid) and looking for return value -1 and errno of ESRCH to indicate that the process is gone

Based on the man page for kill, it might be kill(pid,0) instead. But the idea is the same.

Community
  • 1
  • 1
Andomar
  • 232,371
  • 49
  • 380
  • 404
  • Andomar - thanks for your answer and pointing out chaos answer to similar question. I Should have spotted that before posting this one. – Andrew Nov 16 '09 at 15:11
  • 1
    this is a bit racy, because the pid can overflow and then be assigned to a new process, so you might end up verfying another process than you initially thought. – user175104 Nov 16 '09 at 20:39
  • 1
    And also, polling for things like this in time intervals is really ugly, as it shortens battery life, and makes your apps appear on powertop and people will laugh at you until you stop to do that in shame. – user175104 Nov 16 '09 at 20:49
  • Indeed, this is a really bad practice, and it's unreliable (subject to race conditions). – R.. GitHub STOP HELPING ICE Jul 19 '11 at 17:00
4

The only way to do this in a clean way (i.e. without polling in time intervals, and without risking PID overflows) is to use the Netlink cn_proc connector (which is Linux specific). There's not much example code or documentation around. It's not a nice API, but it's basically to only sensible API for this.

Look for PROC_EVENT_EXIT, which is the event your are interested in.

http://google.com/codesearch?q=PROC_EVENT_EXIT&hl=en&btnG=Search+Code

user175104
  • 3,598
  • 2
  • 23
  • 20
  • It seems that this API does exactly what is wanted, but I can't get it to work. I tried the sample code at http://netsplit.com/2011/02/09/the-proc-connector-and-socket-filters/ and also https://github.com/pturmel/startmon/blob/master/main.c but nothing is ever read from the socket. I would be very interested if you had details about how to make this work. – a3nm Aug 06 '12 at 13:17