Merge branch 'alxu/procps-pwait'

New command, pwait! Waits for another process to finish just like
pgrep finds or pkill kills another process.

References:
 procps-ng/procps!97

Signed-off-by: Craig Small <csmall@dropbear.xyz>
This commit is contained in:
Craig Small 2020-12-22 12:16:09 +11:00
commit 980983db74
7 changed files with 160 additions and 45 deletions

1
.gitignore vendored
View File

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

View 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)

1
NEWS
View File

@ -8,6 +8,7 @@ procps-ng NEXT
* pidof: show worker threads Redhat #1803640
* ps.1: Mention stime alias issue #164
* ps: check also match on truncated 16 char comm names
* pwait: New command waits for a process merge #97
* sysctl: Match systemd directory order Debian #950788
* sysctl: Document directory order Debian #951550
* top: ensure config file backward compatibility Debian #951335

View 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],

39
pgrep.1
View File

@ -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

137
pgrep.c
View File

@ -38,6 +38,11 @@
#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
@ -60,7 +65,13 @@
(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,25 +1021,8 @@ int main (int argc, char **argv)
parse_opts (argc, argv);
procs = select_procs (&num);
if (i_am_pkill) {
int i;
int kill_count = 0;
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++;
continue;
}
if (errno==ESRCH)
/* 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 {
switch (prog_mode) {
case PGREP:
if (opt_count) {
fprintf(stdout, "%d\n", num);
} else {
@ -1013,6 +1031,59 @@ int main (int argc, char **argv)
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++;
continue;
}
if (errno==ESRCH)
/* 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;
#ifdef ENABLE_PWAIT
case PWAIT:
if (opt_count)
fprintf(stdout, "%d\n", 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;
}

1
pwait.1 Normal file
View File

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