summaryrefslogtreecommitdiffstats
path: root/src/pgrep.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pgrep.c')
-rw-r--r--src/pgrep.c1212
1 files changed, 1212 insertions, 0 deletions
diff --git a/src/pgrep.c b/src/pgrep.c
new file mode 100644
index 0000000..d8e57df
--- /dev/null
+++ b/src/pgrep.c
@@ -0,0 +1,1212 @@
+/*
+ * pgrep/pkill -- utilities to filter the process table
+ *
+ * Copyright © 2009-2023 Craig Small <csmall@dropbear.xyz>
+ * Copyright © 2013-2023 Jim Warner <james.warner@comcast.net>
+ * Copyright © 2011-2012 Sami Kerola <kerolasa@iki.fi>
+ * Copyright © 2012 Roberto Polli <rpolli@babel.it>
+ * Copyright © 2002-2007 Albert Cahalan
+ * Copyright © 2000 Kjetil Torgrim Homme <kjetilho@ifi.uio.no>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <unistd.h>
+#include <ctype.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
+#include <regex.h>
+#include <errno.h>
+#include <getopt.h>
+#include <stdbool.h>
+#include <time.h>
+
+#ifdef ENABLE_PIDWAIT
+#include <sys/epoll.h>
+#ifndef HAVE_PIDFD_OPEN
+#include <sys/syscall.h>
+#endif /* !HAVE_PIDFD_OPEN */
+#endif
+
+/* EXIT_SUCCESS is 0 */
+/* EXIT_FAILURE is 1 */
+#define EXIT_USAGE 2
+#define EXIT_FATAL 3
+#define XALLOC_EXIT_CODE EXIT_FATAL
+
+#include "c.h"
+#include "fileutils.h"
+#include "nls.h"
+#include "signals.h"
+#include "xalloc.h"
+
+#include "misc.h"
+#include "pids.h"
+
+enum pids_item Items[] = {
+ PIDS_ID_PID,
+ PIDS_ID_PPID,
+ PIDS_ID_PGRP,
+ PIDS_ID_EUID,
+ PIDS_ID_RUID,
+ PIDS_ID_RGID,
+ PIDS_ID_SESSION,
+ PIDS_ID_TGID,
+ PIDS_TICS_BEGAN,
+ PIDS_TTY_NAME,
+ PIDS_CMD,
+ PIDS_CMDLINE,
+ PIDS_STATE,
+ PIDS_TIME_ELAPSED,
+ PIDS_CGROUP_V,
+ PIDS_SIGCATCH
+};
+enum rel_items {
+ EU_PID, EU_PPID, EU_PGRP, EU_EUID, EU_RUID, EU_RGID, EU_SESSION,
+ EU_TGID, EU_STARTTIME, EU_TTYNAME, EU_CMD, EU_CMDLINE, EU_STA, EU_ELAPSED,
+ EU_CGROUP, EU_SIGCATCH
+};
+#define grow_size(x) do { \
+ if ((x) < 0 || (size_t)(x) >= INT_MAX / 5 / sizeof(struct el)) \
+ xerrx(EXIT_FAILURE, _("integer overflow")); \
+ (x) = (x) * 5 / 4 + 4; \
+} while (0)
+
+static enum {
+ PGREP = 0,
+ PKILL,
+#ifdef ENABLE_PIDWAIT
+ PIDWAIT,
+#endif
+} prog_mode;
+
+struct el {
+ long num;
+ char * str;
+};
+
+/* User supplied arguments */
+
+static int opt_full = 0;
+static int opt_long = 0;
+static int opt_longlong = 0;
+static int opt_oldest = 0;
+static int opt_older = 0;
+static int opt_newest = 0;
+static int opt_negate = 0;
+static int opt_exact = 0;
+static int opt_count = 0;
+static int opt_signal = SIGTERM;
+static int opt_lock = 0;
+static int opt_case = 0;
+static int opt_echo = 0;
+static int opt_threads = 0;
+static pid_t opt_ns_pid = 0;
+static bool use_sigqueue = false;
+static bool require_handler = false;
+static union sigval sigval = {0};
+
+static const char *opt_delim = "\n";
+static struct el *opt_pgrp = NULL;
+static struct el *opt_rgid = NULL;
+static struct el *opt_pid = NULL;
+static struct el *opt_ppid = NULL;
+static struct el *opt_ignore_ancestors = NULL;
+static struct el *opt_sid = NULL;
+static struct el *opt_term = NULL;
+static struct el *opt_euid = NULL;
+static struct el *opt_ruid = NULL;
+static struct el *opt_nslist = NULL;
+static struct el *opt_cgroup = NULL;
+static char *opt_pattern = NULL;
+static char *opt_pidfile = NULL;
+static char *opt_runstates = NULL;
+
+/* by default, all namespaces will be checked */
+static int ns_flags = 0x3f;
+
+static int __attribute__ ((__noreturn__)) usage(int opt)
+{
+ int err = (opt == '?');
+ FILE *fp = err ? stderr : stdout;
+
+ fputs(USAGE_HEADER, fp);
+ fprintf(fp, _(" %s [options] <pattern>\n"), program_invocation_short_name);
+ fputs(USAGE_OPTIONS, fp);
+ 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);
+ break;
+ case PKILL:
+ fputs(_(" -<sig> signal to send (either number or name)\n"), fp);
+ fputs(_(" -H, --require-handler match only if signal handler is present\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_PIDWAIT
+ case PIDWAIT:
+ 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);
+ fputs(_(" -g, --pgroup <PGID,...> match listed process group IDs\n"), fp);
+ fputs(_(" -G, --group <GID,...> match real group IDs\n"), fp);
+ fputs(_(" -i, --ignore-case match case insensitively\n"), fp);
+ fputs(_(" -n, --newest select most recently started\n"), fp);
+ fputs(_(" -o, --oldest select least recently started\n"), fp);
+ fputs(_(" -O, --older <seconds> select where older than seconds\n"), fp);
+ fputs(_(" -P, --parent <PPID,...> match only child processes of the given parent\n"), fp);
+ fputs(_(" -s, --session <SID,...> match session IDs\n"), fp);
+ fputs(_(" --signal <sig> signal to send (either number or name)\n"), fp);
+ fputs(_(" -t, --terminal <tty,...> match by controlling terminal\n"), fp);
+ fputs(_(" -u, --euid <ID,...> match by effective IDs\n"), fp);
+ fputs(_(" -U, --uid <ID,...> match by real IDs\n"), fp);
+ fputs(_(" -x, --exact match exactly with the command name\n"), fp);
+ fputs(_(" -F, --pidfile <file> read PIDs from file\n"), fp);
+ fputs(_(" -L, --logpidfile fail if PID file is not locked\n"), fp);
+ fputs(_(" -r, --runstates <state> match runstates [D,S,Z,...]\n"), fp);
+ fputs(_(" -A, --ignore-ancestors exclude our ancestors from results\n"), fp);
+ fputs(_(" --cgroup <grp,...> match by cgroup v2 names\n"), fp);
+ fputs(_(" --ns <PID> match the processes that belong to the same\n"
+ " namespace as <pid>\n"), fp);
+ fputs(_(" --nslist <ns,...> list which namespaces will be considered for\n"
+ " the --ns option.\n"
+ " Available namespaces: ipc, mnt, net, pid, user, uts\n"), fp);
+ fputs(USAGE_SEPARATOR, fp);
+ fputs(USAGE_HELP, fp);
+ fputs(USAGE_VERSION, fp);
+ fprintf(fp, USAGE_MAN_TAIL("pgrep(1)"));
+
+ exit(fp == stderr ? EXIT_USAGE : EXIT_SUCCESS);
+}
+
+static struct el *get_our_ancestors(void)
+{
+#define PIDS_GETINT(e) PIDS_VAL(EU_##e, s_int, stack, info)
+ struct el *list = NULL;
+ int i = 0;
+ int size = 0;
+ int done = 0;
+ pid_t search_pid = getpid();
+ struct pids_stack *stack;
+
+ while (!done) {
+ struct pids_info *info = NULL;
+
+ if (procps_pids_new(&info, Items, 16) < 0)
+ xerrx(EXIT_FATAL, _("Unable to create pid info structure"));
+
+ if (i == size) {
+ grow_size(size);
+ list = xrealloc(list, (1 + size) * sizeof(*list));
+ }
+
+ done = 1;
+ while ((stack = procps_pids_get(info, PIDS_FETCH_TASKS_ONLY))) {
+ if (PIDS_GETINT(PID) == search_pid) {
+ list[++i].num = PIDS_GETINT(PPID);
+ search_pid = list[i].num;
+ done = 0;
+ break;
+ }
+ }
+
+ procps_pids_unref(&info);
+ }
+
+ if (i == 0) {
+ free(list);
+ list = NULL;
+ } else {
+ list[0].num = i;
+ }
+ return list;
+#undef PIDS_GETINT
+}
+
+static struct el *split_list (const char *restrict str, int (*convert)(const char *, struct el *))
+{
+ char *copy;
+ char *ptr;
+ char *sep_pos;
+ int i = 0;
+ int size = 0;
+ struct el *list = NULL;
+
+ if (str[0] == '\0')
+ return NULL;
+
+ copy = xstrdup (str);
+ ptr = copy;
+
+ do {
+ if (i == size) {
+ grow_size(size);
+ /* add 1 because slot zero is a count */
+ list = xrealloc (list, (1 + size) * sizeof *list);
+ }
+ sep_pos = strchr (ptr, ',');
+ if (sep_pos)
+ *sep_pos = 0;
+ /* Use ++i instead of i++ because slot zero is a count */
+ if (list && !convert (ptr, &list[++i]))
+ exit (EXIT_USAGE);
+ if (sep_pos)
+ ptr = sep_pos + 1;
+ } while (sep_pos);
+
+ free (copy);
+ if (!i) {
+ free (list);
+ list = NULL;
+ } else {
+ list[0].num = i;
+ }
+ return list;
+}
+
+/* strict_atol returns a Boolean: TRUE if the input string
+ * contains a plain number, FALSE if there are any non-digits. */
+static int strict_atol (const char *restrict str, long *restrict value)
+{
+ long res = 0;
+ long sign = 1;
+
+ if (*str == '+')
+ ++str;
+ else if (*str == '-') {
+ ++str;
+ sign = -1;
+ }
+
+ for ( ; *str; ++str) {
+ if (! isdigit (*str))
+ return 0;
+ if (res >= LONG_MAX / 10)
+ return 0;
+ res *= 10;
+ if (res >= LONG_MAX - (*str - '0'))
+ return 0;
+ res += *str - '0';
+ }
+ *value = sign * res;
+ return 1;
+}
+
+#include <sys/file.h>
+
+/* We try a read lock. The daemon should have a write lock.
+ * Seen using flock: FreeBSD code */
+static int has_flock(int fd)
+{
+ return flock(fd, LOCK_SH|LOCK_NB)==-1 && errno==EWOULDBLOCK;
+}
+
+/* We try a read lock. The daemon should have a write lock.
+ * Seen using fcntl: libslack */
+static int has_fcntl(int fd)
+{
+ struct flock f; /* seriously, struct flock is for a fnctl lock! */
+ f.l_type = F_RDLCK;
+ f.l_whence = SEEK_SET;
+ f.l_start = 0;
+ f.l_len = 0;
+ return fcntl(fd,F_SETLK,&f)==-1 && (errno==EACCES || errno==EAGAIN);
+}
+
+static struct el *read_pidfile(void)
+{
+ char buf[12];
+ int fd;
+ struct stat sbuf;
+ char *endp;
+ int n, pid;
+ struct el *list = NULL;
+
+ fd = open(opt_pidfile, O_RDONLY|O_NOCTTY|O_NONBLOCK);
+ if(fd<0)
+ goto just_ret;
+ if(fstat(fd,&sbuf) || !S_ISREG(sbuf.st_mode) || sbuf.st_size<1)
+ goto out;
+ /* type of lock, if any, is not standardized on Linux */
+ if(opt_lock && !has_flock(fd) && !has_fcntl(fd))
+ goto out;
+ memset(buf,'\0',sizeof buf);
+ n = read(fd,buf,sizeof buf-1);
+ if (n<1)
+ goto out;
+ pid = strtoul(buf,&endp,10);
+ if(endp<=buf || pid<1 )
+ goto out;
+ if(*endp && !isspace(*endp))
+ goto out;
+ list = xmalloc(2 * sizeof *list);
+ list[0].num = 1;
+ list[1].num = pid;
+out:
+ close(fd);
+just_ret:
+ return list;
+}
+
+static int conv_uid (const char *restrict name, struct el *restrict e)
+{
+ struct passwd *pwd;
+
+ if (strict_atol (name, &e->num))
+ return (1);
+
+ pwd = getpwnam (name);
+ if (pwd == NULL) {
+ xwarnx(_("invalid user name: %s"), name);
+ return 0;
+ }
+ e->num = pwd->pw_uid;
+ return 1;
+}
+
+
+static int conv_gid (const char *restrict name, struct el *restrict e)
+{
+ struct group *grp;
+
+ if (strict_atol (name, &e->num))
+ return 1;
+
+ grp = getgrnam (name);
+ if (grp == NULL) {
+ xwarnx(_("invalid group name: %s"), name);
+ return 0;
+ }
+ e->num = grp->gr_gid;
+ return 1;
+}
+
+
+static int conv_pgrp (const char *restrict name, struct el *restrict e)
+{
+ if (! strict_atol (name, &e->num)) {
+ xwarnx(_("invalid process group: %s"), name);
+ return 0;
+ }
+ if (e->num == 0)
+ e->num = getpgrp ();
+ return 1;
+}
+
+
+static int conv_sid (const char *restrict name, struct el *restrict e)
+{
+ if (! strict_atol (name, &e->num)) {
+ xwarnx(_("invalid session id: %s"), name);
+ return 0;
+ }
+ if (e->num == 0)
+ e->num = getsid (0);
+ return 1;
+}
+
+
+static int conv_num (const char *restrict name, struct el *restrict e)
+{
+ if (! strict_atol (name, &e->num)) {
+ xwarnx(_("not a number: %s"), name);
+ return 0;
+ }
+ return 1;
+}
+
+
+static int conv_str (const char *restrict name, struct el *restrict e)
+{
+ e->str = xstrdup (name);
+ return 1;
+}
+
+
+static int conv_ns (const char *restrict name, struct el *restrict e)
+{
+ int rc = conv_str(name, e);
+ int id;
+
+ ns_flags = 0;
+ id = procps_ns_get_id(name);
+ if (id < 0)
+ return 0;
+ ns_flags |= (1 << id);
+
+ return rc;
+}
+
+static int match_numlist (long value, const struct el *restrict list)
+{
+ int found = 0;
+ if (list != NULL) {
+ int i;
+ for (i = list[0].num; i > 0; i--) {
+ if (list[i].num == value) {
+ found = 1;
+ break;
+ }
+ }
+ }
+ return found;
+}
+
+static unsigned long long unhex (const char *restrict in)
+{
+ unsigned long long ret;
+ char *rem;
+ errno = 0;
+ ret = strtoull(in, &rem, 16);
+ if (errno || *rem != '\0') {
+ xwarnx(_("not a hex string: %s"), in);
+ return 0;
+ }
+ return ret;
+}
+
+static int match_signal_handler (const char *restrict sigcgt, const int signal)
+{
+ return sigcgt && (((1UL << (signal - 1)) & unhex(sigcgt)) != 0);
+}
+
+static int match_strlist (const char *restrict value, const struct el *restrict list)
+{
+ int found = 0;
+ if (list != NULL) {
+ int i;
+ for (i = list[0].num; i > 0; i--) {
+ if (! strcmp (list[i].str, value)) {
+ found = 1;
+ break;
+ }
+ }
+ }
+ return found;
+}
+
+static int match_ns (const int pid,
+ const struct procps_ns *match_ns)
+{
+ struct procps_ns proc_ns;
+ int found = 1;
+ int i;
+
+ if (procps_ns_read_pid(pid, &proc_ns) < 0)
+ xerrx(EXIT_FATAL,
+ _("Unable to read process namespace information"));
+ for (i = 0; i < PROCPS_NS_COUNT; i++) {
+ if (ns_flags & (1 << i)) {
+ if (proc_ns.ns[i] != match_ns->ns[i]) {
+ found = 0;
+ break;
+ }
+ }
+ }
+ return found;
+}
+
+static int cgroup_cmp(const char *restrict cgroup,
+ const char *restrict path)
+{
+ if (cgroup == NULL || path == NULL)
+ return 1;
+ // Cgroup v2 have 0::
+ if (strncmp("0::", cgroup, 3) == 0) {
+ return strcmp(cgroup+3, path);
+ } //might try for cgroup v1 later
+ return 1;
+}
+
+
+static int match_cgroup_list(char **values,
+ const struct el *restrict list)
+{
+ if (list != NULL && values != NULL) {
+ int i, j;
+ for (i = list[0].num; i > 0; i--) {
+ for (j=0; values[j] && values[j][0]; j++) {
+ if (! cgroup_cmp (values[j], list[i].str)) {
+ return 1;
+ }
+ }
+ }
+ }
+ return 0;
+}
+
+static void output_numlist (const struct el *restrict list, int num)
+{
+ int i;
+ const char *delim = opt_delim;
+ for (i = 0; i < num; i++) {
+ if(i+1==num)
+ delim = "\n";
+ printf ("%ld%s", list[i].num, delim);
+ }
+}
+
+static void output_strlist (const struct el *restrict list, int num)
+{
+/* FIXME: escape codes */
+ int i;
+ const char *delim = opt_delim;
+ for (i = 0; i < num; i++) {
+ if(i+1==num)
+ delim = "\n";
+ printf ("%lu %s%s", list[i].num, list[i].str, delim);
+ }
+}
+
+static regex_t * do_regcomp (void)
+{
+ regex_t *preg = NULL;
+
+ if (opt_pattern) {
+ char *re;
+ char errbuf[256];
+ int re_err;
+
+ preg = xmalloc (sizeof (regex_t));
+ if (opt_exact) {
+ re = xmalloc (strlen (opt_pattern) + 5);
+ sprintf (re, "^(%s)$", opt_pattern);
+ } else {
+ re = opt_pattern;
+ }
+
+ re_err = regcomp (preg, re, REG_EXTENDED | REG_NOSUB | opt_case);
+
+ if (opt_exact) free(re);
+
+ if (re_err) {
+ regerror (re_err, preg, errbuf, sizeof(errbuf));
+ xerrx(EXIT_USAGE, _("regex error: %s"), errbuf);
+ }
+ }
+ return preg;
+}
+
+/*
+ * SC_ARG_MAX used to return the maximum size a command line can be
+ * however changes to the kernel mean this can be bigger than we can
+ * alloc. Clamp it to 128kB like xargs and friends do
+ * Should also not be smaller than POSIX_ARG_MAX which is 4096
+ */
+static size_t get_arg_max(void)
+{
+#define MIN_ARG_SIZE 4096u
+#define MAX_ARG_SIZE (128u * 1024u)
+
+ size_t val = sysconf(_SC_ARG_MAX);
+
+ if (val < MIN_ARG_SIZE)
+ val = MIN_ARG_SIZE;
+ if (val > MAX_ARG_SIZE)
+ val = MAX_ARG_SIZE;
+
+ return val;
+}
+
+/*
+ * Check if we have a long simple (non-regex) match
+ * Returns true if the string:
+ * 1) is longer than 15 characters
+ * 2) Doesn't have | or [ which are used by regex
+ * This is not an exhaustive list but catches most instances
+ * It's only used to suppress the warning
+ */
+static bool is_long_match(const char *str)
+{
+ int i, len;
+
+ if (str == NULL)
+ return FALSE;
+ if (15 >= (len = strlen(str)))
+ return FALSE;
+ for (i=0; i<len; i++)
+ if (str[i] == '|' || str[i] == '[')
+ return FALSE;
+ return TRUE;
+}
+static struct el * select_procs (int *num)
+{
+#define PIDS_GETINT(e) PIDS_VAL(EU_ ## e, s_int, stack, info)
+#define PIDS_GETUNT(e) PIDS_VAL(EU_ ## e, u_int, stack, info)
+#define PIDS_GETULL(e) PIDS_VAL(EU_ ## e, ull_int, stack, info)
+#define PIDS_GETSTR(e) PIDS_VAL(EU_ ## e, str, stack, info)
+#define PIDS_GETSCH(e) PIDS_VAL(EU_ ## e, s_ch, stack, info)
+#define PIDS_GETSTV(e) PIDS_VAL(EU_ ## e, strv, stack, info)
+#define PIDS_GETFLT(e) PIDS_VAL(EU_ ## e, real, stack, info)
+ struct pids_info *info=NULL;
+ struct procps_ns nsp;
+ struct pids_stack *stack;
+ unsigned long long saved_start_time; /* for new/old support */
+ int saved_pid = 0; /* for new/old support */
+ int matches = 0;
+ int size = 0;
+ regex_t *preg;
+ pid_t myself = getpid();
+ struct el *list = NULL;
+ long cmdlen = get_arg_max() * sizeof(char);
+ char *cmdline = xmalloc(cmdlen);
+ char *cmdsearch = xmalloc(cmdlen);
+ char *cmdoutput = xmalloc(cmdlen);
+ char *task_cmdline;
+ enum pids_fetch_type which;
+
+ preg = do_regcomp();
+
+ if (opt_newest) saved_start_time = 0ULL;
+ else saved_start_time = ~0ULL;
+
+ if (opt_newest) saved_pid = 0;
+ if (opt_oldest) saved_pid = INT_MAX;
+ if (opt_ns_pid && procps_ns_read_pid(opt_ns_pid, &nsp) < 0) {
+ xerrx(EXIT_FATAL,
+ _("Error reading reference namespace information\n"));
+ }
+
+ if (procps_pids_new(&info, Items, 16) < 0)
+ xerrx(EXIT_FATAL,
+ _("Unable to create pid info structure"));
+ which = PIDS_FETCH_TASKS_ONLY;
+ // pkill and pidwait don't support -w, but this is checked in getopt
+ if (opt_threads)
+ which = PIDS_FETCH_THREADS_TOO;
+
+ while ((stack = procps_pids_get(info, which))) {
+ int match = 1;
+
+ if (PIDS_GETINT(PID) == myself)
+ continue;
+ else if (opt_ignore_ancestors && match_numlist(PIDS_GETINT(PID), opt_ignore_ancestors))
+ continue;
+ else if (opt_newest && PIDS_GETULL(STARTTIME) < saved_start_time)
+ match = 0;
+ else if (opt_oldest && PIDS_GETULL(STARTTIME) > saved_start_time)
+ match = 0;
+ else if (opt_ppid && ! match_numlist(PIDS_GETINT(PPID), opt_ppid))
+ match = 0;
+ else if (opt_pid && ! match_numlist (PIDS_GETINT(TGID), opt_pid))
+ match = 0;
+ else if (opt_pgrp && ! match_numlist (PIDS_GETINT(PGRP), opt_pgrp))
+ match = 0;
+ else if (opt_euid && ! match_numlist (PIDS_GETUNT(EUID), opt_euid))
+ match = 0;
+ else if (opt_ruid && ! match_numlist (PIDS_GETUNT(RUID), opt_ruid))
+ match = 0;
+ else if (opt_rgid && ! match_numlist (PIDS_GETUNT(RGID), opt_rgid))
+ match = 0;
+ else if (opt_sid && ! match_numlist (PIDS_GETINT(SESSION), opt_sid))
+ match = 0;
+ else if (opt_ns_pid && ! match_ns (PIDS_GETINT(PID), &nsp))
+ match = 0;
+ else if (opt_older && (int)PIDS_GETFLT(ELAPSED) < opt_older)
+ match = 0;
+ else if (opt_term && ! match_strlist(PIDS_GETSTR(TTYNAME), opt_term))
+ match = 0;
+ else if (opt_runstates && ! strchr(opt_runstates, PIDS_GETSCH(STA)))
+ match = 0;
+ else if (opt_cgroup && ! match_cgroup_list (PIDS_GETSTV(CGROUP), opt_cgroup))
+ match = 0;
+ else if (require_handler && ! match_signal_handler (PIDS_GETSTR(SIGCATCH), opt_signal))
+ match = 0;
+
+ task_cmdline = PIDS_GETSTR(CMDLINE);
+
+ if (opt_long || opt_longlong || (match && opt_pattern)) {
+ if (opt_longlong)
+ strncpy (cmdoutput, task_cmdline, cmdlen -1);
+ else
+ strncpy (cmdoutput, PIDS_GETSTR(CMD), cmdlen -1);
+ cmdoutput[cmdlen - 1] = '\0';
+ }
+
+ if (match && opt_pattern) {
+ if (opt_full)
+ strncpy (cmdsearch, task_cmdline, cmdlen -1);
+ else
+ strncpy (cmdsearch, PIDS_GETSTR(CMD), cmdlen -1);
+ cmdsearch[cmdlen - 1] = '\0';
+
+ if (regexec (preg, cmdsearch, 0, NULL, 0) != 0)
+ match = 0;
+ }
+
+ if (match ^ opt_negate) { /* Exclusive OR is neat */
+ if (opt_newest) {
+ if (saved_start_time == PIDS_GETULL(STARTTIME) &&
+ saved_pid > PIDS_GETINT(PID))
+ continue;
+ saved_start_time = PIDS_GETULL(STARTTIME);
+ saved_pid = PIDS_GETINT(PID);
+ matches = 0;
+ }
+ if (opt_oldest) {
+ if (saved_start_time == PIDS_GETULL(STARTTIME) &&
+ saved_pid < PIDS_GETINT(PID))
+ continue;
+ saved_start_time = PIDS_GETULL(STARTTIME);
+ saved_pid = PIDS_GETINT(PID);
+ matches = 0;
+ }
+ if (matches == size) {
+ grow_size(size);
+ list = xrealloc(list, size * sizeof *list);
+ }
+ if (list && (opt_long || opt_longlong || opt_echo)) {
+ list[matches].num = PIDS_GETINT(PID);
+ list[matches++].str = xstrdup (cmdoutput);
+ } else if (list) {
+ list[matches++].num = PIDS_GETINT(PID);
+ } else {
+ xerrx(EXIT_FATAL, _("internal error"));
+ }
+ }
+ }
+ procps_pids_unref(&info);
+ free(cmdline);
+ free(cmdsearch);
+ free(cmdoutput);
+
+ if (preg) {
+ regfree(preg);
+ free(preg);
+ }
+
+ *num = matches;
+
+ if ((!matches) && (!opt_full) && is_long_match(opt_pattern))
+ xwarnx(_("pattern that searches for process name longer than 15 characters will result in zero matches\n"
+ "Try `%s -f' option to match against the complete command line."),
+ program_invocation_short_name);
+ return list;
+#undef PIDS_GETINT
+#undef PIDS_GETUNT
+#undef PIDS_GETULL
+#undef PIDS_GETSTR
+#undef PIDS_GETSTV
+}
+
+static int signal_option(int *argc, char **argv)
+{
+ int sig;
+ int i;
+ for (i = 1; i < *argc; i++) {
+ if (argv[i][0] == '-') {
+ sig = signal_name_to_number(argv[i] + 1);
+ if (-1 < sig) {
+ memmove(argv + i, argv + i + 1,
+ sizeof(char *) * (*argc - i));
+ (*argc)--;
+ return sig;
+ }
+ }
+ }
+ return -1;
+}
+
+#if defined(ENABLE_PIDWAIT) && !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] = "";
+ int opt;
+ int criteria_count = 0;
+
+ enum {
+ SIGNAL_OPTION = CHAR_MAX + 1,
+ NS_OPTION,
+ NSLIST_OPTION,
+ CGROUP_OPTION,
+ };
+ static const struct option longopts[] = {
+ {"signal", required_argument, NULL, SIGNAL_OPTION},
+ {"ignore-ancestors", no_argument, NULL, 'A'},
+ {"require-handler", no_argument, NULL, 'H'},
+ {"count", no_argument, NULL, 'c'},
+ {"cgroup", required_argument, NULL, CGROUP_OPTION},
+ {"delimiter", required_argument, NULL, 'd'},
+ {"list-name", no_argument, NULL, 'l'},
+ {"list-full", no_argument, NULL, 'a'},
+ {"full", no_argument, NULL, 'f'},
+ {"pgroup", required_argument, NULL, 'g'},
+ {"group", required_argument, NULL, 'G'},
+ {"ignore-case", no_argument, NULL, 'i'},
+ {"newest", no_argument, NULL, 'n'},
+ {"oldest", no_argument, NULL, 'o'},
+ {"older", required_argument, NULL, 'O'},
+ {"parent", required_argument, NULL, 'P'},
+ {"session", required_argument, NULL, 's'},
+ {"terminal", required_argument, NULL, 't'},
+ {"euid", required_argument, NULL, 'u'},
+ {"uid", required_argument, NULL, 'U'},
+ {"inverse", no_argument, NULL, 'v'},
+ {"lightweight", no_argument, NULL, 'w'},
+ {"exact", no_argument, NULL, 'x'},
+ {"pidfile", required_argument, NULL, 'F'},
+ {"logpidfile", no_argument, NULL, 'L'},
+ {"echo", no_argument, NULL, 'e'},
+ {"ns", required_argument, NULL, NS_OPTION},
+ {"nslist", required_argument, NULL, NSLIST_OPTION},
+ {"queue", required_argument, NULL, 'q'},
+ {"runstates", required_argument, NULL, 'r'},
+ {"help", no_argument, NULL, 'h'},
+ {"version", no_argument, NULL, 'V'},
+ {NULL, 0, NULL, 0}
+ };
+
+
+#ifdef ENABLE_PIDWAIT
+ if (strcmp (program_invocation_short_name, "pidwait") == 0 ||
+ strcmp (program_invocation_short_name, "lt-pidwait") == 0) {
+ prog_mode = PIDWAIT;
+ strcat (opts, "e");
+ } else
+#endif
+ if (strcmp (program_invocation_short_name, "pkill") == 0 ||
+ strcmp (program_invocation_short_name, "lt-pkill") == 0) {
+ int sig;
+ prog_mode = PKILL;
+ sig = signal_option(&argc, argv);
+ if (-1 < sig)
+ opt_signal = sig;
+ strcat (opts, "eq:");
+ } else {
+ strcat (opts, "lad:vw");
+ prog_mode = PGREP;
+ }
+
+ strcat (opts, "LF:cfinoxP:O:AHg:s:u:U:G:t:r:?Vh");
+
+ while ((opt = getopt_long (argc, argv, opts, longopts, NULL)) != -1) {
+ switch (opt) {
+ case SIGNAL_OPTION:
+ opt_signal = signal_name_to_number (optarg);
+ if (opt_signal == -1) {
+ if (isdigit (optarg[0]))
+ opt_signal = atoi (optarg);
+ else {
+ fprintf(stderr, _("Unknown signal \"%s\"."), optarg);
+ usage('?');
+ }
+ }
+ break;
+ case 'e':
+ opt_echo = 1;
+ break;
+/* case 'D': / * FreeBSD: print info about non-matches for debugging * /
+ * break; */
+ case 'F': /* FreeBSD: the arg is a file containing a PID to match */
+ free(opt_pidfile);
+ opt_pidfile = xstrdup (optarg);
+ ++criteria_count;
+ break;
+ case 'G': /* Solaris: match rgid/rgroup */
+ opt_rgid = split_list (optarg, conv_gid);
+ if (opt_rgid == NULL)
+ usage ('?');
+ ++criteria_count;
+ break;
+/* case 'I': / * FreeBSD: require confirmation before killing * /
+ * break; */
+/* case 'J': / * Solaris: match by project ID (name or number) * /
+ * break; */
+ case 'L': /* FreeBSD: fail if pidfile (see -F) not locked */
+ opt_lock++;
+ break;
+/* case 'M': / * FreeBSD: specify core (OS crash dump) file * /
+ * break; */
+/* case 'N': / * FreeBSD: specify alternate namelist file (for us, System.map -- but we don't need it) * /
+ * break; */
+ case 'P': /* Solaris: match by PPID */
+ opt_ppid = split_list (optarg, conv_num);
+ if (opt_ppid == NULL)
+ usage ('?');
+ ++criteria_count;
+ break;
+/* case 'S': / * FreeBSD: don't ignore the built-in kernel tasks * /
+ * break; */
+/* case 'T': / * Solaris: match by "task ID" (probably not a Linux task) * /
+ * break; */
+ case 'U': /* Solaris: match by ruid/rgroup */
+ opt_ruid = split_list (optarg, conv_uid);
+ if (opt_ruid == NULL)
+ usage ('?');
+ ++criteria_count;
+ break;
+ case 'V':
+ printf(PROCPS_NG_VERSION);
+ exit(EXIT_SUCCESS);
+/* case 'c': / * Solaris: match by contract ID * /
+ * break; */
+ case 'c':
+ opt_count = 1;
+ break;
+ case 'd': /* Solaris: change the delimiter */
+ opt_delim = xstrdup (optarg);
+ break;
+ case 'f': /* Solaris: match full process name (as in "ps -f") */
+ opt_full = 1;
+ break;
+ case 'g': /* Solaris: match pgrp */
+ opt_pgrp = split_list (optarg, conv_pgrp);
+ if (opt_pgrp == NULL)
+ usage ('?');
+ ++criteria_count;
+ break;
+ case 'i': /* FreeBSD: ignore case. OpenBSD: withdrawn. See -I. This sucks. */
+ if (opt_case)
+ usage (opt);
+ opt_case = REG_ICASE;
+ break;
+/* case 'j': / * FreeBSD: restricted to the given jail ID * /
+ * break; */
+ case 'l': /* Solaris: long output format (pgrep only) Should require -f for beyond argv[0] maybe? */
+ opt_long = 1;
+ break;
+ case 'a':
+ opt_longlong = 1;
+ break;
+ case 'A':
+ opt_ignore_ancestors = get_our_ancestors();
+ break;
+ case 'n': /* Solaris: match only the newest */
+ if (opt_oldest|opt_negate|opt_newest)
+ usage ('?');
+ opt_newest = 1;
+ ++criteria_count;
+ break;
+ case 'o': /* Solaris: match only the oldest */
+ if (opt_oldest|opt_negate|opt_newest)
+ usage ('?');
+ opt_oldest = 1;
+ ++criteria_count;
+ break;
+ case 'O':
+ opt_older = atoi (optarg);
+ ++criteria_count;
+ break;
+ case 's': /* Solaris: match by session ID -- zero means self */
+ opt_sid = split_list (optarg, conv_sid);
+ if (opt_sid == NULL)
+ usage ('?');
+ ++criteria_count;
+ break;
+ case 't': /* Solaris: match by tty */
+ opt_term = split_list (optarg, conv_str);
+ if (opt_term == NULL)
+ usage ('?');
+ ++criteria_count;
+ break;
+ case 'u': /* Solaris: match by euid/egroup */
+ opt_euid = split_list (optarg, conv_uid);
+ if (opt_euid == NULL)
+ usage ('?');
+ ++criteria_count;
+ break;
+ case 'v': /* Solaris: as in grep, invert the matching (uh... applied after selection I think) */
+ if (opt_oldest|opt_negate|opt_newest)
+ usage ('?');
+ opt_negate = 1;
+ break;
+ case 'w': // Linux: show threads (lightweight process) too
+ opt_threads = 1;
+ break;
+ /* OpenBSD -x, being broken, does a plain string */
+ case 'x': /* Solaris: use ^(regexp)$ in place of regexp (FreeBSD too) */
+ opt_exact = 1;
+ break;
+/* case 'z': / * Solaris: match by zone ID * /
+ * break; */
+ case NS_OPTION:
+ opt_ns_pid = atoi(optarg);
+ if (opt_ns_pid == 0)
+ case 'r': /* match by runstate */
+ opt_runstates = xstrdup (optarg);
+ ++criteria_count;
+ break;
+ case NSLIST_OPTION:
+ opt_nslist = split_list (optarg, conv_ns);
+ if (opt_nslist == NULL)
+ usage ('?');
+ break;
+ case 'q':
+ sigval.sival_int = atoi(optarg);
+ use_sigqueue = true;
+ break;
+ case CGROUP_OPTION:
+ opt_cgroup = split_list (optarg, conv_str);
+ if (opt_cgroup == NULL)
+ usage ('?');
+ ++criteria_count;
+ break;
+ case 'H':
+ require_handler = true;
+ ++criteria_count;
+ break;
+ case 'h':
+ case '?':
+ usage (opt);
+ break;
+ }
+ }
+
+ if(opt_lock && !opt_pidfile)
+ xerrx(EXIT_USAGE, _("-L without -F makes no sense\n"
+ "Try `%s --help' for more information."),
+ program_invocation_short_name);
+
+ if(opt_pidfile){
+ opt_pid = read_pidfile();
+ if(!opt_pid)
+ xerrx(EXIT_FAILURE, _("pidfile not valid\n"
+ "Try `%s --help' for more information."),
+ program_invocation_short_name);
+ }
+
+ if (argc - optind == 1)
+ opt_pattern = argv[optind];
+
+ else if (argc - optind > 1)
+ xerrx(EXIT_USAGE, _("only one pattern can be provided\n"
+ "Try `%s --help' for more information."),
+ program_invocation_short_name);
+ else if (criteria_count == 0)
+ xerrx(EXIT_USAGE, _("no matching criteria specified\n"
+ "Try `%s --help' for more information."),
+ program_invocation_short_name);
+}
+
+inline static int execute_kill(pid_t pid, int sig_num)
+{
+ if (use_sigqueue)
+ return sigqueue(pid, sig_num, sigval);
+ else
+ return kill(pid, sig_num);
+}
+
+int main (int argc, char **argv)
+{
+ struct el *procs;
+ int num;
+ int i;
+ int kill_count = 0;
+#ifdef ENABLE_PIDWAIT
+ 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;
+#endif
+ setlocale (LC_ALL, "");
+ bindtextdomain(PACKAGE, LOCALEDIR);
+ textdomain(PACKAGE);
+ atexit(close_stdout);
+
+ parse_opts (argc, argv);
+
+ procs = select_procs (&num);
+ 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++;
+ 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_PIDWAIT
+ case PIDWAIT:
+ 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) {
+ if (errno == ENOSYS)
+ xerrx(EXIT_FAILURE, _("pidfd_open() not implemented in Linux < 5.3"));
+ /* 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
+ }
+ /* Not sure if it is possible to get here */
+ return -1;
+}