#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;
};
static void print_signal_name(int signum)
{
- size_t n;
+ const char *name = signum_to_signame(signum);
- for (n = 0; n < ARRAY_SIZE(sys_signame); n++) {
- if (sys_signame[n].val == signum) {
- printf("%s\n", sys_signame[n].name);
- return;
- }
+ if (name) {
+ printf("%s\n", name);
+ return;
}
#ifdef SIGRTMIN
if (SIGRTMIN <= signum && signum <= SIGRTMAX) {
static void print_all_signals(FILE *fp, int pretty)
{
size_t n, lth, lpos = 0, width;
+ const char *signame = NULL;
+ int signum = 0;
if (!pretty) {
- for (n = 0; n < ARRAY_SIZE(sys_signame); n++) {
- lth = 1 + strlen(sys_signame[n].name);
+ for (n = 0; get_signame_by_idx(n, &signame, NULL) == 0; n++) {
+ lth = 1 + strlen(signame);
if (KILL_OUTPUT_WIDTH < lpos + lth) {
fputc('\n', fp);
lpos = 0;
} else if (lpos)
fputc(' ', fp);
lpos += lth;
- fputs(sys_signame[n].name, fp);
+ fputs(signame, fp);
}
#ifdef SIGRTMIN
fputs(" RT<N> RTMIN+<N> RTMAX-<N>", fp);
/* pretty print */
width = get_terminal_width(KILL_OUTPUT_WIDTH + 1) - 1;
- for (n = 0; n < ARRAY_SIZE(sys_signame); n++)
- pretty_print_signal(fp, width, &lpos,
- sys_signame[n].val, sys_signame[n].name);
+ for (n = 0; get_signame_by_idx(n, &signame, &signum) == 0; n++)
+ pretty_print_signal(fp, width, &lpos, signum, signame);
#ifdef SIGRTMIN
pretty_print_signal(fp, width, &lpos, SIGRTMIN, "RTMIN");
pretty_print_signal(fp, width, &lpos, SIGRTMAX, "RTMAX");
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);
exit(EXIT_SUCCESS);
}
+static void __attribute__((__noreturn__)) print_kill_version(void)
+{
+ static const char *features[] = {
+#ifdef HAVE_SIGQUEUE
+ "sigqueue",
+#endif
+#ifdef UL_HAVE_PIDFD
+ "pidfd",
+#endif
+ };
+
+ printf(_("%s from %s"), program_invocation_short_name, PACKAGE_STRING);
+
+ if (ARRAY_SIZE(features)) {
+ size_t i;
+ fputs(_(" (with: "), stdout);
+ for (i = 0; i < ARRAY_SIZE(features); i++) {
+ fputs(features[i], stdout);
+ if (i + 1 < ARRAY_SIZE(features))
+ fputs(", ", stdout);
+ }
+ fputs(")\n", stdout);
+ }
+ exit(EXIT_SUCCESS);
+}
+
static char **parse_arguments(int argc, char **argv, struct kill_control *ctl)
{
char *arg;
break;
}
if (!strcmp(arg, "-v") || !strcmp(arg, "-V") ||
- !strcmp(arg, "--version")) {
- printf(UTIL_LINUX_VERSION);
- exit(EXIT_SUCCESS);
- }
+ !strcmp(arg, "--version"))
+ print_kill_version();
if (!strcmp(arg, "-h") || !strcmp(arg, "--help"))
usage();
if (!strcmp(arg, "--verbose")) {
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"));
+ INIT_LIST_HEAD(&next->follow_ups);
+ 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);
setlocale(LC_ALL, "");
bindtextdomain(PACKAGE, LOCALEDIR);
textdomain(PACKAGE);
- atexit(close_stdout);
+ close_stdout_atexit();
+#ifdef UL_HAVE_PIDFD
+ INIT_LIST_HEAD(&ctl.follow_ups);
+#endif
argv = parse_arguments(argc, argv, &ctl);
/* The rest of the arguments should be process ids and names. */
}
}
+#ifdef UL_HAVE_PIDFD
+ while (!list_empty(&ctl.follow_ups)) {
+ struct timeouts *x = list_entry(ctl.follow_ups.next,
+ struct timeouts, follow_ups);
+ list_del(&x->follow_ups);
+ free(x);
+ }
+#endif
if (ct && nerrs == 0)
return EXIT_SUCCESS; /* full success */
else if (ct == nerrs)