AM_SILENT_RULES([yes])
AC_CONFIG_SRCDIR([free.c])
AC_CONFIG_HEADERS([config.h])
+AC_LANG([C])
# Checks for programs.
AC_USE_SYSTEM_EXTENSIONS
AC_MSG_RESULT(yes),
AC_MSG_RESULT(no))
+AC_CHECK_FUNC([pidfd_open], [enable_pwait=yes], [
+ AC_MSG_CHECKING([for __NR_pidfd_open])
+ AC_COMPILE_IFELSE([AC_LANG_SOURCE([
+#include <sys/syscall.h>
+#ifndef __NR_pidfd_open
+#error __NR_pidfd_open not defined
+#endif
+ ])], [enable_pwait=yes], [enable_pwait=no])
+ AC_MSG_RESULT([$enable_pwait])
+])
+if test "$enable_pwait" = yes; then
+ AC_DEFINE([ENABLE_PWAIT], [1], [Enable pwait])
+fi
+AM_CONDITIONAL([BUILD_PWAIT], [test x$enable_pwait = xyes])
+
dnl watch8bit must be before the AC_ARG_WITH set as it sets up ncurses
AC_SUBST([WITH_WATCH8BIT])
AC_ARG_ENABLE([watch8bit],
.\"
.TH PGREP "1" "2020-06-04" "procps-ng" "User Commands"
.SH NAME
-pgrep, pkill \- look up or signal processes based on name and other attributes
+pgrep, pkill, pwait \- look up, signal, or wait for processes based on name and other attributes
.SH SYNOPSIS
.B pgrep
[options] pattern
.br
.B pkill
[options] pattern
+.br
+.B pwait
+[options] pattern
.SH DESCRIPTION
.B pgrep
looks through the currently running processes and lists the process IDs which
will send the specified signal (by default
.BR SIGTERM )
to each process instead of listing them on stdout.
+.PP
+.B pwait
+will wait for each process instead of listing them on stdout.
.SH OPTIONS
.TP
\fB\-\fR\fIsignal\fP
\fB\-c\fR, \fB\-\-count\fR
Suppress normal output; instead print a count of matching processes. When
count does not match anything, e.g. returns zero, the command will return
-non-zero value.
+non-zero value. Note that for pkill and pwait, the count is the number of
+matching processes, not the processes that were successfully signaled or waited
+for.
.TP
\fB\-d\fR, \fB\-\-delimiter\fR \fIdelimiter\fP
Sets the string used to delimit each process ID in the output (by default a
\fB\-g\fR, \fB\-\-pgroup\fR \fIpgrp\fP,...
Only match processes in the process group IDs listed. Process group 0 is
translated into
-.BR pgrep 's
+.BR pgrep 's,
+.BR pkill 's,
or
-.BR pkill 's
+.BR pwait 's
own process group.
.TP
\fB\-G\fR, \fB\-\-group\fR \fIgid\fP,...
\fB\-s\fR, \fB\-\-session\fR \fIsid\fP,...
Only match processes whose process session ID is listed. Session ID 0
is translated into
-.BR pgrep 's
+.BR pgrep 's,
+.BR pkill 's,
or
-.BR pkill 's
+.BR pwait 's
own session ID.
.TP
\fB\-t\fR, \fB\-\-terminal\fR \fIterm\fP,...
\fB\-v\fR, \fB\-\-inverse\fR\fR
Negates the matching. This option is usually used in
.BR pgrep 's
+or
+.BR pwait 's
context. In
.BR pkill 's
context the short option is disabled to avoid accidental usage of the option.
\fB\-w\fR, \fB\-\-lightweight\fR\fR
Shows all thread ids instead of pids in
.BR pgrep 's
+or
+.BR pwait 's
context. In
.BR pkill 's
context this option is disabled.
.IR pattern .
.TP
\fB\-F\fR, \fB\-\-pidfile\fR \fIfile\fR
-Read \fIPID\fRs from \fIfile\fR. This option is perhaps more useful for
-.B pkill
+Read \fIPID\fRs from \fIfile\fR. This option is more useful for
+.BR pkill or pwait
than
.BR pgrep .
.TP
.PD 0
.TP
0
-One or more processes matched the criteria. For pkill the process must also
-have been successfully signalled.
+One or more processes matched the criteria. For pkill and pwait, one or more
+processes must also have been successfully signalled or waited for.
.TP
1
No processes matched or none of them could be signalled.
complete command line, /proc/\fIpid\fP/cmdline.
.PP
The running
-.B pgrep
+.BR pgrep ,
+.BR pkill ,
or
-.B pkill
+.B pwait
process will never report itself as a
match.
.SH BUGS
#include <stdbool.h>
#include <time.h>
+#if defined(ENABLE_PWAIT) && !defined(HAVE_PIDFD_OPEN)
+#include <sys/epoll.h>
+#include <sys/syscall.h>
+#endif
+
/* EXIT_SUCCESS is 0 */
/* EXIT_FAILURE is 1 */
#define EXIT_USAGE 2
(x) = (x) * 5 / 4 + 4; \
} while (0)
-static int i_am_pkill = 0;
+static enum {
+ PGREP = 0,
+ PKILL,
+#ifdef ENABLE_PWAIT
+ PWAIT,
+#endif
+} prog_mode;
struct el {
long num;
fputs(USAGE_HEADER, fp);
fprintf(fp, _(" %s [options] <pattern>\n"), program_invocation_short_name);
fputs(USAGE_OPTIONS, fp);
- if (i_am_pkill == 0) {
+ switch (prog_mode) {
+ case PGREP:
fputs(_(" -d, --delimiter <string> specify output delimiter\n"),fp);
fputs(_(" -l, --list-name list PID and process name\n"),fp);
fputs(_(" -a, --list-full list PID and full command line\n"),fp);
fputs(_(" -v, --inverse negates the matching\n"),fp);
fputs(_(" -w, --lightweight list all TID\n"), fp);
- }
- if (i_am_pkill == 1) {
+ break;
+ case PKILL:
fputs(_(" -<sig>, --signal <sig> signal to send (either number or name)\n"), fp);
fputs(_(" -q, --queue <value> integer value to be sent with the signal\n"), fp);
fputs(_(" -e, --echo display what is killed\n"), fp);
+ break;
+#ifdef ENABLE_PWAIT
+ case PWAIT:
+ fputs(_(" -e, --echo display PIDs before waiting\n"), fp);
+ break;
+#endif
}
fputs(_(" -c, --count count of matching processes\n"), fp);
fputs(_(" -f, --full use full process name to match\n"), fp);
xerrx(EXIT_FAILURE, _("internal error"));
}
- // pkill does not need subtasks!
- // this control is still done at
- // argparse time, but a further
- // control is free
- if (opt_threads && !i_am_pkill) {
+ // pkill and pwait don't support -w, but this is checked in getopt
+ if (opt_threads) {
while (readtask(ptp, &task, &subtask)){
// don't add redundant tasks
if (task.XXXID == subtask.XXXID)
return -1;
}
+#if defined(ENABLE_PWAIT) && !defined(HAVE_PIDFD_OPEN)
+static int pidfd_open (pid_t pid, unsigned int flags)
+{
+ return syscall(__NR_pidfd_open, pid, flags);
+}
+#endif
+
static void parse_opts (int argc, char **argv)
{
char opts[64] = "";
{NULL, 0, NULL, 0}
};
- if (strstr (program_invocation_short_name, "pkill")) {
+#ifdef ENABLE_PWAIT
+ if (strcmp (program_invocation_short_name, "pwait") == 0) {
+ prog_mode = PWAIT;
+ strcat (opts, "e");
+ } else
+#endif
+ if (strcmp (program_invocation_short_name, "pkill") == 0) {
int sig;
- i_am_pkill = 1;
+ prog_mode = PKILL;
sig = signal_option(&argc, argv);
if (-1 < sig)
opt_signal = sig;
- /* These options are for pkill only */
strcat (opts, "eq:");
} else {
- /* These options are for pgrep only */
+ prog_mode = PGREP;
strcat (opts, "lad:vw");
}
{
struct el *procs;
int num;
+ int i;
+ int kill_count = 0;
+#ifdef ENABLE_PWAIT
+ int poll_count = 0;
+ int wait_count = 0;
+ int epollfd = epoll_create(1);
+ struct epoll_event ev, events[32];
+#endif
#ifdef HAVE_PROGRAM_INVOCATION_NAME
program_invocation_name = program_invocation_short_name;
parse_opts (argc, argv);
procs = select_procs (&num);
- if (i_am_pkill) {
- int i;
- int kill_count = 0;
+ switch (prog_mode) {
+ case PGREP:
+ if (opt_count) {
+ fprintf(stdout, "%d\n", num);
+ } else {
+ if (opt_long || opt_longlong)
+ output_strlist (procs,num);
+ else
+ output_numlist (procs,num);
+ }
+ return !num;
+
+ case PKILL:
for (i = 0; i < num; i++) {
if (execute_kill (procs[i].num, opt_signal) != -1) {
if (opt_echo)
printf(_("%s killed (pid %lu)\n"), procs[i].str, procs[i].num);
- kill_count++;
+ kill_count++;
continue;
}
if (errno==ESRCH)
- /* gone now, which is OK */
+ /* gone now, which is OK */
continue;
xwarn(_("killing pid %ld failed"), procs[i].num);
}
if (opt_count)
fprintf(stdout, "%d\n", num);
return !kill_count;
- } else {
- if (opt_count) {
+
+#ifdef ENABLE_PWAIT
+ case PWAIT:
+ if (opt_count)
fprintf(stdout, "%d\n", num);
- } else {
- if (opt_long || opt_longlong)
- output_strlist (procs,num);
- else
- output_numlist (procs,num);
+
+ for (i = 0; i < num; i++) {
+ if (opt_echo)
+ printf(_("waiting for %s (pid %lu)\n"), procs[i].str, procs[i].num);
+ int pidfd = pidfd_open(procs[i].num, 0);
+ if (pidfd == -1) {
+ /* ignore ESRCH, same as pkill */
+ if (errno != ESRCH)
+ xwarn(_("opening pid %ld failed"), procs[i].num);
+ continue;
+ }
+ ev.events = EPOLLIN | EPOLLET;
+ ev.data.fd = pidfd;
+ if (epoll_ctl(epollfd, EPOLL_CTL_ADD, pidfd, &ev) != -1)
+ poll_count++;
+ }
+
+ while (wait_count < poll_count) {
+ int ew = epoll_wait(epollfd, events, sizeof(events)/sizeof(events[0]), -1);
+ if (ew == -1) {
+ if (errno == EINTR)
+ continue;
+ xwarn(_("epoll_wait failed"));
+ }
+ wait_count += ew;
}
+
+ return !wait_count;
+#endif
}
- return !num; /* exit(EXIT_SUCCESS) if match, otherwise exit(EXIT_FAILURE) */
+ /* Not sure if it is possible to get here */
+ return -1;
}