xref: /dragonfly/usr.bin/killall/killall.c (revision a32bc35d)
1 /*-
2  * Copyright (c) 2000 Peter Wemm <peter@FreeBSD.org>
3  * Copyright (c) 2000 Paul Saab <ps@FreeBSD.org>
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms, with or without
7  * modification, are permitted provided that the following conditions
8  * are met:
9  * 1. Redistributions of source code must retain the above copyright
10  *    notice, this list of conditions and the following disclaimer.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  *
15  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25  * SUCH DAMAGE.
26  *
27  * $FreeBSD: src/usr.bin/killall/killall.c,v 1.5.2.4 2001/05/19 19:22:49 phk Exp $
28  */
29 
30 #include <sys/user.h>
31 #include <sys/param.h>
32 #include <sys/stat.h>
33 #include <sys/sysctl.h>
34 #include <fcntl.h>
35 #include <dirent.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 #include <pwd.h>
40 #include <signal.h>
41 #include <regex.h>
42 #include <ctype.h>
43 #include <err.h>
44 #include <errno.h>
45 #include <unistd.h>
46 
47 static char	*prog;
48 
49 static void __dead2
50 usage(void)
51 {
52 
53 	fprintf(stderr, "usage: %s [-l] [-v] [-m] [-sig] "
54 			"[-u user] [-j jail] [-t tty] "
55 			"[-c cmd] [cmd]...\n", prog);
56 	fprintf(stderr, "At least one option or argument to specify "
57 			"processes must be given.\n");
58 	exit(1);
59 }
60 
61 static char *
62 upper(const char *str)
63 {
64 	static char buf[80];
65 	char *s;
66 
67 	strlcpy(buf, str, sizeof(buf));
68 	for (s = buf; *s; s++)
69 		*s = toupper((unsigned char)*s);
70 	return buf;
71 }
72 
73 
74 static void
75 printsig(FILE *fp)
76 {
77 	const char	*const * p;
78 	int		cnt;
79 	int		offset = 0;
80 
81 	for (cnt = NSIG, p = sys_signame + 1; --cnt; ++p) {
82 		offset += fprintf(fp, "%s ", upper(*p));
83 		if (offset >= 75 && cnt > 1) {
84 			offset = 0;
85 			fprintf(fp, "\n");
86 		}
87 	}
88 	fprintf(fp, "\n");
89 }
90 
91 static void
92 nosig(char *name)
93 {
94 
95 	warnx("unknown signal %s; valid signals:", name);
96 	printsig(stderr);
97 	exit(1);
98 }
99 
100 int
101 main(int ac, char **av)
102 {
103 	struct kinfo_proc *procs = NULL, *newprocs;
104 	struct stat	sb;
105 	struct passwd	*pw;
106 	regex_t		rgx;
107 	regmatch_t	pmatch;
108 	int		i, j;
109 	char		buf[256];
110 	char		*user = NULL;
111 	char		*tty = NULL;
112 	char		*cmd = NULL;
113 	int		qflag = 0;
114 	int		vflag = 0;
115 	int		sflag = 0;
116 	int		jflag = 0, jailid = 0;
117 	int		dflag = 0;
118 	int		mflag = 0;
119 	uid_t		uid = 0;
120 	dev_t		tdev = 0;
121 	pid_t		mypid;
122 	char		thiscmd[MAXCOMLEN + 1];
123 	pid_t		thispid;
124 	uid_t		thisuid;
125 	dev_t		thistdev;
126 	int		sig = SIGTERM;
127 	const char *const *p;
128 	char		*ep;
129 	int		errors = 0;
130 	int		mib[4];
131 	size_t		miblen;
132 	int		st, nprocs;
133 	size_t		size;
134 	int		matched;
135 	int		killed = 0;
136 
137 	prog = av[0];
138 	av++;
139 	ac--;
140 
141 	while (ac > 0) {
142 		if (strcmp(*av, "-l") == 0) {
143 			printsig(stdout);
144 			exit(0);
145 		}
146 		if (strcmp(*av, "-help") == 0)
147 			usage();
148 		if (**av == '-') {
149 			++*av;
150 			switch (**av) {
151 			case 'u':
152 				++*av;
153 				if (**av == '\0')
154 					++av;
155 				--ac;
156 				user = *av;
157 				break;
158 			case 't':
159 				++*av;
160 				if (**av == '\0')
161 					++av;
162 				--ac;
163 				tty = *av;
164 				break;
165 			case 'c':
166 				++*av;
167 				if (**av == '\0')
168 					++av;
169 				--ac;
170 				cmd = *av;
171 				break;
172 			case 'j':
173 			{
174 				const char *errstr;
175 				++*av;
176 				if (**av == '\0')
177 					++av;
178 				--ac;
179 				jailid = strtonum(*av, 1, INT_MAX, &errstr);
180 
181 				if (errstr)
182 					errx(1, "jail id is %s: %s", errstr, *av);
183 				jflag++;
184 				break;
185 			}
186 			case 'q':
187 				qflag++;
188 				break;
189 			case 'v':
190 				vflag++;
191 				break;
192 			case 's':
193 				sflag++;
194 				break;
195 			case 'd':
196 				dflag++;
197 				break;
198 			case 'm':
199 				mflag++;
200 				break;
201 			default:
202 				if (isalpha((unsigned char)**av)) {
203 					if (strncasecmp(*av, "sig", 3) == 0)
204 						*av += 3;
205 					for (sig = NSIG, p = sys_signame + 1;
206 					     --sig; ++p)
207 						if (strcasecmp(*p, *av) == 0) {
208 							sig = p - sys_signame;
209 							break;
210 						}
211 					if (!sig)
212 						nosig(*av);
213 				} else if (isdigit((unsigned char)**av)) {
214 					sig = strtol(*av, &ep, 10);
215 					if (!*av || *ep)
216 						errx(1, "illegal signal number: %s", *av);
217 					if (sig < 0 || sig >= NSIG)
218 						nosig(*av);
219 				} else
220 					nosig(*av);
221 			}
222 			++av;
223 			--ac;
224 		} else {
225 			break;
226 		}
227 	}
228 
229 	if (user == NULL && tty == NULL && cmd == NULL && jflag == 0 && ac == 0)
230 		usage();
231 
232 	if (tty) {
233 		if (strncmp(tty, "/dev/", 5) == 0)
234 			snprintf(buf, sizeof(buf), "%s", tty);
235 		else if (strncmp(tty, "tty", 3) == 0)
236 			snprintf(buf, sizeof(buf), "/dev/%s", tty);
237 		else if (isdigit(tty[0]))
238 			snprintf(buf, sizeof(buf), "/dev/pts/%s", tty);
239 		else
240 			snprintf(buf, sizeof(buf), "/dev/tty%s", tty);
241 		if (stat(buf, &sb) < 0)
242 			err(1, "stat(%s)", buf);
243 		if (!S_ISCHR(sb.st_mode))
244 			errx(1, "%s: not a character device", buf);
245 		tdev = sb.st_rdev;
246 		if (dflag)
247 			printf("ttydev:0x%x\n", tdev);
248 	}
249 	if (user) {
250 		pw = getpwnam(user);
251 		if (pw == NULL)
252 			errx(1, "user %s does not exist", user);
253 		uid = pw->pw_uid;
254 		if (dflag)
255 			printf("uid:%d\n", uid);
256 	} else {
257 		uid = getuid();
258 		if (uid != 0) {
259 			pw = getpwuid(uid);
260 			if (pw)
261 				user = pw->pw_name;
262 			if (dflag)
263 				printf("uid:%d\n", uid);
264 		}
265 	}
266 	size = 0;
267 	mib[0] = CTL_KERN;
268 	mib[1] = KERN_PROC;
269 	mib[2] = KERN_PROC_ALL;
270 	mib[3] = 0;
271 	miblen = 3;
272 
273 	if (user && mib[2] == KERN_PROC_ALL) {
274 		mib[2] = KERN_PROC_RUID;
275 		mib[3] = uid;
276 		miblen = 4;
277 	}
278 	if (tty && mib[2] == KERN_PROC_ALL) {
279 		mib[2] = KERN_PROC_TTY;
280 		mib[3] = tdev;
281 		miblen = 4;
282 	}
283 
284 	st = sysctl(mib, miblen, NULL, &size, NULL, 0);
285 	do {
286 		size += size / 10;
287 		newprocs = realloc(procs, size);
288 		if (newprocs == NULL) {
289 			if (procs)
290 				free(procs);
291 			errx(1, "could not reallocate memory");
292 		}
293 		procs = newprocs;
294 		st = sysctl(mib, miblen, procs, &size, NULL, 0);
295 	} while (st == -1 && errno == ENOMEM);
296 	if (st == -1)
297 		err(1, "could not sysctl(KERN_PROC)");
298 	if (size % sizeof(struct kinfo_proc) != 0) {
299 		fprintf(stderr, "proc size mismatch (%zu total, %zu chunks)\n",
300 			size, sizeof(struct kinfo_proc));
301 		fprintf(stderr, "userland out of sync with kernel, recompile libkvm etc\n");
302 		exit(1);
303 	}
304 	nprocs = size / sizeof(struct kinfo_proc);
305 	if (dflag)
306 		printf("nprocs %d\n", nprocs);
307 	mypid = getpid();
308 
309 	for (i = 0; i < nprocs; i++) {
310 		thispid = procs[i].kp_pid;
311 		strncpy(thiscmd, procs[i].kp_comm, MAXCOMLEN);
312 		thiscmd[MAXCOMLEN] = '\0';
313 		thistdev = procs[i].kp_tdev;
314 		thisuid = procs[i].kp_ruid;	/* real uid */
315 
316 		if (thispid == mypid)
317 			continue;
318 		matched = 1;
319 		if ((int)procs[i].kp_pid < 0)
320 			matched = 0;
321 		if (user) {
322 			if (thisuid != uid)
323 				matched = 0;
324 		}
325 		if (tty) {
326 			if (thistdev != tdev)
327 				matched = 0;
328 		}
329 		if (jflag) {
330 			if (procs[i].kp_jailid != jailid)
331 				matched = 0;
332 		}
333 		if (cmd) {
334 			if (mflag) {
335 				if (regcomp(&rgx, cmd,
336 				    REG_EXTENDED|REG_NOSUB) != 0) {
337 					mflag = 0;
338 					warnx("%s: illegal regexp", cmd);
339 				}
340 			}
341 			if (mflag) {
342 				pmatch.rm_so = 0;
343 				pmatch.rm_eo = strlen(thiscmd);
344 				if (regexec(&rgx, thiscmd, 0, &pmatch,
345 				    REG_STARTEND) != 0)
346 					matched = 0;
347 				regfree(&rgx);
348 			} else {
349 				if (strcmp(thiscmd, cmd) != 0)
350 					matched = 0;
351 			}
352 		}
353 		if (matched == 0)
354 			continue;
355 		if (ac > 0) {
356 			matched = 0;
357 			for (j = 0; j < ac; j++) {
358 				if (mflag) {
359 					if (regcomp(&rgx, av[j],
360 					    REG_EXTENDED|REG_NOSUB) != 0) {
361 						mflag = 0;
362 						warnx("%s: illegal regexp", av[j]);
363 					}
364 				}
365 				if (mflag) {
366 					pmatch.rm_so = 0;
367 					pmatch.rm_eo = strlen(thiscmd);
368 					if (regexec(&rgx, thiscmd, 0, &pmatch,
369 					    REG_STARTEND) == 0)
370 						matched = 1;
371 					regfree(&rgx);
372 				} else {
373 					if (strcmp(thiscmd, av[j]) == 0)
374 						matched = 1;
375 				}
376 				if (matched)
377 					break;
378 			}
379 			if (matched == 0)
380 				continue;
381 		}
382 		if (dflag)
383 			printf("sig:%d, cmd:%s, pid:%d, dev:0x%x uid:%d\n", sig,
384 			    thiscmd, thispid, thistdev, thisuid);
385 
386 		if (vflag || sflag)
387 			printf("kill -%s %d\n", upper(sys_signame[sig]),
388 			    thispid);
389 
390 		killed++;
391 		if (!dflag && !sflag) {
392 			if (kill(thispid, sig) < 0 /* && errno != ESRCH */ ) {
393 				warn("kill -%s %d", upper(sys_signame[sig]),
394 				    thispid);
395 				errors = 1;
396 			}
397 		}
398 	}
399 	if (!qflag && killed == 0) {
400 		fprintf(stderr, "No matching processes %swere found\n",
401 		    getuid() != 0 ? "belonging to you " : "");
402 		errors = 1;
403 	}
404 	exit(errors);
405 }
406