Wait for an arbitrary process.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

261 lines
6.2 KiB

/*
* wait4pid - Wait for an arbitrary process
* Copyright (C) 2013 Damien Goutte-Gattat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef HAVE_LINUX_NETLINK_H
#include <string.h>
#include <unistd.h>
#include <signal.h>
#include <sys/socket.h>
#include <sys/wait.h> /* for WEXITSTATUS */
#include <linux/netlink.h>
#include <linux/connector.h>
#include <linux/cn_proc.h>
int sock = 0;
int listener_set = 0;
static ssize_t
do_send(int sock, const void *buf, size_t len, int flags)
{
int ret;
do {
ret = send(sock, buf, len, flags);
} while ( ret == -1 && errno == EINTR );
return ret;
}
static int
do_recv(int sock, void *buf, size_t len, int flags)
{
int ret;
do {
ret = recv(sock, buf, len, flags);
} while ( ret == -1 && errno == EINTR );
return ret;
}
static int
set_proc_listen(int sock, int enable)
{
struct nlmsghdr *hdr;
struct __attribute__ ((__packed__)) payload_t {
struct cn_msg msg;
enum proc_cn_mcast_op op;
} *payload;
char buf[NLMSG_SPACE(sizeof(struct payload_t))];
memset(buf, 0, sizeof(buf));
hdr = (struct nlmsghdr *)buf;
hdr->nlmsg_type = NLMSG_DONE;
hdr->nlmsg_flags = NLM_F_REQUEST;
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*payload));
hdr->nlmsg_pid = getpid();
payload = (struct payload_t *) NLMSG_DATA(hdr);
payload->msg.id.idx = CN_IDX_PROC;
payload->msg.id.val = CN_VAL_PROC;
payload->msg.len = sizeof(payload->op);
payload->op = enable ? PROC_CN_MCAST_LISTEN : PROC_CN_MCAST_IGNORE;
return do_send(sock, hdr, NLMSG_SPACE(sizeof(*payload)), 0);
}
static void
wait4pid_close(void)
{
if ( listener_set ) {
set_proc_listen(sock, 0);
listener_set = 0;
}
if ( sock ) {
close(sock);
sock = 0;
}
}
static int
wait4pid_init(void)
{
struct sockaddr_nl sa;
if ( (sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR)) == -1 )
return -1;
atexit(wait4pid_close);
memset(&sa, 0, sizeof(sa));
sa.nl_family = AF_NETLINK;
sa.nl_groups = CN_IDX_PROC;
sa.nl_pid = getpid();
if ( bind(sock, (struct sockaddr *)&sa, sizeof(sa)) == -1 )
return -1;
if ( setgid(getgid()) == -1 || setuid(getuid()) == -1 )
return -1;
if ( set_proc_listen(sock, 1) == -1 )
return -1;
return 0;
}
struct __attribute__ ((__packed__)) cn_proc_msg {
struct cn_msg msg;
struct proc_event evt;
};
#ifdef HAVE_LINUX_FILTER_H
#include <linux/filter.h>
#include <arpa/inet.h>
#include <stddef.h>
static int
set_filter(int sock, pid_t *pids, size_t len)
{
struct sock_fprog flt;
int i, j = 0;
flt.len = 5 + len;
if ( (flt.filter = calloc(sizeof(struct sock_filter), flt.len)) == NULL )
return -1;
/* Block the packet if proc_event.what != PROC_EVENT_EXIT */
flt.filter[j].code = BPF_LD+BPF_W+BPF_ABS;
flt.filter[j++].k = NLMSG_LENGTH(0)
+ offsetof(struct cn_proc_msg, evt.what);
flt.filter[j].code = BPF_JMP+BPF_JEQ+BPF_K;
flt.filter[j].k = htonl(PROC_EVENT_EXIT);
flt.filter[j].jt = 0; /* continue */
flt.filter[j++].jf = 1 + len; /* jump to the "block" instruction */
/* Get the process identifier */
flt.filter[j].code = BPF_LD+BPF_W+BPF_ABS;
flt.filter[j++].k = NLMSG_LENGTH(0)
+ offsetof(struct cn_proc_msg, evt.event_data.exit.process_pid);
for ( i = 0; i < len; i++ ) {
/* Accept the packet if its pid matches pids[i]. */
flt.filter[j].code = BPF_JMP+BPF_JEQ+BPF_K;
flt.filter[j].k = htonl(pids[i]);
flt.filter[j].jt = len - i; /* jump to the "accept" instruction */
flt.filter[j++].jf = 0; /* continue */
}
/* No match, block the packet. */
flt.filter[j].code = BPF_RET+BPF_K;
flt.filter[j++].k = 0;
/* Accept the packet. */
flt.filter[j].code = BPF_RET+BPF_K;
flt.filter[j++].k = (uint)-1;
i = setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &flt, sizeof(flt));
free(flt.filter);
return i;
}
#endif
int
wait4all(pid_t *pids, size_t len)
{
int ec, nproc, i;
struct cn_proc_msg *payload;
char buf[NLMSG_SPACE(sizeof(struct cn_proc_msg))];
if ( ! listener_set && wait4pid_init() == -1 )
return -1;
for ( i = 0; i < len; i++ ) {
if ( kill(pids[i], 0) == -1 && errno == ESRCH )
return -1;
}
#ifdef HAVE_LINUX_FILTER_H
if ( set_filter(sock, pids, len) == -1 )
return -1;
#endif
nproc = len;
while ( nproc > 0 ) {
if ( do_recv(sock, buf, sizeof(buf), 0) <= 0 )
return -1;
payload = (struct cn_proc_msg *) NLMSG_DATA(buf);
if ( payload->evt.what != PROC_EVENT_EXIT )
continue;
for ( i = 0; i < len; i++ ) {
if ( pids[i] != -1
&& pids[i] == payload->evt.event_data.exit.process_pid ) {
pids[i] = -1;
nproc -= 1;
if ( i == len - 1 )
ec = WEXITSTATUS(payload->evt.event_data.exit.exit_code);
}
}
}
return ec;
}
#else /* HAVE_LINUX_NETLINK_H */
#include <time.h>
#include <signal.h>
int
wait4all(pid_t *pids, size_t len)
{
int nproc, i;
struct timespec ts = { 1, 0 };
nproc = len;
while ( nproc > 0 ) {
for ( i = 0; i < len; i++ ) {
if ( pids[i] == -1 )
continue;
if ( kill(pids[i], 0) == -1 && errno == ESRCH ) {
pids[i] = -1;
nproc -= 1;
}
}
nanosleep(&ts, NULL);
}
return 0;
}
#endif /* ! HAVE_LINUX_NETLINK_H */