--- /dev/null
+#ifndef UTIL_LINUX_PIDFD_UTILS
+#define UTIL_LINUX_PIDFD_UTILS
+
+#if defined (__linux__)
+# include <sys/types.h>
+# include <sys/syscall.h>
+# ifndef HAVE_PIDFD_OPEN
+static inline int pidfd_send_signal(int pidfd, int sig, siginfo_t *info,
+ unsigned int flags)
+{
+ return syscall(SYS_pidfd_send_signal, pidfd, sig, info, flags);
+}
+# endif
+# ifndef HAVE_PIDFD_SEND_SIGNAL
+static inline int pidfd_open(pid_t pid, unsigned int flags)
+{
+ return syscall(SYS_pidfd_open, pid, flags);
+}
+# endif
+# define UL_HAVE_PIDFD 1
+#endif
+
+#endif
.\" Copyright 1994 Salvatore Valente (svalente@mit.edu)
.\" Copyright 1992 Rickard E. Faith (faith@cs.unc.edu)
.\" May be distributed under the GNU General Public License
-.TH KILL 1 "July 2014" "util-linux" "User Commands"
+.TH KILL 1 "November 2019" "util-linux" "User Commands"
.SH NAME
kill \- terminate a process
.SH SYNOPSIS
.RB [ \-q
.IR value ]
.RB [ \-a ]
+\fR[\fB\-\-timeout \fImilliseconds signal\fR]
.RB [ \-\- ]
.IR pid | name ...
.br
field of the
.I siginfo_t
structure.
-
+.TP
+\fB\-\-timeout\fR \fImilliseconds signal\fR
+Send a signal defined the usual way to a process.
+.B \-\-timeout
+will make
+.B kill
+to wait for a period defined in
+.I milliseconds
+before sending follow-up
+.I signal
+to process. When timeout is speficified multiple times to a list of
+timeouts and signals that are sent sequentially. The
+.B \-\-timeout
+option can be combined with
+.B \-\-queue
+option.
+.IP
+Example. Send signal that does nothing twice, and terminate cat(1).
+.br
+kill --timeout 1000 0 --timeout 1000 TERM --verbose -s 0 cat
.SH NOTES
Although it is possible to specify the TID (thread ID, see
.BR gettid (2))
#include "c.h"
#include "closestream.h"
#include "nls.h"
+#include "pidfd-utils.h"
#include "procutils.h"
#include "signames.h"
#include "strutils.h"
KILL_OUTPUT_WIDTH = 72
};
+#ifdef UL_HAVE_PIDFD
+# include <poll.h>
+# include "list.h"
+struct timeouts {
+ int period;
+ int sig;
+ struct list_head follow_ups;
+};
+#endif
+
struct kill_control {
char *arg;
pid_t pid;
int numsig;
#ifdef HAVE_SIGQUEUE
union sigval sigdata;
+#endif
+#ifdef UL_HAVE_PIDFD
+ struct list_head follow_ups;
#endif
unsigned int
check_all:1,
do_kill:1,
do_pid:1,
use_sigval:1,
+#ifdef UL_HAVE_PIDFD
+ timeout:1,
+#endif
verbose:1;
};
fputs(_(" -s, --signal <signal> send this <signal> instead of SIGTERM\n"), out);
#ifdef HAVE_SIGQUEUE
fputs(_(" -q, --queue <value> use sigqueue(2), not kill(2), and pass <value> as data\n"), out);
+#endif
+#ifdef UL_HAVE_PIDFD
+ fputs(_(" --timeout <milliseconds> <follow-up signal>\n"
+ " wait up to timeout and send follow-up signal\n"), out);
#endif
fputs(_(" -p, --pid print pids without signaling them\n"), out);
fputs(_(" -l, --list[=<signal>] list signal names, or convert a signal number to a name\n"), out);
ctl->use_sigval = 1;
continue;
}
+#endif
+#ifdef UL_HAVE_PIDFD
+ if (!strcmp(arg, "--timeout")) {
+ struct timeouts *next;
+
+ ctl->timeout = 1;
+ if (argc < 2)
+ errx(EXIT_FAILURE, _("option '%s' requires an argument"), arg);
+ argc--, argv++;
+ arg = *argv;
+ next = xcalloc(1, sizeof(*next));
+ next->period = strtos32_or_err(arg, _("argument error"));
+ argc--, argv++;
+ arg = *argv;
+ if ((next->sig = arg_to_signum(arg, 0)) < 0)
+ err_nosig(arg);
+ list_add_tail(&next->follow_ups, &ctl->follow_ups);
+ continue;
+ }
#endif
/* 'arg' begins with a dash but is not a known option.
* So it's probably something like -HUP, or -1/-n try to
return argv;
}
+#ifdef UL_HAVE_PIDFD
+static int kill_with_timeout(const struct kill_control *ctl)
+{
+ int pfd, n;
+ struct pollfd p = { 0 };
+ siginfo_t info = { 0 };
+ struct list_head *entry;
+
+ info.si_code = SI_QUEUE;
+ info.si_signo = ctl->numsig;
+ info.si_uid = getuid();
+ info.si_pid = getpid();
+ info.si_value.sival_int =
+ ctl->use_sigval != 0 ? ctl->use_sigval : ctl->numsig;
+
+ if ((pfd = pidfd_open(ctl->pid, 0)) < 0)
+ err(EXIT_FAILURE, _("pidfd_open() failed: %d"), ctl->pid);
+ p.fd = pfd;
+ p.events = POLLIN;
+
+ if (pidfd_send_signal(pfd, ctl->numsig, &info, 0) < 0)
+ err(EXIT_FAILURE, _("pidfd_send_signal() failed"));
+ list_for_each(entry, &ctl->follow_ups) {
+ struct timeouts *timeout;
+
+ timeout = list_entry(entry, struct timeouts, follow_ups);
+ n = poll(&p, 1, timeout->period);
+ if (n < 0)
+ err(EXIT_FAILURE, _("poll() failed"));
+ if (n == 0) {
+ info.si_signo = timeout->sig;
+ if (ctl->verbose)
+ printf(_("timeout, sending signal %d to pid %d\n"),
+ timeout->sig, ctl->pid);
+ if (pidfd_send_signal(pfd, timeout->sig, &info, 0) < 0)
+ err(EXIT_FAILURE, _("pidfd_send_signal() failed"));
+ }
+ }
+ return 0;
+}
+#endif
static int kill_verbose(const struct kill_control *ctl)
{
printf("%ld\n", (long) ctl->pid);
return 0;
}
+#ifdef UL_HAVE_PIDFD
+ if (ctl->timeout) {
+ rc = kill_with_timeout(ctl);
+ } else
+#endif
#ifdef HAVE_SIGQUEUE
if (ctl->use_sigval)
rc = sigqueue(ctl->pid, ctl->numsig, ctl->sigdata);
textdomain(PACKAGE);
close_stdout_atexit();
+ INIT_LIST_HEAD(&ctl.follow_ups);
argv = parse_arguments(argc, argv, &ctl);
/* The rest of the arguments should be process ids and names. */