1 /* emacs settings: -*- c-basic-offset: 8 tab-width: 8 -*-
3 * pgrep/pkill -- utilities to filter the process table
5 * Copyright 2000 Kjetil Torgrim Homme <kjetilho@ifi.uio.no>
7 * May be distributed under the conditions of the
8 * GNU General Public License; a copy is in COPYING
10 * Changes by Albert Cahalan, 2002,2006.
21 #include <sys/types.h>
30 #include "proc/readproc.h"
32 #include "proc/devname.h"
33 #include "proc/sysinfo.h"
34 #include "proc/version.h" /* procps_version */
41 static int i_am_pkill = 0;
42 static const char *progname = "pgrep";
49 /* User supplied arguments */
51 static int opt_full = 0;
52 static int opt_long = 0;
53 static int opt_oldest = 0;
54 static int opt_newest = 0;
55 static int opt_negate = 0;
56 static int opt_exact = 0;
57 static int opt_signal = SIGTERM;
58 static int opt_lock = 0;
59 static int opt_case = 0;
61 static const char *opt_delim = "\n";
62 static union el *opt_pgrp = NULL;
63 static union el *opt_rgid = NULL;
64 static union el *opt_pid = NULL;
65 static union el *opt_ppid = NULL;
66 static union el *opt_sid = NULL;
67 static union el *opt_term = NULL;
68 static union el *opt_euid = NULL;
69 static union el *opt_ruid = NULL;
70 static char *opt_pattern = NULL;
71 static char *opt_pidfile = NULL;
73 static int usage (int opt) NORETURN;
74 static int usage (int opt)
76 int err = (opt=='?'); /* getopt() uses '?' to mark an error */
77 FILE *fp = err ? stderr : stdout;
80 fprintf (fp, "Usage: pkill [-SIGNAL] [-fvx] ");
82 fprintf (fp, "Usage: pgrep [-flvx] [-d DELIM] ");
83 fprintf (fp, "[-n|-o] [-P PPIDLIST] [-g PGRPLIST] [-s SIDLIST]\n"
84 "\t[-u EUIDLIST] [-U UIDLIST] [-G GIDLIST] [-t TERMLIST] "
87 exit(err ? EXIT_USAGE : EXIT_SUCCESS);
91 static union el *split_list (const char *restrict str, int (*convert)(const char *, union el *))
93 char *copy = strdup (str);
98 union el *list = NULL;
102 size = size * 5 / 4 + 4;
103 // add 1 because slot zero is a count
104 list = realloc (list, 1 + size * sizeof *list);
108 sep_pos = strchr (ptr, ',');
111 // Use ++i instead of i++ because slot zero is a count
112 if (!convert (ptr, &list[++i]))
128 // strict_atol returns a Boolean: TRUE if the input string
129 // contains a plain number, FALSE if there are any non-digits.
130 static int strict_atol (const char *restrict str, long *restrict value)
137 else if (*str == '-') {
142 for ( ; *str; ++str) {
143 if (! isdigit (*str))
152 #include <sys/file.h>
154 // Seen non-BSD code do this:
156 //if (fcntl_lock(pid_fd, F_SETLK, F_WRLCK, SEEK_SET, 0, 0) == -1)
158 int fcntl_lock(int fd, int cmd, int type, int whence, int start, int len)
160 struct flock lock[1];
163 lock->l_whence = whence;
164 lock->l_start = start;
167 return fcntl(fd, cmd, lock);
171 // We try a read lock. The daemon should have a write lock.
172 // Seen using flock: FreeBSD code
173 static int has_flock(int fd)
175 return flock(fd, LOCK_SH|LOCK_NB)==-1 && errno==EWOULDBLOCK;
178 // We try a read lock. The daemon should have a write lock.
179 // Seen using fcntl: libslack
180 static int has_fcntl(int fd)
182 struct flock f; // seriously, struct flock is for a fnctl lock!
184 f.l_whence = SEEK_SET;
187 return fcntl(fd,F_SETLK,&f)==-1 && (errno==EACCES || errno==EAGAIN);
190 static union el *read_pidfile(void)
197 union el *list = NULL;
199 fd = open(opt_pidfile, O_RDONLY|O_NOCTTY|O_NONBLOCK);
202 if(fstat(fd,&sbuf) || !S_ISREG(sbuf.st_mode) || sbuf.st_size<1)
204 // type of lock, if any, is not standardized on Linux
205 if(opt_lock && !has_flock(fd) && !has_fcntl(fd))
207 memset(buf,'\0',sizeof buf);
208 buf[read(fd,buf+1,sizeof buf-2)] = '\0';
209 pid = strtoul(buf+1,&endp,10);
210 if(endp<=buf+1 || pid<1 || pid>0x7fffffff)
212 if(*endp && !isspace(*endp))
214 list = malloc(2 * sizeof *list);
222 static int conv_uid (const char *restrict name, union el *restrict e)
226 if (strict_atol (name, &e->num))
229 pwd = getpwnam (name);
231 fprintf (stderr, "%s: invalid user name: %s\n",
235 e->num = pwd->pw_uid;
240 static int conv_gid (const char *restrict name, union el *restrict e)
244 if (strict_atol (name, &e->num))
247 grp = getgrnam (name);
249 fprintf (stderr, "%s: invalid group name: %s\n",
253 e->num = grp->gr_gid;
258 static int conv_pgrp (const char *restrict name, union el *restrict e)
260 if (! strict_atol (name, &e->num)) {
261 fprintf (stderr, "%s: invalid process group: %s\n",
271 static int conv_sid (const char *restrict name, union el *restrict e)
273 if (! strict_atol (name, &e->num)) {
274 fprintf (stderr, "%s: invalid session id: %s\n",
284 static int conv_num (const char *restrict name, union el *restrict e)
286 if (! strict_atol (name, &e->num)) {
287 fprintf (stderr, "%s: not a number: %s\n",
295 static int conv_str (const char *restrict name, union el *restrict e)
297 e->str = strdup (name);
302 static int match_numlist (long value, const union el *restrict list)
309 for (i = list[0].num; i > 0; i--) {
310 if (list[i].num == value)
317 static int match_strlist (const char *restrict value, const union el *restrict list)
324 for (i = list[0].num; i > 0; i--) {
325 if (! strcmp (list[i].str, value))
332 static void output_numlist (const union el *restrict list, int num)
335 const char *delim = opt_delim;
336 for (i = 0; i < num; i++) {
339 printf ("%ld%s", list[i].num, delim);
343 static void output_strlist (const union el *restrict list, int num)
345 // FIXME: escape codes
347 const char *delim = opt_delim;
348 for (i = 0; i < num; i++) {
351 printf ("%s%s", list[i].str, delim);
355 static PROCTAB *do_openproc (void)
360 if (opt_pattern || opt_full)
361 flags |= PROC_FILLCOM;
362 if (opt_ruid || opt_rgid)
363 flags |= PROC_FILLSTATUS;
364 if (opt_oldest || opt_newest || opt_pgrp || opt_sid || opt_term)
365 flags |= PROC_FILLSTAT;
366 if (!(flags & PROC_FILLSTAT))
367 flags |= PROC_FILLSTATUS; // FIXME: need one, and PROC_FILLANY broken
368 if (opt_euid && !opt_negate) {
369 int num = opt_euid[0].num;
371 uid_t *uids = malloc (num * sizeof (uid_t));
375 uids[i] = opt_euid[i+1].num;
378 ptp = openproc (flags, uids, num);
380 ptp = openproc (flags);
385 static regex_t * do_regcomp (void)
387 regex_t *preg = NULL;
394 preg = malloc (sizeof (regex_t));
398 re = malloc (strlen (opt_pattern) + 5);
401 sprintf (re, "^(%s)$", opt_pattern);
406 re_err = regcomp (preg, re, REG_EXTENDED | REG_NOSUB | opt_case);
408 regerror (re_err, preg, errbuf, sizeof(errbuf));
409 fputs(errbuf,stderr);
416 static union el * select_procs (int *num)
420 unsigned long long saved_start_time; // for new/old support
421 pid_t saved_pid = 0; // for new/old support
425 pid_t myself = getpid();
426 union el *list = NULL;
432 if (opt_newest) saved_start_time = 0ULL;
433 if (opt_oldest) saved_start_time = ~0ULL;
434 if (opt_newest) saved_pid = 0;
435 if (opt_oldest) saved_pid = INT_MAX;
437 memset(&task, 0, sizeof (task));
438 while(readproc(ptp, &task)) {
441 if (task.XXXID == myself)
443 else if (opt_newest && task.start_time < saved_start_time)
445 else if (opt_oldest && task.start_time > saved_start_time)
447 else if (opt_ppid && ! match_numlist (task.ppid, opt_ppid))
449 else if (opt_pid && ! match_numlist (task.tgid, opt_pid))
451 else if (opt_pgrp && ! match_numlist (task.pgrp, opt_pgrp))
453 else if (opt_euid && ! match_numlist (task.euid, opt_euid))
455 else if (opt_ruid && ! match_numlist (task.ruid, opt_ruid))
457 else if (opt_rgid && ! match_numlist (task.rgid, opt_rgid))
459 else if (opt_sid && ! match_numlist (task.session, opt_sid))
466 dev_to_tty (tty, sizeof(tty) - 1,
467 task.tty, task.XXXID, ABBREV_DEV);
468 match = match_strlist (tty, opt_term);
471 if (opt_long || (match && opt_pattern)) {
472 if (opt_full && task.cmdline) {
474 int bytes = sizeof (cmd) - 1;
476 /* make sure it is always NUL-terminated */
478 /* make room for SPC in loop below */
481 strncpy (cmd, task.cmdline[i], bytes);
482 bytes -= strlen (task.cmdline[i++]);
483 while (task.cmdline[i] && bytes > 0) {
484 strncat (cmd, " ", bytes);
485 strncat (cmd, task.cmdline[i], bytes);
486 bytes -= strlen (task.cmdline[i++]) + 1;
489 strcpy (cmd, task.cmd);
493 if (match && opt_pattern) {
494 if (regexec (preg, cmd, 0, NULL, 0) != 0)
498 if (match ^ opt_negate) { /* Exclusive OR is neat */
500 if (saved_start_time == task.start_time &&
501 saved_pid > task.XXXID)
503 saved_start_time = task.start_time;
504 saved_pid = task.XXXID;
508 if (saved_start_time == task.start_time &&
509 saved_pid < task.XXXID)
511 saved_start_time = task.start_time;
512 saved_pid = task.XXXID;
515 if (matches == size) {
516 size = size * 5 / 4 + 4;
517 list = realloc(list, size * sizeof *list);
522 char buff[5096]; // FIXME
523 sprintf (buff, "%d %s", task.XXXID, cmd);
524 list[matches++].str = strdup (buff);
526 list[matches++].num = task.XXXID;
530 memset (&task, 0, sizeof (task));
539 static void parse_opts (int argc, char **argv)
543 int criteria_count = 0;
545 if (strstr (argv[0], "pkill")) {
548 /* Look for a signal name or number as first argument */
549 if (argc > 1 && argv[1][0] == '-') {
551 sig = signal_name_to_number (argv[1] + 1);
552 if (sig == -1 && isdigit (argv[1][1]))
553 sig = atoi (argv[1] + 1);
556 for (i = 2; i < argc; i++)
563 /* These options are for pgrep only */
564 strcat (opts, "ld:");
567 strcat (opts, "LF:fnovxP:g:s:u:U:G:t:?V");
569 while ((opt = getopt (argc, argv, opts)) != -1) {
571 // case 'D': // FreeBSD: print info about non-matches for debugging
573 case 'F': // FreeBSD: the arg is a file containing a PID to match
574 opt_pidfile = strdup (optarg);
577 case 'G': // Solaris: match rgid/rgroup
578 opt_rgid = split_list (optarg, conv_gid);
579 if (opt_rgid == NULL)
583 // case 'I': // FreeBSD: require confirmation before killing
585 // case 'J': // Solaris: match by project ID (name or number)
587 case 'L': // FreeBSD: fail if pidfile (see -F) not locked
590 // case 'M': // FreeBSD: specify core (OS crash dump) file
592 // case 'N': // FreeBSD: specify alternate namelist file (for us, System.map -- but we don't need it)
594 case 'P': // Solaris: match by PPID
595 opt_ppid = split_list (optarg, conv_num);
596 if (opt_ppid == NULL)
600 // case 'S': // FreeBSD: don't ignore the built-in kernel tasks
602 // case 'T': // Solaris: match by "task ID" (probably not a Linux task)
604 case 'U': // Solaris: match by ruid/rgroup
605 opt_ruid = split_list (optarg, conv_uid);
606 if (opt_ruid == NULL)
611 fprintf(stdout, "%s (%s)\n", progname, procps_version);
613 // case 'c': // Solaris: match by contract ID
615 case 'd': // Solaris: change the delimiter
616 opt_delim = strdup (optarg);
618 case 'f': // Solaris: match full process name (as in "ps -f")
621 case 'g': // Solaris: match pgrp
622 opt_pgrp = split_list (optarg, conv_pgrp);
623 if (opt_pgrp == NULL)
627 // case 'i': // FreeBSD: ignore case. OpenBSD: withdrawn. See -I. This sucks.
630 // opt_case = REG_ICASE;
632 // case 'j': // FreeBSD: restricted to the given jail ID
634 case 'l': // Solaris: long output format (pgrep only) Should require -f for beyond argv[0] maybe?
637 case 'n': // Solaris: match only the newest
638 if (opt_oldest|opt_negate|opt_newest)
643 case 'o': // Solaris: match only the oldest
644 if (opt_oldest|opt_negate|opt_newest)
649 case 's': // Solaris: match by session ID -- zero means self
650 opt_sid = split_list (optarg, conv_sid);
655 case 't': // Solaris: match by tty
656 opt_term = split_list (optarg, conv_str);
657 if (opt_term == NULL)
661 case 'u': // Solaris: match by euid/egroup
662 opt_euid = split_list (optarg, conv_uid);
663 if (opt_euid == NULL)
667 case 'v': // Solaris: as in grep, invert the matching (uh... applied after selection I think)
668 if (opt_oldest|opt_negate|opt_newest)
672 // OpenBSD -x, being broken, does a plain string
673 case 'x': // Solaris: use ^(regexp)$ in place of regexp (FreeBSD too)
676 // case 'z': // Solaris: match by zone ID
684 if(opt_lock && !opt_pidfile){
685 fprintf(stderr, "%s: -L without -F makes no sense\n",progname);
690 opt_pid = read_pidfile();
692 fprintf(stderr, "%s: pidfile not valid\n",progname);
697 if (argc - optind == 1)
698 opt_pattern = argv[optind];
699 else if (argc - optind > 1)
701 else if (criteria_count == 0) {
702 fprintf (stderr, "%s: No matching criteria specified\n",
709 int main (int argc, char *argv[])
714 parse_opts (argc, argv);
716 procs = select_procs (&num);
719 for (i = 0; i < num; i++) {
720 if (kill (procs[i].num, opt_signal) != -1) continue;
721 if (errno==ESRCH) continue; // gone now, which is OK
722 fprintf (stderr, "pkill: %ld - %s\n",
723 procs[i].num, strerror (errno));
727 output_strlist(procs,num);
729 output_numlist(procs,num);
731 return !num; // exit(EXIT_SUCCESS) if match, otherwise exit(EXIT_FAILURE)