]> granicus.if.org Git - procps-ng/commitdiff
pgrep: add pwait
authorAlex Xu (Hello71) <alex_y_xu@yahoo.ca>
Mon, 24 Feb 2020 03:02:59 +0000 (22:02 -0500)
committerCraig Small <csmall@dropbear.xyz>
Tue, 22 Dec 2020 01:09:16 +0000 (12:09 +1100)
.gitignore
Makefile.am
configure.ac
pgrep.1
pgrep.c
pwait.1 [new file with mode: 0644]

index 9d5313b6bdbd380f2a2ebc2dfa6f2dd18ba67051..da595d330e8b850e1ba37b632cde778f266828a2 100644 (file)
@@ -39,6 +39,7 @@ pgrep
 pidof
 pkill
 pmap
+pwait
 procps-ng-*.tar.xz
 proc/.depend
 proc/libprocps.la
index bcc7318dcf2574a5026bde2b8f0583699c9d0469..4a005778306d499d1aa92dd4b0cf251c7f67e193 100644 (file)
@@ -49,6 +49,9 @@ bin_PROGRAMS = \
        uptime \
        vmstat \
        w
+if BUILD_PWAIT
+bin_PROGRAMS += pwait
+endif
 else
 usrbin_exec_PROGRAMS += \
        ps/pscommand \
@@ -81,6 +84,10 @@ dist_man_MANS += \
        sysctl.8 \
        sysctl.conf.5 \
        ps/ps.1
+
+if BUILD_PWAIT
+dist_man_MANS += pwait.1
+endif
 endif
 
 EXTRA_DIST = \
@@ -192,6 +199,9 @@ free_SOURCES = free.c lib/strutils.c lib/fileutils.c
 pgrep_SOURCES = pgrep.c lib/fileutils.c lib/nsutils.c
 pkill_SOURCES = pgrep.c lib/fileutils.c lib/nsutils.c
 pmap_SOURCES = pmap.c lib/fileutils.c
+if BUILD_PWAIT
+pwait_SOURCES = pgrep.c lib/fileutils.c lib/nsutils.c
+endif
 if !CYGWIN
 pwdx_SOURCES = pwdx.c lib/fileutils.c
 pwdx_LDADD= $(CYGWINFLAGS)
index e5ed2aa890eae5d5e5487c66b7fbec4f1ab7f7f4..f29fc0646679232803f2c6254dbfe3c6d7eea509 100644 (file)
@@ -10,6 +10,7 @@ AM_INIT_AUTOMAKE([foreign 1.11 subdir-objects -Wall -Wno-portability tar-pax no-
 AM_SILENT_RULES([yes])
 AC_CONFIG_SRCDIR([free.c])
 AC_CONFIG_HEADERS([config.h])
+AC_LANG([C])
 
 # Checks for programs.
 AC_USE_SYSTEM_EXTENSIONS
@@ -125,6 +126,21 @@ AC_TRY_COMPILE([#include <errno.h>],
                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],
diff --git a/pgrep.1 b/pgrep.1
index a1810f0dfc33327f8c721f431fda813c00915ce7..4f8907b8ab41ec17742d78fe021de652e57a70e0 100644 (file)
--- a/pgrep.1
+++ b/pgrep.1
@@ -9,13 +9,16 @@
 .\"
 .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
@@ -41,6 +44,9 @@ OR
 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
@@ -54,7 +60,9 @@ only.)
 \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
@@ -77,9 +85,10 @@ is set, the full command line is used.
 \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,...
@@ -114,9 +123,10 @@ Only match processes whose parent process ID is listed.
 \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,...
@@ -134,6 +144,8 @@ symbolical value may be used.
 \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.
@@ -141,6 +153,8 @@ 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.
@@ -152,8 +166,8 @@ match the
 .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
@@ -223,8 +237,8 @@ $ renice +4 $(pgrep chrome)
 .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.
@@ -241,9 +255,10 @@ the output of /proc/\fIpid\fP/stat.  Use the \fB\-f\fR option to match against t
 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
diff --git a/pgrep.c b/pgrep.c
index 4b64c1a14b63ab29beac5a31bc0c13c06968bc23..205a1db684eb3db002fa836539e093a54c198d8f 100644 (file)
--- a/pgrep.c
+++ b/pgrep.c
 #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;
@@ -112,17 +123,24 @@ static int __attribute__ ((__noreturn__)) usage(int opt)
        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);
@@ -669,11 +687,8 @@ static struct el * select_procs (int *num)
                                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)
@@ -722,6 +737,13 @@ static int signal_option(int *argc, char **argv)
        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] = "";
@@ -766,16 +788,21 @@ static void parse_opts (int argc, char **argv)
                {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");
        }
 
@@ -974,6 +1001,14 @@ int main (int argc, char **argv)
 {
        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;
@@ -986,33 +1021,69 @@ int main (int argc, char **argv)
        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;
 }
diff --git a/pwait.1 b/pwait.1
new file mode 100644 (file)
index 0000000..0e94b52
--- /dev/null
+++ b/pwait.1
@@ -0,0 +1 @@
+.so man1/pgrep.1