pgrep.c 6.8 KB

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