pgrep.c 6.5 KB

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