diff --git a/include/applets.h b/include/applets.h index f162c5479..e88192d24 100644 --- a/include/applets.h +++ b/include/applets.h @@ -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)) diff --git a/include/libbb.h b/include/libbb.h index a6709c95d..34b978452 100644 --- a/include/libbb.h +++ b/include/libbb.h @@ -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 diff --git a/include/usage.h b/include/usage.h index 4152fecd3..d7e1dd9f1 100644 --- a/include/usage.h +++ b/include/usage.h @@ -2558,6 +2558,18 @@ "$ 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 @@ -2640,6 +2652,18 @@ "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 \ diff --git a/libbb/procps.c b/libbb/procps.c index e62e5a1f1..476e1f34d 100644 --- a/libbb/procps.c +++ b/libbb/procps.c @@ -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 diff --git a/libbb/u_signal_names.c b/libbb/u_signal_names.c index f8eaea76d..5a6f592aa 100644 --- a/libbb/u_signal_names.c +++ b/libbb/u_signal_names.c @@ -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); +} diff --git a/procps/Config.in b/procps/Config.in index 2bd3cd245..7e7ee7922 100644 --- a/procps/Config.in +++ b/procps/Config.in @@ -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 diff --git a/procps/Kbuild b/procps/Kbuild index c75be291b..8e62fdfa6 100644 --- a/procps/Kbuild +++ b/procps/Kbuild @@ -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 diff --git a/procps/kill.c b/procps/kill.c index 892a798c5..961f8cb14 100644 --- a/procps/kill.c +++ b/procps/kill.c @@ -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 */ - 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 */ + 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 index 000000000..3bf087f28 --- /dev/null +++ b/procps/pgrep.c @@ -0,0 +1,136 @@ +/* vi: set sw=4 ts=4: */ +/* + * Mini pgrep/pkill implementation for busybox + * + * Copyright (C) 2007 Loic Grenie + * + * Licensed under the GPL v2 or later, see the file LICENSE in this tarball. + */ + +#include + +#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 */ +}