Browse Source

Merge branch 'develop'

master v0.3.0
Damien Goutte-Gattat 9 years ago
parent
commit
e5bccb426a
  1. 7
      NEWS
  2. 8
      README
  3. 5
      configure.ac
  4. 174
      src/wait4pid.c
  5. 23
      wait4.1.in

7
NEWS

@ -1,3 +1,10 @@
Changes in wait4 0.3.0
* Use Linux Socket Filtering to filter events in the kernel.
* Add (theoretical) support for BSD kevent(2).
* Fix Netlink initialization (thanks to "neologix", linuxfr.org)
Changes in wait4 0.2.1
* Add the -t, --timeout option.

8
README

@ -21,6 +21,14 @@ privileged process can bind to an AF_NETLINK address; root privileges
are dropped immediately after the bind(2) call.
BSD kevent support
------------------
Starting from version 0.3.0, wait4 should be able to make use of the
kevent(2) call found on BSD systems, thus avoiding the signal-based
fallback method on those systems. Note, however, that this support
is currently theoretical only, as it has not been tested yet.
Copying
-------
wait4 is distributed under the terms of the GNU General Public License,

5
configure.ac

@ -1,6 +1,6 @@
dnl Configure template for the wait4 package
AC_INIT([wait4], [0.2.1],
AC_INIT([wait4], [0.3.0],
[dgouttegattat@incenp.org])
AC_CONFIG_SRCDIR([configure.ac])
AC_CONFIG_MACRO_DIR([m4])
@ -22,7 +22,10 @@ dnl Check for possible mechanism, default is kill
backend="kill"
AC_CHECK_HEADERS([linux/netlink.h],
[backend=Netlink])
AC_CHECK_HEADERS([sys/event.h],
[backend=Kevent])
AM_CONDITIONAL([HAVE_NETLINK], [test x$ac_cv_header_linux_netlink_h = xyes])
AC_CHECK_HEADERS([linux/filter.h])
dnl Output files
AC_CONFIG_FILES([Makefile wait4.1 lib/Makefile src/Makefile])

174
src/wait4pid.c

@ -20,28 +20,67 @@
#include <config.h>
#endif
#ifdef HAVE_LINUX_NETLINK_H
#if defined HAVE_LINUX_NETLINK_H /* Netlink */
#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>
/* Netlink message to (un)register a proc events listener. */
struct __attribute__ ((__packed__)) cn_proc_op {
struct cn_msg msg;
enum proc_cn_mcast_op op;
};
/* Netlink message sent on a proc event. */
struct __attribute__ ((__packed__)) cn_proc_msg {
struct cn_msg msg;
struct proc_event evt;
};
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))];
struct cn_proc_op *payload;
char buf[NLMSG_SPACE(sizeof(struct cn_proc_op))];
memset(buf, 0, sizeof(buf));
hdr = (struct nlmsghdr *)buf;
hdr->nlmsg_type = NLMSG_DONE;
@ -49,12 +88,13 @@ set_proc_listen(int sock, int enable)
hdr->nlmsg_len = NLMSG_LENGTH(sizeof(*payload));
hdr->nlmsg_pid = getpid();
payload = (struct payload_t *) NLMSG_DATA(hdr);
payload = (struct cn_proc_op *) 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 send(sock, hdr, NLMSG_SPACE(sizeof(*payload)), 0);
return do_send(sock, hdr, NLMSG_SPACE(sizeof(*payload)), 0);
}
static void
@ -98,27 +138,66 @@ wait4pid_init(void)
return 0;
}
#ifdef HAVE_LINUX_FILTER_H
#include <linux/filter.h>
#include <arpa/inet.h>
#include <stddef.h>
static int
do_recv(int sock, void *buf, size_t len, int flags)
set_filter(int sock, pid_t *pids, size_t len)
{
int ret;
int i;
struct sock_fprog flt;
struct sock_filter filter[256] = {
/* Load event type. */
BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
NLMSG_LENGTH(0) + offsetof(struct cn_proc_msg, evt.what)),
/* If this is a PROC_EVENT_EXIT, continue to the next step;
* otherwise go to the "block" step at the end of the filter. */
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, htonl(PROC_EVENT_EXIT), 0, 1 + len),
/* Load the process identifier. */
BPF_STMT(BPF_LD+BPF_W+BPF_ABS,
NLMSG_LENGTH(0) +
offsetof(struct cn_proc_msg, evt.event_data.exit.process_pid))
};
if ( len > 251 ) {
errno = EINVAL;
return -1;
}
do {
ret = recv(sock, buf, len, flags);
} while ( ret == -1 && errno == EINTR );
flt.len = 5 + len;
flt.filter = filter;
return ret;
for ( i = 0; i < len; i++ ) {
/* If the loaded PID matches pids[i], go to the "accept" step
* at the end of the filter; otherwise continue to the next step. */
filter[i+3].code = BPF_JMP+BPF_JEQ+BPF_K;
filter[i+3].k = htonl(pids[i]);
filter[i+3].jt = len - i;
filter[i+3].jf = 0;
}
/* No match, block the packet. */
filter[len+3].code = BPF_RET+BPF_K;
filter[len+3].k = 0;
/* Accept the packet. */
filter[len+4].code = BPF_RET+BPF_K;
filter[len+4].k = (uint)-1;
return setsockopt(sock, SOL_SOCKET, SO_ATTACH_FILTER, &flt, sizeof(flt));
}
#endif
int
wait4all(pid_t *pids, size_t len)
{
int ec, nproc, i;
struct __attribute__ ((__packed__ )) payload_t {
struct cn_msg msg;
struct proc_event evt;
} *payload;
char buf[NLMSG_SPACE(sizeof(struct payload_t))];
struct cn_proc_msg *payload;
char buf[NLMSG_SPACE(sizeof(struct cn_proc_msg))];
if ( ! listener_set && wait4pid_init() == -1 )
return -1;
@ -128,30 +207,79 @@ wait4all(pid_t *pids, size_t len)
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 payload_t *) NLMSG_DATA(buf);
payload = (struct cn_proc_msg *) NLMSG_DATA(buf);
if ( payload->evt.what != PROC_EVENT_EXIT )
continue;
#ifdef HAVE_LINUX_FILTER_H
nproc -= 1;
if ( payload->evt.event_data.exit.process_pid == pids[len-1] )
ec = WEXITSTATUS(payload->evt.event_data.exit.exit_code);
#else
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 = payload->evt.event_data.exit.exit_code / 256;
ec = WEXITSTATUS(payload->evt.event_data.exit.exit_code);
}
}
#endif
}
return ec;
}
#else /* HAVE_LINUX_NETLINK_H */
#elif defined HAVE_SYS_EVENT_H /* BSD Kevent */
#include <sys/event.h>
#include <xmem.h>
int
wait4all(pid_t *pids, size_t len)
{
int fd, i, nproc, ec;
struct kevent *evts, evt;
if ( (fd = kqueue()) == -1 )
return -1;
evts = xmalloc(sizeof(struct kevent) * len);
for ( i = 0; i < len; i++ ) {
EV_SET(evts[i], pids[i], EVFILT_PROC, EV_ENABLE, NOTE_EXIT, 0, 0):
}
i = kevent(fd, evts, len, NULL, 0, 0);
free(evts);
if ( i == -1 )
return -1;
nproc = len;
ec = 0;
while ( nproc > 0 ) {
if ( kevent(fd, NULL, 0, &evt, 1, 0) <= 0 )
return -1;
if ( evt->ident == pids[len - 1] )
ec = evt->data;
nproc -= 1;
}
return ec;
}
#else /* Fallback */
#include <time.h>
#include <signal.h>
@ -180,4 +308,4 @@ wait4all(pid_t *pids, size_t len)
return 0;
}
#endif /* ! HAVE_LINUX_NETLINK_H */
#endif

23
wait4.1.in

@ -34,13 +34,24 @@ Aborts waiting after the specified
.SH NOTES
.PP
The ability to report the termination status of the waited
process depends on
.BR netlink (7).
On systems without netlink, a fallback
.B wait4
depends on the
.BR netlink (7)
interface or the
.BR kevent (2)
system call to fulfill its function properly. On systems
which do not have any of these, a fallback
.BR kill (2)-based
mechanism is used, which does not allow to retrieve the
termination status; the exit status is then always 0.
mechanism is used, which suffers from the following
limitations: it does not allow to retrieve the termination
status, and thus
.B wait4
will always return 0; and it can miss the termination of a
process, if its PID gets reused too quickly by a new process.
.PP
When using
.BR netlink (7),
the number of processes to wait for cannot be greater than 251.
.SH REPORTING BUGS
.PP

Loading…
Cancel
Save