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] [-xfvno] [-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
  59#include "libbb.h"
  60#include "xregex.h"
  61
  62/* Idea taken from kill.c */
  63#define pgrep (ENABLE_PGREP && (!ENABLE_PKILL || applet_name[1] == 'g'))
  64#define pkill (ENABLE_PKILL && (!ENABLE_PGREP || applet_name[1] == 'k'))
  65
  66enum {
  67        /* "vlafxons:+P:+" */
  68        OPTBIT_V = 0, /* must be first, we need OPT_INVERT = 0/1 */
  69        OPTBIT_L,
  70        OPTBIT_A,
  71        OPTBIT_F,
  72        OPTBIT_X,
  73        OPTBIT_O,
  74        OPTBIT_N,
  75        OPTBIT_S,
  76        OPTBIT_P,
  77};
  78
  79#define OPT_INVERT      (opt & (1 << OPTBIT_V))
  80#define OPT_LIST        (opt & (1 << OPTBIT_L))
  81#define OPT_LISTFULL    (opt & (1 << OPTBIT_A))
  82#define OPT_FULL        (opt & (1 << OPTBIT_F))
  83#define OPT_ANCHOR      (opt & (1 << OPTBIT_X))
  84#define OPT_FIRST       (opt & (1 << OPTBIT_O))
  85#define OPT_LAST        (opt & (1 << OPTBIT_N))
  86#define OPT_SID         (opt & (1 << OPTBIT_S))
  87#define OPT_PPID        (opt & (1 << OPTBIT_P))
  88
  89static void act(unsigned pid, char *cmd, int signo)
  90{
  91        if (pgrep) {
  92                if (option_mask32 & ((1 << OPTBIT_L)|(1 << OPTBIT_A))) /* -l or -a */
  93                        printf("%u %s\n", pid, cmd);
  94                else
  95                        printf("%u\n", pid);
  96        } else
  97                kill(pid, signo);
  98}
  99
 100int pgrep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
 101int pgrep_main(int argc UNUSED_PARAM, char **argv)
 102{
 103        unsigned pid;
 104        int signo;
 105        unsigned opt;
 106        int scan_mask;
 107        int matched_pid;
 108        int sid2match, ppid2match;
 109        char *cmd_last;
 110        procps_status_t *proc;
 111        /* These are initialized to 0 */
 112        struct {
 113                regex_t re_buffer;
 114                regmatch_t re_match[1];
 115        } Z;
 116#define re_buffer (Z.re_buffer)
 117#define re_match  (Z.re_match )
 118
 119        memset(&Z, 0, sizeof(Z));
 120
 121        /* Parse -SIGNAL for pkill. Must be first option, if present */
 122        signo = SIGTERM;
 123        if (pkill && argv[1] && argv[1][0] == '-') {
 124                int temp = get_signum(argv[1]+1);
 125                if (temp != -1) {
 126                        signo = temp;
 127                        argv++;
 128                }
 129        }
 130
 131        /* Parse remaining options */
 132        ppid2match = -1;
 133        sid2match = -1;
 134        opt = getopt32(argv, "vlafxons:+P:+", &sid2match, &ppid2match);
 135        argv += optind;
 136
 137        if (pkill && OPT_LIST) { /* -l: print the whole signal list */
 138                print_signames();
 139                return 0;
 140        }
 141
 142        pid = getpid();
 143        if (sid2match == 0)
 144                sid2match = getsid(pid);
 145
 146        scan_mask = PSSCAN_COMM | PSSCAN_ARGV0;
 147        if (OPT_FULL)
 148                scan_mask |= PSSCAN_ARGVN;
 149
 150        /* One pattern is required, if no -s and no -P */
 151        if ((sid2match & ppid2match) < 0 && (!argv[0] || argv[1]))
 152                bb_show_usage();
 153
 154        if (argv[0])
 155                xregcomp(&re_buffer, argv[0], OPT_ANCHOR ? REG_EXTENDED : (REG_EXTENDED|REG_NOSUB));
 156
 157        matched_pid = 0;
 158        cmd_last = NULL;
 159        proc = NULL;
 160        while ((proc = procps_scan(proc, scan_mask)) != NULL) {
 161                char *cmd;
 162                int cmdlen, match;
 163
 164                if (proc->pid == pid)
 165                        continue;
 166
 167                if (!OPT_INVERT) {
 168                        /* Quickly reject -sN -PN mismatches... unless -v */
 169                        if (ppid2match >= 0 && ppid2match != proc->ppid)
 170                                continue;
 171                        if (sid2match >= 0 && sid2match != proc->sid)
 172                                continue;
 173                }
 174
 175                cmdlen = -1;
 176                cmd = proc->argv0;
 177                if (!cmd) {
 178                        cmd = proc->comm;
 179                } else {
 180                        int i = proc->argv_len;
 181
 182                        if (!OPT_LISTFULL)
 183                                cmdlen = strlen(cmd); /* not -a: find first NUL */
 184                        /*
 185                         * "sleep 11" looks like "sleep""\0""11""\0" in argv0.
 186                         * Make sure last "\0" does not get converted to " ":
 187                         */
 188                        if (i && cmd[i-1] == '\0')
 189                                i--;
 190                        while (--i >= 0) {
 191                                if ((unsigned char)cmd[i] < ' ')
 192                                        cmd[i] = ' ';
 193                        }
 194                }
 195
 196                if (OPT_INVERT) {
 197                        /* "pgrep -v -P1 firefox" means "not (ppid=1 AND name=firefox)"
 198                         * or equivalently "ppid!=1 OR name!=firefox".
 199                         * Check the first condition and if true, skip matching.
 200                         */
 201                        if (ppid2match >= 0 && ppid2match != proc->ppid)
 202                                goto got_it;
 203                        if (sid2match >= 0 && sid2match != proc->sid)
 204                                goto got_it;
 205                }
 206
 207                match = !argv[0]; /* if no PATTERN, then it's a match, else... */
 208                if (!match) {
 209 again:
 210                        match = (regexec(&re_buffer, cmd, 1, re_match, 0) == 0);
 211                        if (!match && cmd != proc->comm) {
 212                                /* if argv[] did not match, try comm */
 213                                cmdlen = -1;
 214                                cmd = proc->comm;
 215                                goto again;
 216                        }
 217                        if (match && OPT_ANCHOR) {
 218                                /* -x requires full string match */
 219                                match = (re_match[0].rm_so == 0 && cmd[re_match[0].rm_eo] == '\0');
 220                        }
 221                }
 222
 223                /* NB: OPT_INVERT is always 0 or 1 */
 224                if (match ^ OPT_INVERT) {
 225 got_it:
 226                        matched_pid = proc->pid;
 227                        if (OPT_LAST) {
 228                                free(cmd_last);
 229                                cmd_last = xstrdup(cmd);
 230                                continue;
 231                        }
 232                        if (cmdlen >= 0)
 233                                cmd[cmdlen] = '\0';
 234                        act(proc->pid, cmd, signo);
 235                        if (OPT_FIRST)
 236                                break;
 237                }
 238        }
 239
 240        if (cmd_last) {
 241                act(matched_pid, cmd_last, signo);
 242                if (ENABLE_FEATURE_CLEAN_UP)
 243                        free(cmd_last);
 244        }
 245        return matched_pid == 0; /* return 1 if no processes listed/signaled */
 246}
 247