pgrep.c 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  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"
  11. //config: default y
  12. //config: help
  13. //config: Look for processes by name.
  14. //config:
  15. //config:config PKILL
  16. //config: bool "pkill"
  17. //config: default y
  18. //config: help
  19. //config: Send signals to processes by name.
  20. //applet:IF_PGREP(APPLET(pgrep, BB_DIR_USR_BIN, BB_SUID_DROP))
  21. // APPLET_ODDNAME:name main location suid_type help
  22. //applet:IF_PKILL(APPLET_ODDNAME(pkill, pgrep, BB_DIR_USR_BIN, BB_SUID_DROP, pkill))
  23. //kbuild:lib-$(CONFIG_PGREP) += pgrep.o
  24. //kbuild:lib-$(CONFIG_PKILL) += pgrep.o
  25. //usage:#define pgrep_trivial_usage
  26. //usage: "[-flanovx] [-s SID|-P PPID|PATTERN]"
  27. //usage:#define pgrep_full_usage "\n\n"
  28. //usage: "Display process(es) selected by regex PATTERN\n"
  29. //usage: "\n -l Show command name too"
  30. //usage: "\n -a Show command line too"
  31. //usage: "\n -f Match against entire command line"
  32. //usage: "\n -n Show the newest process only"
  33. //usage: "\n -o Show the oldest process only"
  34. //usage: "\n -v Negate the match"
  35. //usage: "\n -x Match whole name (not substring)"
  36. //usage: "\n -s Match session ID (0 for current)"
  37. //usage: "\n -P Match parent process ID"
  38. //usage:
  39. //usage:#define pkill_trivial_usage
  40. //usage: "[-l|-SIGNAL] [-fnovx] [-s SID|-P PPID|PATTERN]"
  41. //usage:#define pkill_full_usage "\n\n"
  42. //usage: "Send a signal to process(es) selected by regex PATTERN\n"
  43. //usage: "\n -l List all signals"
  44. //usage: "\n -f Match against entire command line"
  45. //usage: "\n -n Signal the newest process only"
  46. //usage: "\n -o Signal the oldest process only"
  47. //usage: "\n -v Negate the match"
  48. //usage: "\n -x Match whole name (not substring)"
  49. //usage: "\n -s Match session ID (0 for current)"
  50. //usage: "\n -P Match parent process ID"
  51. #include "libbb.h"
  52. #include "xregex.h"
  53. /* Idea taken from kill.c */
  54. #define pgrep (ENABLE_PGREP && (!ENABLE_PKILL || applet_name[1] == 'g'))
  55. #define pkill (ENABLE_PKILL && (!ENABLE_PGREP || applet_name[1] == 'k'))
  56. enum {
  57. /* "vlafxons:+P:+" */
  58. OPTBIT_V = 0, /* must be first, we need OPT_INVERT = 0/1 */
  59. OPTBIT_L,
  60. OPTBIT_A,
  61. OPTBIT_F,
  62. OPTBIT_X,
  63. OPTBIT_O,
  64. OPTBIT_N,
  65. OPTBIT_S,
  66. OPTBIT_P,
  67. };
  68. #define OPT_INVERT (opt & (1 << OPTBIT_V))
  69. #define OPT_LIST (opt & (1 << OPTBIT_L))
  70. #define OPT_LISTFULL (opt & (1 << OPTBIT_A))
  71. #define OPT_FULL (opt & (1 << OPTBIT_F))
  72. #define OPT_ANCHOR (opt & (1 << OPTBIT_X))
  73. #define OPT_FIRST (opt & (1 << OPTBIT_O))
  74. #define OPT_LAST (opt & (1 << OPTBIT_N))
  75. #define OPT_SID (opt & (1 << OPTBIT_S))
  76. #define OPT_PPID (opt & (1 << OPTBIT_P))
  77. static void act(unsigned pid, char *cmd, int signo)
  78. {
  79. if (pgrep) {
  80. if (option_mask32 & ((1 << OPTBIT_L)|(1 << OPTBIT_A))) /* -l or -a */
  81. printf("%u %s\n", pid, cmd);
  82. else
  83. printf("%u\n", pid);
  84. } else
  85. kill(pid, signo);
  86. }
  87. int pgrep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
  88. int pgrep_main(int argc UNUSED_PARAM, char **argv)
  89. {
  90. unsigned pid;
  91. int signo;
  92. unsigned opt;
  93. int scan_mask;
  94. int matched_pid;
  95. int sid2match, ppid2match;
  96. char *cmd_last;
  97. procps_status_t *proc;
  98. /* These are initialized to 0 */
  99. struct {
  100. regex_t re_buffer;
  101. regmatch_t re_match[1];
  102. } Z;
  103. #define re_buffer (Z.re_buffer)
  104. #define re_match (Z.re_match )
  105. memset(&Z, 0, sizeof(Z));
  106. /* Parse -SIGNAL for pkill. Must be first option, if present */
  107. signo = SIGTERM;
  108. if (pkill && argv[1] && argv[1][0] == '-') {
  109. int temp = get_signum(argv[1]+1);
  110. if (temp != -1) {
  111. signo = temp;
  112. argv++;
  113. }
  114. }
  115. /* Parse remaining options */
  116. ppid2match = -1;
  117. sid2match = -1;
  118. opt = getopt32(argv, "vlafxons:+P:+", &sid2match, &ppid2match);
  119. argv += optind;
  120. if (pkill && OPT_LIST) { /* -l: print the whole signal list */
  121. print_signames();
  122. return 0;
  123. }
  124. pid = getpid();
  125. if (sid2match == 0)
  126. sid2match = getsid(pid);
  127. scan_mask = PSSCAN_COMM | PSSCAN_ARGV0;
  128. if (OPT_FULL)
  129. scan_mask |= PSSCAN_ARGVN;
  130. /* One pattern is required, if no -s and no -P */
  131. if ((sid2match & ppid2match) < 0 && (!argv[0] || argv[1]))
  132. bb_show_usage();
  133. if (argv[0])
  134. xregcomp(&re_buffer, argv[0], OPT_ANCHOR ? REG_EXTENDED : (REG_EXTENDED|REG_NOSUB));
  135. matched_pid = 0;
  136. cmd_last = NULL;
  137. proc = NULL;
  138. while ((proc = procps_scan(proc, scan_mask)) != NULL) {
  139. char *cmd;
  140. int cmdlen;
  141. if (proc->pid == pid)
  142. continue;
  143. if (ppid2match >= 0 && ppid2match != proc->ppid)
  144. continue;
  145. if (sid2match >= 0 && sid2match != proc->sid)
  146. continue;
  147. cmdlen = -1;
  148. cmd = proc->argv0;
  149. if (!cmd) {
  150. cmd = proc->comm;
  151. } else {
  152. int i = proc->argv_len;
  153. if (!OPT_LISTFULL)
  154. cmdlen = strlen(cmd); /* not -a: find first NUL */
  155. /*
  156. * "sleep 11" looks like "sleep""\0""11""\0" in argv0.
  157. * Make sure last "\0" does not get converted to " ":
  158. */
  159. if (i && cmd[i-1] == '\0')
  160. i--;
  161. while (--i >= 0) {
  162. if ((unsigned char)cmd[i] < ' ')
  163. cmd[i] = ' ';
  164. }
  165. }
  166. /* NB: OPT_INVERT is always 0 or 1 */
  167. if (!argv[0]
  168. || (regexec(&re_buffer, cmd, 1, re_match, 0) == 0 /* match found */
  169. && (!OPT_ANCHOR || (re_match[0].rm_so == 0 && re_match[0].rm_eo == (regoff_t)strlen(cmd)))
  170. ) ^ OPT_INVERT
  171. ) {
  172. matched_pid = proc->pid;
  173. if (OPT_LAST) {
  174. free(cmd_last);
  175. cmd_last = xstrdup(cmd);
  176. continue;
  177. }
  178. if (cmdlen >= 0)
  179. cmd[cmdlen] = '\0';
  180. act(proc->pid, cmd, signo);
  181. if (OPT_FIRST)
  182. break;
  183. }
  184. }
  185. if (cmd_last) {
  186. act(matched_pid, cmd_last, signo);
  187. if (ENABLE_FEATURE_CLEAN_UP)
  188. free(cmd_last);
  189. }
  190. return matched_pid == 0; /* return 1 if no processes listed/signaled */
  191. }