busybox/procps/pgrep.c
<<
>>
Prefs
   1/* vi: set sw=4 ts=4: */
   2/*
   3 * Mini pgrep/pkill implementation for busybox
   4 *
   5 * Copyright (C) 2007 Loic Grenie <loic.grenie@gmail.com>
   6 *
   7 * Licensed under GPLv2 or later, see file LICENSE in this source tree.
   8 */
   9//config:config PGREP
  10//config:       bool "pgrep (6.5 kb)"
  11//config:       default y
  12//config:       help
  13//config:       Look for processes by name.
  14//config:
  15//config:config PKILL
  16//config:       bool "pkill (7.5 kb)"
  17//config:       default y
  18//config:       help
  19//config:       Send signals to processes by name.
  20
  21//applet:IF_PGREP(APPLET_ODDNAME(pgrep, pgrep, BB_DIR_USR_BIN, BB_SUID_DROP, pgrep))
  22//                APPLET_ODDNAME:name   main   location        suid_type     help
  23//applet:IF_PKILL(APPLET_ODDNAME(pkill, pgrep, BB_DIR_USR_BIN, BB_SUID_DROP, pkill))
  24/* can't be noexec: can find _itself_ under wrong name, since after fork only,
  25 * /proc/PID/cmdline and comm are wrong! Can fix comm (prctl(PR_SET_NAME)),
  26 * but cmdline?
  27 */
  28
  29//kbuild:lib-$(CONFIG_PGREP) += pgrep.o
  30//kbuild:lib-$(CONFIG_PKILL) += pgrep.o
  31
  32//usage:#define pgrep_trivial_usage
  33//usage:       "[-flanovx] [-s SID|-P PPID|PATTERN]"
  34//usage:#define pgrep_full_usage "\n\n"
  35//usage:       "Display process(es) selected by regex PATTERN\n"
  36//usage:     "\n        -l      Show command name too"
  37//usage:     "\n        -a      Show command line too"
  38//usage:     "\n        -f      Match against entire command line"
  39//usage:     "\n        -n      Show the newest process only"
  40//usage:     "\n        -o      Show the oldest process only"
  41//usage:     "\n        -v      Negate the match"
  42//usage:     "\n        -x      Match whole name (not substring)"
  43//usage:     "\n        -s      Match session ID (0 for current)"
  44//usage:     "\n        -P      Match parent process ID"
  45//usage:
  46//usage:#define pkill_trivial_usage
  47//usage:       "[-l|-SIGNAL] [-xfvnoe] [-s SID|-P PPID|PATTERN]"
  48//usage:#define pkill_full_usage "\n\n"
  49//usage:       "Send signal to processes selected by regex PATTERN\n"
  50//usage:     "\n        -l      List all signals"
  51//usage:     "\n        -x      Match whole name (not substring)"
  52//usage:     "\n        -f      Match against entire command line"
  53//usage:     "\n        -s SID  Match session ID (0 for current)"
  54//usage:     "\n        -P PPID Match parent process ID"
  55//usage:     "\n        -v      Negate the match"
  56//usage:     "\n        -n      Signal the newest process only"
  57//usage:     "\n        -o      Signal the oldest process only"
  58//usage:     "\n        -e      Display name and PID of the process being killed"
  59
  60#include "libbb.h"
  61#include "xregex.h"
  62
  63/* Idea taken from kill.c */
  64#define pgrep (ENABLE_PGREP && (!ENABLE_PKILL || applet_name[1] == 'g'))
  65#define pkill (ENABLE_PKILL && (!ENABLE_PGREP || applet_name[1] == 'k'))
  66
  67enum {
  68        /* "vlafxones:+P:+" */
  69        OPTBIT_V = 0, /* must be first, we need OPT_INVERT = 0/1 */
  70        OPTBIT_L,
  71        OPTBIT_A,
  72        OPTBIT_F,
  73        OPTBIT_X,
  74        OPTBIT_O,
  75        OPTBIT_N,
  76        OPTBIT_E, /* should be pkill-only, do we care? */
  77        OPTBIT_S,
  78        OPTBIT_P,
  79};
  80
  81#define OPT_INVERT      (opt & (1 << OPTBIT_V))
  82#define OPT_LIST        (opt & (1 << OPTBIT_L))
  83#define OPT_LISTFULL    (opt & (1 << OPTBIT_A))
  84#define OPT_FULL        (opt & (1 << OPTBIT_F))
  85#define OPT_ANCHOR      (opt & (1 << OPTBIT_X))
  86#define OPT_FIRST       (opt & (1 << OPTBIT_O))
  87#define OPT_LAST        (opt & (1 << OPTBIT_N))
  88#define OPT_ECHO        (opt & (1 << OPTBIT_E))
  89#define OPT_SID         (opt & (1 << OPTBIT_S))
  90#define OPT_PPID        (opt & (1 << OPTBIT_P))
  91
  92static void act(unsigned pid, char *cmd, int signo)
  93{
  94        if (pgrep) {
  95                if (option_mask32 & ((1 << OPTBIT_L)|(1 << OPTBIT_A))) /* -l or -a */
  96                        printf("%u %s\n", pid, cmd);
  97                else
  98                        printf("%u\n", pid);
  99        } else {
 100                kill(pid, signo);
 101                if (option_mask32 & (1 << OPTBIT_E)) {
 102                        printf("%s killed (pid %u)\n", cmd, pid);
 103                }
 104        }
 105}
 106
 107int pgrep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 108int pgrep_main(int argc UNUSED_PARAM, char **argv)
 109{
 110        unsigned pid;
 111        int signo;
 112        unsigned opt;
 113        int scan_mask;
 114        int matched_pid;
 115        int sid2match, ppid2match;
 116        char *cmd_last;
 117        procps_status_t *proc;
 118        /* These are initialized to 0 */
 119        struct {
 120                regex_t re_buffer;
 121                regmatch_t re_match[1];
 122        } Z;
 123#define re_buffer (Z.re_buffer)
 124#define re_match  (Z.re_match )
 125
 126        memset(&Z, 0, sizeof(Z));
 127
 128        /* Parse -SIGNAL for pkill. Must be first option, if present */
 129        signo = SIGTERM;
 130        if (pkill && argv[1] && argv[1][0] == '-') {
 131                int temp = get_signum(argv[1]+1);
 132                if (temp != -1) {
 133                        signo = temp;
 134                        argv++;
 135                }
 136        }
 137
 138        /* Parse remaining options */
 139        ppid2match = -1;
 140        sid2match = -1;
 141        opt = getopt32(argv, "vlafxones:+P:+", &sid2match, &ppid2match);
 142        argv += optind;
 143
 144        if (pkill && OPT_LIST) { /* -l: print the whole signal list */
 145                print_signames();
 146                return 0;
 147        }
 148
 149        pid = getpid();
 150        if (sid2match == 0)
 151                sid2match = getsid(pid);
 152
 153        scan_mask = PSSCAN_COMM | PSSCAN_ARGV0;
 154        if (OPT_FULL)
 155                scan_mask |= PSSCAN_ARGVN;
 156
 157        /* One pattern is required, if no -s and no -P */
 158        if ((sid2match & ppid2match) < 0 && (!argv[0] || argv[1]))
 159                bb_show_usage();
 160
 161        if (argv[0])
 162                xregcomp(&re_buffer, argv[0], OPT_ANCHOR ? REG_EXTENDED : (REG_EXTENDED|REG_NOSUB));
 163
 164        matched_pid = 0;
 165        cmd_last = NULL;
 166        proc = NULL;
 167        while ((proc = procps_scan(proc, scan_mask)) != NULL) {
 168                char *cmd;
 169                int cmdlen, match;
 170
 171                if (proc->pid == pid)
 172                        continue;
 173
 174                if (!OPT_INVERT) {
 175                        /* Quickly reject -sN -PN mismatches... unless -v */
 176                        if (ppid2match >= 0 && ppid2match != proc->ppid)
 177                                continue;
 178                        if (sid2match >= 0 && sid2match != proc->sid)
 179                                continue;
 180                }
 181
 182                cmdlen = -1;
 183                cmd = proc->argv0;
 184                if (!cmd) {
 185                        cmd = proc->comm;
 186                } else {
 187                        int i = proc->argv_len;
 188
 189                        if (!OPT_LISTFULL)
 190                                cmdlen = strlen(cmd); /* not -a: find first NUL */
 191                        /*
 192                         * "sleep 11" looks like "sleep""\0""11""\0" in argv0.
 193                         * Make sure last "\0" does not get converted to " ":
 194                         */
 195                        if (i && cmd[i-1] == '\0')
 196                                i--;
 197                        while (--i >= 0) {
 198                                if ((unsigned char)cmd[i] < ' ')
 199                                        cmd[i] = ' ';
 200                        }
 201                }
 202
 203                if (OPT_INVERT) {
 204                        /* "pgrep -v -P1 firefox" means "not (ppid=1 AND name=firefox)"
 205                         * or equivalently "ppid!=1 OR name!=firefox".
 206                         * Check the first condition and if true, skip matching.
 207                         */
 208                        if (ppid2match >= 0 && ppid2match != proc->ppid)
 209                                goto got_it;
 210                        if (sid2match >= 0 && sid2match != proc->sid)
 211                                goto got_it;
 212                }
 213
 214                match = !argv[0]; /* if no PATTERN, then it's a match, else... */
 215                if (!match) {
 216 again:
 217                        match = (regexec(&re_buffer, cmd, 1, re_match, 0) == 0);
 218                        if (!match && cmd != proc->comm) {
 219                                /* if argv[] did not match, try comm */
 220                                cmdlen = -1;
 221                                cmd = proc->comm;
 222                                goto again;
 223                        }
 224                        if (match && OPT_ANCHOR) {
 225                                /* -x requires full string match */
 226                                match = (re_match[0].rm_so == 0 && cmd[re_match[0].rm_eo] == '\0');
 227                        }
 228                }
 229
 230                /* NB: OPT_INVERT is always 0 or 1 */
 231                if (match ^ OPT_INVERT) {
 232 got_it:
 233                        matched_pid = proc->pid;
 234                        if (OPT_LAST) {
 235                                free(cmd_last);
 236                                cmd_last = xstrdup(cmd);
 237                                continue;
 238                        }
 239                        if (cmdlen >= 0)
 240                                cmd[cmdlen] = '\0';
 241                        act(proc->pid, cmd, signo);
 242                        if (OPT_FIRST)
 243                                break;
 244                }
 245        }
 246
 247        if (cmd_last) {
 248                act(matched_pid, cmd_last, signo);
 249                if (ENABLE_FEATURE_CLEAN_UP)
 250                        free(cmd_last);
 251        }
 252        return matched_pid == 0; /* return 1 if no processes listed/signaled */
 253}
 254