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 the GPL v2 or later, see the file LICENSE in this tarball.
8 */
9#include "libbb.h"
10#include "xregex.h"
11
12/* Idea taken from kill.c */
13#define pgrep (ENABLE_PGREP && applet_name[1] == 'g')
14#define pkill (ENABLE_PKILL && applet_name[1] == 'k')
15
16enum {
17	/* "vlfxons:P:" */
18	OPTBIT_V = 0, /* must be first, we need OPT_INVERT = 0/1 */
19	OPTBIT_L,
20	OPTBIT_F,
21	OPTBIT_X,
22	OPTBIT_O,
23	OPTBIT_N,
24	OPTBIT_S,
25	OPTBIT_P,
26};
27
28#define OPT_INVERT	(opt & (1 << OPTBIT_V))
29#define OPT_LIST	(opt & (1 << OPTBIT_L))
30#define OPT_FULL	(opt & (1 << OPTBIT_F))
31#define OPT_ANCHOR	(opt & (1 << OPTBIT_X))
32#define OPT_FIRST	(opt & (1 << OPTBIT_O))
33#define OPT_LAST	(opt & (1 << OPTBIT_N))
34#define OPT_SID		(opt & (1 << OPTBIT_S))
35#define OPT_PPID	(opt & (1 << OPTBIT_P))
36
37static void act(unsigned pid, char *cmd, int signo)
38{
39	if (pgrep) {
40		if (option_mask32 & (1 << OPTBIT_L)) /* OPT_LIST */
41			printf("%d %s\n", pid, cmd);
42		else
43			printf("%d\n", pid);
44	} else
45		kill(pid, signo);
46}
47
48int pgrep_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
49int pgrep_main(int argc UNUSED_PARAM, char **argv)
50{
51	unsigned pid;
52	int signo;
53	unsigned opt;
54	int scan_mask;
55	int matched_pid;
56	int sid2match, ppid2match;
57	char *cmd_last;
58	procps_status_t *proc;
59	/* These are initialized to 0 */
60	struct {
61		regex_t re_buffer;
62		regmatch_t re_match[1];
63	} Z;
64#define re_buffer (Z.re_buffer)
65#define re_match  (Z.re_match )
66
67	memset(&Z, 0, sizeof(Z));
68
69	/* Parse -SIGNAL for pkill. Must be first option, if present */
70	signo = SIGTERM;
71	if (pkill && argv[1] && argv[1][0] == '-') {
72		int temp = get_signum(argv[1]+1);
73		if (temp != -1) {
74			signo = temp;
75			argv++;
76		}
77	}
78
79	/* Parse remaining options */
80	ppid2match = -1;
81	sid2match = -1;
82	opt_complementary = "s+:P+"; /* numeric opts */
83	opt = getopt32(argv, "vlfxons:P:", &sid2match, &ppid2match);
84	argv += optind;
85
86	if (pkill && OPT_LIST) { /* -l: print the whole signal list */
87		print_signames();
88		return 0;
89	}
90
91	pid = getpid();
92	if (sid2match == 0)
93		sid2match = getsid(pid);
94
95	scan_mask = PSSCAN_COMM | PSSCAN_ARGV0;
96	if (OPT_FULL)
97		scan_mask |= PSSCAN_ARGVN;
98
99	/* One pattern is required, if no -s and no -P */
100	if ((sid2match & ppid2match) < 0 && (!argv[0] || argv[1]))
101		bb_show_usage();
102
103	if (argv[0])
104		xregcomp(&re_buffer, argv[0], 0);
105
106	matched_pid = 0;
107	cmd_last = NULL;
108	proc = NULL;
109	while ((proc = procps_scan(proc, scan_mask)) != NULL) {
110		char *cmd;
111
112		if (proc->pid == pid)
113			continue;
114
115		cmd = proc->argv0;
116		if (!cmd) {
117			cmd = proc->comm;
118		} else {
119			int i = proc->argv_len;
120			while (--i >= 0) {
121				if ((unsigned char)cmd[i] < ' ')
122					cmd[i] = ' ';
123			}
124		}
125
126		if (ppid2match >= 0 && ppid2match != proc->ppid)
127			continue;
128		if (sid2match >= 0  && sid2match != proc->sid)
129			continue;
130
131		/* NB: OPT_INVERT is always 0 or 1 */
132		if (!argv[0]
133		 || (regexec(&re_buffer, cmd, 1, re_match, 0) == 0 /* match found */
134		    && (!OPT_ANCHOR || (re_match[0].rm_so == 0 && re_match[0].rm_eo == (regoff_t)strlen(cmd)))
135		    ) ^ OPT_INVERT
136		) {
137			matched_pid = proc->pid;
138			if (OPT_LAST) {
139				free(cmd_last);
140				cmd_last = xstrdup(cmd);
141				continue;
142			}
143			act(proc->pid, cmd, signo);
144			if (OPT_FIRST)
145				break;
146		}
147	}
148
149	if (cmd_last) {
150		act(matched_pid, cmd_last, signo);
151		if (ENABLE_FEATURE_CLEAN_UP)
152			free(cmd_last);
153	}
154	return matched_pid == 0; /* return 1 if no processes listed/signaled */
155}
156