pgrep: add pwait

This commit is contained in:
Alex Xu (Hello71) 2020-02-23 22:02:59 -05:00 committed by Craig Small
parent 03fac47bd2
commit 09327c2b77
6 changed files with 159 additions and 45 deletions

1
.gitignore vendored
View File

@ -39,6 +39,7 @@ pgrep
pidof pidof
pkill pkill
pmap pmap
pwait
procps-ng-*.tar.xz procps-ng-*.tar.xz
proc/.depend proc/.depend
proc/libprocps.la proc/libprocps.la

View File

@ -49,6 +49,9 @@ bin_PROGRAMS = \
uptime \ uptime \
vmstat \ vmstat \
w w
if BUILD_PWAIT
bin_PROGRAMS += pwait
endif
else else
usrbin_exec_PROGRAMS += \ usrbin_exec_PROGRAMS += \
ps/pscommand \ ps/pscommand \
@ -81,6 +84,10 @@ dist_man_MANS += \
sysctl.8 \ sysctl.8 \
sysctl.conf.5 \ sysctl.conf.5 \
ps/ps.1 ps/ps.1
if BUILD_PWAIT
dist_man_MANS += pwait.1
endif
endif endif
EXTRA_DIST = \ 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 pgrep_SOURCES = pgrep.c lib/fileutils.c lib/nsutils.c
pkill_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 pmap_SOURCES = pmap.c lib/fileutils.c
if BUILD_PWAIT
pwait_SOURCES = pgrep.c lib/fileutils.c lib/nsutils.c
endif
if !CYGWIN if !CYGWIN
pwdx_SOURCES = pwdx.c lib/fileutils.c pwdx_SOURCES = pwdx.c lib/fileutils.c
pwdx_LDADD= $(CYGWINFLAGS) pwdx_LDADD= $(CYGWINFLAGS)

View File

