pgrep,pkill: new applets by Loic Grenie <loic.grenie@gmail.com>
authorDenis Vlasenko <vda.linux@googlemail.com>
Sat, 29 Sep 2007 22:26:01 +0000 (22:26 -0000)
committerDenis Vlasenko <vda.linux@googlemail.com>
Sat, 29 Sep 2007 22:26:01 +0000 (22:26 -0000)
include/applets.h
include/libbb.h
include/usage.h
libbb/procps.c
libbb/u_signal_names.c
procps/Config.in
procps/Kbuild
procps/kill.c
procps/pgrep.c [new file with mode: 0644]

index f162c5479226734dd5d791974ec3a19c5d1729d0..e88192d245e2e33fbea455c1f62137b99197e20a 100644 (file)
@@ -251,11 +251,13 @@ USE_OD(APPLET(od, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
 USE_OPENVT(APPLET(openvt, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
 USE_PASSWD(APPLET(passwd, _BB_DIR_USR_BIN, _BB_SUID_ALWAYS))
 USE_PATCH(APPLET(patch, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
+USE_PGREP(APPLET(pgrep, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
 USE_PIDOF(APPLET(pidof, _BB_DIR_BIN, _BB_SUID_NEVER))
 USE_PING(APPLET(ping, _BB_DIR_BIN, _BB_SUID_MAYBE))
 USE_PING6(APPLET(ping6, _BB_DIR_BIN, _BB_SUID_NEVER))
 USE_PIPE_PROGRESS(APPLET_NOUSAGE(pipe_progress, pipe_progress, _BB_DIR_BIN, _BB_SUID_NEVER))
 USE_PIVOT_ROOT(APPLET(pivot_root, _BB_DIR_SBIN, _BB_SUID_NEVER))
+USE_PKILL(APPLET_ODDNAME(pkill, pgrep, _BB_DIR_USR_BIN, _BB_SUID_NEVER, pkill))
 USE_HALT(APPLET_ODDNAME(poweroff, halt, _BB_DIR_SBIN, _BB_SUID_NEVER, poweroff))
 USE_PRINTENV(APPLET(printenv, _BB_DIR_BIN, _BB_SUID_NEVER))
 USE_PRINTF(APPLET(printf, _BB_DIR_USR_BIN, _BB_SUID_NEVER))
index a6709c95d7eb9d1fac2e268a8b35b186b5c80fee..34b978452e6e4b298170a778294ce1557275124c 100644 (file)
@@ -787,6 +787,7 @@ int bb_make_directory(char *path, long mode, int flags);
 
 int get_signum(const char *name);
 const char *get_signame(int number);
+void print_signames_and_exit(void) ATTRIBUTE_NORETURN;
 
 char *bb_simplify_path(const char *path);
 
@@ -973,7 +974,8 @@ enum {
        PSSCAN_UTIME    = 1 << 13,
        PSSCAN_TTY      = 1 << 14,
        PSSCAN_SMAPS    = (1 << 15) * ENABLE_FEATURE_TOPMEM,
-       USE_SELINUX(PSSCAN_CONTEXT = 1 << 16,)
+       PSSCAN_ARGVN    = (1 << 16) * (ENABLE_PGREP | ENABLE_PKILL),
+       USE_SELINUX(PSSCAN_CONTEXT = 1 << 17,)
        /* These are all retrieved from proc/NN/stat in one go: */
        PSSCAN_STAT     = PSSCAN_PPID | PSSCAN_PGID | PSSCAN_SID
                        | PSSCAN_COMM | PSSCAN_STATE
index 4152fecd3735e88e8ec4de4d4293786f2ede413b..d7e1dd9f1955d16b112eeffd53585a57d3e90feb 100644 (file)
        "$ patch -p1 < example.diff\n" \
        "$ patch -p0 -i example.diff"
 
+#define pgrep_trivial_usage \
+       "[-flnovx] pattern"
+#define pgrep_full_usage \
+       "Display process(es) selected by regex pattern" \
+       "\n\nOptions:\n" \
+       "       -l      Show command name too\n" \
+       "       -f      Match against entire command line\n" \
+       "       -n      Signal the newest process only\n" \
+       "       -o      Signal the oldest process only\n" \
+       "       -v      Negate the matching\n" \
+       "       -x      Match whole name (not substring)"
+
 #if (ENABLE_FEATURE_PIDOF_SINGLE || ENABLE_FEATURE_PIDOF_OMIT)
 #define USAGE_PIDOF "Options:"
 #else
        "Move the current root file system to PUT_OLD and make NEW_ROOT\n" \
        "the new root file system"
 
+#define pkill_trivial_usage \
+       "[-l] | [-fnovx] [-signal] pattern"
+#define pkill_full_usage \
+       "Send a signal to process(es) selected by regex pattern" \
+       "\n\nOptions:\n" \
+       "       -l      List all signals\n" \
+       "       -f      Match against entire command line\n" \
+       "       -n      Signal the newest process only\n" \
+       "       -o      Signal the oldest process only\n" \
+       "       -v      Negate the matching\n" \
+       "       -x      Match whole name (not substring)"
+
 #define poweroff_trivial_usage \
        "[-d delay] [-n] [-f]"
 #define poweroff_full_usage \
index e62e5a1f16959664367f864cf53a591d117fb87c..476e1f34de68900959283f05961036679fb3b661 100644 (file)
@@ -375,13 +375,22 @@ procps_status_t *procps_scan(procps_status_t* sp, int flags)
                        }
                }
 #else
-               if (flags & PSSCAN_ARGV0) {
+               if (flags & *PSSCAN_ARGV0|PSSCAN_ARGVN)) {
                        free(sp->argv0);
                        sp->argv0 = NULL;
                        strcpy(filename_tail, "/cmdline");
                        n = read_to_buf(filename, buf);
                        if (n <= 0)
                                break;
+#if ENABLE_PGREP || ENABLE_PKILL
+                       if (flags & PSSCAN_ARGVN) {
+                               do {
+                                       n--;
+                                       if (buf[n] == '\0')
+                                               buf[n] = ' ';
+                               } while (n);
+                       }
+#endif
                        sp->argv0 = xstrdup(buf);
                }
 #endif
index f8eaea76da52311499d1e329e54d60dc6a08e095..5a6f592aa459de1fa116eeb7658bcc84229724d7 100644 (file)
@@ -159,3 +159,18 @@ const char *get_signame(int number)
 
        return itoa(number);
 }
+
+
+// Print the whole signal list
+
+void print_signames_and_exit(void)
+{
+       int signo;
+
+       for (signo = 1; signo < ARRAY_SIZE(signals); signo++) {
+               const char *name = signals[signo];
+               if (name[0])
+                       puts(name);
+       }
+       exit(EXIT_SUCCESS);
+}
index 2bd3cd245b69311a1a313f2b820aac95dc79ac66..7e7ee7922cd830c8ac7cfa8411a99fe5063f04cc 100644 (file)
@@ -49,6 +49,12 @@ config NMETER
        help
          Prints selected system stats continuously, one line per update.
 
+config PGREP
+       bool "pgrep"
+       default n
+       help
+         Look for processes by name.
+
 config PIDOF
        bool "pidof"
        default n
@@ -72,6 +78,12 @@ config FEATURE_PIDOF_OMIT
          The special pid %PPID can be used to name the parent process
          of the pidof, in other words the calling shell or shell script.
 
+config PKILL
+       bool "pkill"
+       default n
+       help
+         Send signals to processes by name.
+
 config PS
        bool "ps"
        default n
index c75be291b3e6cecca91305ca55b7e20610bdfd72..8e62fdfa6d3d6b5afe46f54d15560c769cde22f0 100644 (file)
@@ -10,6 +10,8 @@ lib-$(CONFIG_FUSER)   += fuser.o
 lib-$(CONFIG_KILL)     += kill.o
 lib-$(CONFIG_ASH)      += kill.o  # used for built-in kill by ash
 lib-$(CONFIG_NMETER)    += nmeter.o
+lib-$(CONFIG_PGREP)    += pgrep.o
+lib-$(CONFIG_PKILL)    += pgrep.o
 lib-$(CONFIG_PIDOF)    += pidof.o
 lib-$(CONFIG_PS)       += ps.o
 lib-$(CONFIG_RENICE)   += renice.o
index 892a798c59f66019e162b660d33c40794c3e4dc5..961f8cb14bcce01f3c9f27c5e3c5d23208fd646e 100644 (file)
@@ -58,33 +58,29 @@ int kill_main(int argc, char **argv)
        if (arg[1] == 'l' && arg[2] == '\0') {
                if (argc == 1) {
                        /* Print the whole signal list */
-                       for (signo = 1; signo < 32; signo++) {
-                               const char *name = get_signame(signo);
-                               if (!isdigit(name[0]))
-                                       puts(name);
-                       }
-               } else { /* -l <sig list> */
-                       while ((arg = *++argv)) {
-                               if (isdigit(arg[0])) {
-                                       signo = bb_strtou(arg, NULL, 10);
-                                       if (errno) {
-                                               bb_error_msg("unknown signal '%s'", arg);
-                                               return EXIT_FAILURE;
-                                       }
-                                       /* Exitcodes >= 0x80 are to be treated
-                                        * as "killed by signal (exitcode & 0x7f)" */
-                                       puts(get_signame(signo & 0x7f));
-                                       /* TODO: 'bad' signal# - coreutils says:
-                                        * kill: 127: invalid signal
-                                        * we just print "127" instead */
-                               } else {
-                                       signo = get_signum(arg);
-                                       if (signo < 0) {
-                                               bb_error_msg("unknown signal '%s'", arg);
-                                               return EXIT_FAILURE;
-                                       }
-                                       printf("%d\n", signo);
+                       print_signames_and_exit();
+               }
+               /* -l <sig list> */
+               while ((arg = *++argv)) {
+                       if (isdigit(arg[0])) {
+                               signo = bb_strtou(arg, NULL, 10);
+                               if (errno) {
+                                       bb_error_msg("unknown signal '%s'", arg);
+                                       return EXIT_FAILURE;
+                               }
+                               /* Exitcodes >= 0x80 are to be treated
+                                * as "killed by signal (exitcode & 0x7f)" */
+                               puts(get_signame(signo & 0x7f));
+                               /* TODO: 'bad' signal# - coreutils says:
+                                * kill: 127: invalid signal
+                                * we just print "127" instead */
+                       } else {
+                               signo = get_signum(arg);
+                               if (signo < 0) {
+                                       bb_error_msg("unknown signal '%s'", arg);
+                                       return EXIT_FAILURE;
                                }
+                               printf("%d\n", signo);
                        }
                }
                /* If they specified -l, we are all done */
diff --git a/procps/pgrep.c b/procps/pgrep.c
new file mode 100644 (file)
index 0000000..3bf087f
--- /dev/null
@@ -0,0 +1,136 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini pgrep/pkill implementation for busybox
+ *
+ * Copyright (C) 2007 Loic Grenie <loic.grenie@gmail.com>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include <getopt.h>
+
+#include "libbb.h"
+#include "xregex.h"
+
+/* Idea taken from kill.c */
+#define pgrep (ENABLE_PGREP && applet_name[1] == 'g')
+#define pkill (ENABLE_PKILL && applet_name[1] == 'k')
+
+enum {
+       /* "vlfxon" */
+       PGREPOPTBIT_V = 0, /* must be first, we need OPT_INVERT = 0/1 */
+       PGREPOPTBIT_L,
+       PGREPOPTBIT_F,
+       PGREPOPTBIT_X,
+       PGREPOPTBIT_O,
+       PGREPOPTBIT_N,
+};
+
+#define OPT_INVERT     (opt & (1 << PGREPOPTBIT_V))
+#define OPT_LIST       (opt & (1 << PGREPOPTBIT_L))
+#define OPT_FULL       (opt & (1 << PGREPOPTBIT_F))
+#define OPT_ANCHOR     (opt & (1 << PGREPOPTBIT_X))
+#define OPT_FIRST      (opt & (1 << PGREPOPTBIT_O))
+#define OPT_LAST       (opt & (1 << PGREPOPTBIT_N))
+
+static void act(unsigned pid, char *cmd, int signo, unsigned opt)
+{
+       if (pgrep) {
+               if (OPT_LIST)
+                       printf("%d %s\n", pid, cmd);
+               else
+                       printf("%d\n", pid);
+       } else
+               kill(pid, signo);
+}
+
+int pgrep_main(int argc, char **argv);
+int pgrep_main(int argc, char **argv)
+{
+       unsigned pid = getpid();
+       int signo = SIGTERM;
+       unsigned opt;
+       int scan_mask = PSSCAN_COMM;
+       char *first_arg;
+       int first_arg_idx;
+       int matched_pid;
+       char *cmd_last;
+       procps_status_t *proc;
+       /* These are initialized to 0 */
+       struct {
+               regex_t re_buffer;
+               regmatch_t re_match[1];
+       } Z;
+#define re_buffer (Z.re_buffer)
+#define re_match  (Z.re_match )
+
+       memset(&Z, 0, sizeof(Z));
+
+       /* We must avoid interpreting -NUM (signal num) as an option */
+       first_arg_idx = 1;
+       while (1) {
+               first_arg = argv[first_arg_idx];
+               if (!first_arg)
+                       break;
+               if (first_arg[0] != '-' || first_arg[1] < 'a' || first_arg[1] > 'z') {
+                       argv[first_arg_idx] = NULL;
+                       break;
+               }
+               first_arg_idx++;
+       }
+       opt = getopt32(argv, "vlfxon");
+       argv[first_arg_idx] = first_arg;
+
+       argv += optind;
+       //argc -= optind; - unused anyway
+       if (OPT_FULL)
+               scan_mask |= PSSCAN_ARGVN;
+
+       if (pkill) {
+               if (OPT_LIST) /* -l: print the whole signal list */
+                       print_signames_and_exit();
+               if (first_arg && first_arg[0] == '-') {
+                       signo = get_signum(&first_arg[1]);
+                       if (signo < 0) /* || signo > MAX_SIGNUM ? */
+                               bb_error_msg_and_die("bad signal name '%s'", &first_arg[1]);
+                       argv++;
+               }
+       }
+
+       /* One pattern is required */
+       if (!argv[0] || argv[1])
+               bb_show_usage();
+
+       xregcomp(&re_buffer, argv[0], 0);
+       matched_pid = 0;
+       cmd_last = NULL;
+       proc = NULL;
+       while ((proc = procps_scan(proc, scan_mask)) != NULL) {
+               char *cmd;
+               if (proc->pid == pid)
+                       continue;
+               cmd = proc->argv0;
+               if (!cmd)
+                       cmd = proc->comm;
+               /* NB: OPT_INVERT is always 0 or 1 */
+               if ((regexec(&re_buffer, cmd, 1, re_match, 0) == 0 /* match found */
+                     && (!OPT_ANCHOR || (re_match[0].rm_so == 0 && re_match[0].rm_eo == strlen(cmd)))) ^ OPT_INVERT
+               ) {
+                       matched_pid = proc->pid;
+                       if (OPT_LAST) {
+                               free(cmd_last);
+                               cmd_last = xstrdup(cmd_last);
+                               continue;
+                       }
+                       act(proc->pid, cmd, signo, opt);
+                       if (OPT_FIRST)
+                               break;
+               }
+       }
+       if (cmd_last) {
+               act(matched_pid, cmd_last, signo, opt);
+               if (ENABLE_FEATURE_CLEAN_UP)
+                       free(cmd_last);
+       }
+       return matched_pid == 0; /* return 1 if no processes listed/signaled */
+}