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:
commit
980983db74
1
.gitignore
vendored
1
.gitignore
vendored
@ -39,6 +39,7 @@ pgrep
|
||||
pidof
|
||||
pkill
|
||||
pmap
|
||||
pwait
|
||||
procps-ng-*.tar.xz
|
||||
proc/.depend
|
||||
proc/libprocps.la
|
||||
|
10
Makefile.am
10
Makefile.am
@ -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
1
NEWS
@ -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
|
||||
|
16
configure.ac
16
configure.ac
@ -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
39
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
|
||||
|
137
pgrep.c
137
pgrep.c
@ -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;
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user