@ -10,6 +10,7 @@ AM_INIT_AUTOMAKE([foreign 1.11 subdir-objects -Wall -Wno-portability tar-pax no-
AM_SILENT_RULES([yes]) AM_SILENT_RULES([yes])
AC_CONFIG_SRCDIR([free.c]) AC_CONFIG_SRCDIR([free.c])
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AC_LANG([C])
# Checks for programs. # Checks for programs.
AC_USE_SYSTEM_EXTENSIONS AC_USE_SYSTEM_EXTENSIONS
@ -125,6 +126,21 @@ AC_TRY_COMPILE([#include <errno.h>],
AC_MSG_RESULT(yes), AC_MSG_RESULT(yes),
AC_MSG_RESULT(no)) 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 dnl watch8bit must be before the AC_ARG_WITH set as it sets up ncurses
AC_SUBST([WITH_WATCH8BIT]) AC_SUBST([WITH_WATCH8BIT])
AC_ARG_ENABLE([watch8bit], AC_ARG_ENABLE([watch8bit],

39
pgrep.1
View File

@ -9,13 +9,16 @@
.\" .\"
.TH PGREP "1" "2020-06-04" "procps-ng" "User Commands" .TH PGREP "1" "2020-06-04" "procps-ng" "User Commands"
.SH NAME .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 .SH SYNOPSIS
.B pgrep .B pgrep
[options] pattern [options] pattern
.br .br
.B pkill .B pkill
[options] pattern [options] pattern
.br
.B pwait
[options] pattern
.SH DESCRIPTION .SH DESCRIPTION
.B pgrep .B pgrep
looks through the currently running processes and lists the process IDs which looks through the currently running processes and lists the process IDs which
@ -41,6 +44,9 @@ OR
will send the specified signal (by default will send the specified signal (by default
.BR SIGTERM ) .BR SIGTERM )
to each process instead of listing them on stdout. 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 .SH OPTIONS
.TP .TP
\fB\-\fR\fIsignal\fP \fB\-\fR\fIsignal\fP
@ -54,7 +60,9 @@ only.)
\fB\-c\fR, \fB\-\-count\fR \fB\-c\fR, \fB\-\-count\fR
Suppress normal output; instead print a count of matching processes. When Suppress normal output; instead print a count of matching processes. When
count does not match anything, e.g. returns zero, the command will return 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 .TP
\fB\-d\fR, \fB\-\-delimiter\fR \fIdelimiter\fP \fB\-d\fR, \fB\-\-delimiter\fR \fIdelimiter\fP
Sets the string used to delimit each process ID in the output (by default a 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,... \fB\-g\fR, \fB\-\-pgroup\fR \fIpgrp\fP,...
Only match processes in the process group IDs listed. Process group 0 is Only match processes in the process group IDs listed. Process group 0 is
translated into translated into
.BR pgrep 's .BR pgrep 's,
.BR pkill 's,
or or
.BR pkill 's .BR pwait 's
own process group. own process group.
.TP .TP
\fB\-G\fR, \fB\-\-group\fR \fIgid\fP,... \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,... \fB\-s\fR, \fB\-\-session\fR \fIsid\fP,...
Only match processes whose process session ID is listed. Session ID 0 Only match processes whose process session ID is listed. Session ID 0
is translated into is translated into
.BR pgrep 's .BR pgrep 's,
.BR pkill 's,
or or
.BR pkill 's .BR pwait 's
own session ID. own session ID.
.TP .TP
\fB\-t\fR, \fB\-\-terminal\fR \fIterm\fP,... \fB\-t\fR, \fB\-\-terminal\fR \fIterm\fP,...
@ -134,6 +144,8 @@ symbolical value may be used.
\fB\-v\fR, \fB\-\-inverse\fR\fR \fB\-v\fR, \fB\-\-inverse\fR\fR
Negates the matching. This option is usually used in Negates the matching. This option is usually used in
.BR pgrep 's .BR pgrep 's
or
.BR pwait 's
context. In context. In
.BR pkill 's .BR pkill 's
context the short option is disabled to avoid accidental usage of the option. 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 \fB\-w\fR, \fB\-\-lightweight\fR\fR
Shows all thread ids instead of pids in Shows all thread ids instead of pids in
.BR pgrep 's .BR pgrep 's
or
.BR pwait 's
context. In context. In
.BR pkill 's .BR pkill 's
context this option is disabled. context this option is disabled.
@ -152,8 +166,8 @@ match the
.IR pattern . .IR pattern .
.TP .TP
\fB\-F\fR, \fB\-\-pidfile\fR \fIfile\fR \fB\-F\fR, \fB\-\-pidfile\fR \fIfile\fR
Read \fIPID\fRs from \fIfile\fR. This option is perhaps more useful for Read \fIPID\fRs from \fIfile\fR. This option is more useful for
.B pkill .BR pkill or pwait
than than
.BR pgrep . .BR pgrep .
.TP .TP
@ -223,8 +237,8 @@ $ renice +4 $(pgrep chrome)
.PD 0 .PD 0
.TP .TP
0 0
One or more processes matched the criteria. For pkill the process must also One or more processes matched the criteria. For pkill and pwait, one or more
have been successfully signalled. processes must also have been successfully signalled or waited for.
.TP .TP
1 1
No processes matched or none of them could be signalled. 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. complete command line, /proc/\fIpid\fP/cmdline.
.PP .PP
The running The running
.B pgrep .BR pgrep ,
.BR pkill ,
or or
.B pkill .B pwait
process will never report itself as a process will never report itself as a
match. match.
.SH BUGS .SH BUGS

119
pgrep.c
View File

@ -38,6 +38,11 @@
#include <stdbool.h> #include <stdbool.h>
#include <time.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_SUCCESS is 0 */
/* EXIT_FAILURE is 1 */ /* EXIT_FAILURE is 1 */
#define EXIT_USAGE 2 #define EXIT_USAGE 2
@ -60,7 +65,13 @@
(x) = (x) * 5 / 4 + 4; \ (x) = (x) * 5 / 4 + 4; \
} while (0) } while (0)
static int i_am_pkill = 0; static enum {
PGREP = 0,
PKILL,
#ifdef ENABLE_PWAIT
PWAIT,
#endif
} prog_mode;
struct el { struct el {
long num; long num;
@ -112,17 +123,24 @@ static int __attribute__ ((__noreturn__)) usage(int opt)
fputs(USAGE_HEADER, fp); fputs(USAGE_HEADER, fp);
fprintf(fp, _(" %s [options] <pattern>\n"), program_invocation_short_name); fprintf(fp, _(" %s [options] <pattern>\n"), program_invocation_short_name);
fputs(USAGE_OPTIONS, fp); fputs(USAGE_OPTIONS, fp);
if (i_am_pkill == 0) { switch (prog_mode) {
case PGREP:
fputs(_(" -d, --delimiter <string> specify output delimiter\n"),fp); fputs(_(" -d, --delimiter <string> specify output delimiter\n"),fp);
fputs(_(" -l, --list-name list PID and process name\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(_(" -a, --list-full list PID and full command line\n"),fp);
fputs(_(" -v, --inverse negates the matching\n"),fp); fputs(_(" -v, --inverse negates the matching\n"),fp);
fputs(_(" -w, --lightweight list all TID\n"), fp); fputs(_(" -w, --lightweight list all TID\n"), fp);
} break;
if (i_am_pkill == 1) { case PKILL:
fputs(_(" -<sig>, --signal <sig> signal to send (either number or name)\n"), fp); 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(_(" -q, --queue <value> integer value to be sent with the signal\n"), fp);
fputs(_(" -e, --echo display what is killed\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(_(" -c, --count count of matching processes\n"), fp);
fputs(_(" -f, --full use full process name to match\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")); xerrx(EXIT_FAILURE, _("internal error"));
} }
// pkill does not need subtasks! // pkill and pwait don't support -w, but this is checked in getopt
// this control is still done at if (opt_threads) {
// argparse time, but a further
// control is free
if (opt_threads && !i_am_pkill) {
while (readtask(ptp, &task, &subtask)){ while (readtask(ptp, &task, &subtask)){
// don't add redundant tasks // don't add redundant tasks
if (task.XXXID == subtask.XXXID) if (task.XXXID == subtask.XXXID)
@ -722,6 +737,13 @@ static int signal_option(int *argc, char **argv)
return -1; 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) static void parse_opts (int argc, char **argv)
{ {
char opts[64] = ""; char opts[64] = "";
@ -766,16 +788,21 @@ static void parse_opts (int argc, char **argv)
{NULL, 0, NULL, 0} {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; int sig;
i_am_pkill = 1; prog_mode = PKILL;
sig = signal_option(&argc, argv); sig = signal_option(&argc, argv);
if (-1 < sig) if (-1 < sig)
opt_signal = sig; opt_signal = sig;
/* These options are for pkill only */
strcat (opts, "eq:"); strcat (opts, "eq:");
} else { } else {
/* These options are for pgrep only */ prog_mode = PGREP;
strcat (opts, "lad:vw"); strcat (opts, "lad:vw");
} }
@ -974,6 +1001,14 @@ int main (int argc, char **argv)
{ {
struct el *procs; struct el *procs;
int num; 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 #ifdef HAVE_PROGRAM_INVOCATION_NAME
program_invocation_name = program_invocation_short_name; program_invocation_name = program_invocation_short_name;
@ -986,9 +1021,19 @@ int main (int argc, char **argv)
parse_opts (argc, argv); parse_opts (argc, argv);
procs = select_procs (&num); procs = select_procs (&num);
if (i_am_pkill) { switch (prog_mode) {
int i; case PGREP:
int kill_count = 0; 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++) { for (i = 0; i < num; i++) {
if (execute_kill (procs[i].num, opt_signal) != -1) { if (execute_kill (procs[i].num, opt_signal) != -1) {
if (opt_echo) if (opt_echo)
@ -1004,15 +1049,41 @@ int main (int argc, char **argv)
if (opt_count) if (opt_count)
fprintf(stdout, "%d\n", num); fprintf(stdout, "%d\n", num);
return !kill_count; return !kill_count;
} else {
if (opt_count) { #ifdef ENABLE_PWAIT
case PWAIT:
if (opt_count)
fprintf(stdout, "%d\n", num); fprintf(stdout, "%d\n", num);
} else {
if (opt_long || opt_longlong) for (i = 0; i < num; i++) {
output_strlist (procs,num); if (opt_echo)
else printf(_("waiting for %s (pid %lu)\n"), procs[i].str, procs[i].num);
output_numlist (procs,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++;
} }
return !num; /* exit(EXIT_SUCCESS) if match, otherwise exit(EXIT_FAILURE) */
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
}
/* Not sure if it is possible to get here */
return -1;
} }

1
pwait.1 Normal file
View File

@ -0,0 +1 @@
.so man1/pgrep.1