killall.c revision 237846
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
28#include <sys/cdefs.h>
29__FBSDID("$FreeBSD: head/usr.bin/killall/killall.c 237846 2012-06-30 16:23:08Z kib $");
30
31#include <sys/param.h>
32#include <sys/jail.h>
33#include <sys/stat.h>
34#include <sys/uio.h>
35#include <sys/user.h>
36#include <sys/sysctl.h>
37#include <fcntl.h>
38#include <dirent.h>
39#include <jail.h>
40#include <stdio.h>
41#include <stdlib.h>
42#include <string.h>
43#include <pwd.h>
44#include <signal.h>
45#include <regex.h>
46#include <ctype.h>
47#include <err.h>
48#include <errno.h>
49#include <unistd.h>
50#include <locale.h>
51
52static void __dead2
53usage(void)
54{
55
56	fprintf(stderr, "usage: killall [-delmsvz] [-help] [-I] [-j jail]\n");
57	fprintf(stderr,
58	    "               [-u user] [-t tty] [-c cmd] [-SIGNAL] [cmd]...\n");
59	fprintf(stderr, "At least one option or argument to specify processes must be given.\n");
60	exit(1);
61}
62
63
64static void
65printsig(FILE *fp)
66{
67	const char	*const * p;
68	int		cnt;
69	int		offset = 0;
70
71	for (cnt = NSIG, p = sys_signame + 1; --cnt; ++p) {
72		offset += fprintf(fp, "%s ", *p);
73		if (offset >= 75 && cnt > 1) {
74			offset = 0;
75			fprintf(fp, "\n");
76		}
77	}
78	fprintf(fp, "\n");
79}
80
81static void
82nosig(char *name)
83{
84
85	warnx("unknown signal %s; valid signals:", name);
86	printsig(stderr);
87	exit(1);
88}
89
90int
91main(int ac, char **av)
92{
93	struct kinfo_proc *procs, *newprocs;
94	struct stat	sb;
95	struct passwd	*pw;
96	regex_t		rgx;
97	regmatch_t	pmatch;
98	int		i, j, ch;
99	char		buf[256];
100	char		first;
101	char		*user = NULL;
102	char		*tty = NULL;
103	char		*cmd = NULL;
104	int		vflag = 0;
105	int		sflag = 0;
106	int		dflag = 0;
107	int		eflag = 0;
108	int		Iflag = 0;
109	int		jflag = 0;
110	int		mflag = 0;
111	int		zflag = 0;
112	uid_t		uid = 0;
113	dev_t		tdev = 0;
114	pid_t		mypid;
115	char		thiscmd[MAXCOMLEN + 1];
116	pid_t		thispid;
117	uid_t		thisuid;
118	dev_t		thistdev;
119	int		sig = SIGTERM;
120	const char *const *p;
121	char		*ep;
122	int		errors = 0;
123	int		jid;
124	int		mib[4];
125	size_t		miblen;
126	int		st, nprocs;
127	size_t		size;
128	int		matched;
129	int		killed = 0;
130
131	setlocale(LC_ALL, "");
132
133	av++;
134	ac--;
135
136	while (ac > 0) {
137		if (strcmp(*av, "-l") == 0) {
138			printsig(stdout);
139			exit(0);
140		}
141		if (strcmp(*av, "-help") == 0)
142			usage();
143		if (**av == '-') {
144			++*av;
145			switch (**av) {
146			case 'I':
147				Iflag = 1;
148				break;
149			case 'j':
150				++*av;
151				if (**av == '\0') {
152					++av;
153					--ac;
154				}
155				jflag++;
156				if (*av == NULL)
157				    	errx(1, "must specify jail");
158				jid = jail_getid(*av);
159				if (jid < 0)
160					errx(1, "%s", jail_errmsg);
161				if (jail_attach(jid) == -1)
162					err(1, "jail_attach(%d)", jid);
163				break;
164			case 'u':
165				++*av;
166				if (**av == '\0') {
167					++av;
168					--ac;
169				}
170				if (*av == NULL)
171				    	errx(1, "must specify user");
172				user = *av;
173				break;
174			case 't':
175				++*av;
176				if (**av == '\0') {
177					++av;
178					--ac;
179				}
180				if (*av == NULL)
181				    	errx(1, "must specify tty");
182				tty = *av;
183				break;
184			case 'c':
185				++*av;
186				if (**av == '\0') {
187					++av;
188					--ac;
189				}
190				if (*av == NULL)
191				    	errx(1, "must specify procname");
192				cmd = *av;
193				break;
194			case 'v':
195				vflag++;
196				break;
197			case 's':
198				sflag++;
199				break;
200			case 'd':
201				dflag++;
202				break;
203			case 'e':
204				eflag++;
205				break;
206			case 'm':
207				mflag++;
208				break;
209			case 'z':
210				zflag++;
211				break;
212			default:
213				if (isalpha((unsigned char)**av)) {
214					if (strncasecmp(*av, "SIG", 3) == 0)
215						*av += 3;
216					for (sig = NSIG, p = sys_signame + 1;
217					     --sig; ++p)
218						if (strcasecmp(*p, *av) == 0) {
219							sig = p - sys_signame;
220							break;
221						}
222					if (!sig)
223						nosig(*av);
224				} else if (isdigit((unsigned char)**av)) {
225					sig = strtol(*av, &ep, 10);
226					if (!*av || *ep)
227						errx(1, "illegal signal number: %s", *av);
228					if (sig < 0 || sig >= NSIG)
229						nosig(*av);
230				} else
231					nosig(*av);
232			}
233			++av;
234			--ac;
235		} else {
236			break;
237		}
238	}
239
240	if (user == NULL && tty == NULL && cmd == NULL && !jflag && ac == 0)
241		usage();
242
243	if (tty) {
244		if (strncmp(tty, "/dev/", 5) == 0)
245			snprintf(buf, sizeof(buf), "%s", tty);
246		else if (strncmp(tty, "tty", 3) == 0)
247			snprintf(buf, sizeof(buf), "/dev/%s", tty);
248		else
249			snprintf(buf, sizeof(buf), "/dev/tty%s", tty);
250		if (stat(buf, &sb) < 0)
251			err(1, "stat(%s)", buf);
252		if (!S_ISCHR(sb.st_mode))
253			errx(1, "%s: not a character device", buf);
254		tdev = sb.st_rdev;
255		if (dflag)
256			printf("ttydev:0x%x\n", tdev);
257	}
258	if (user) {
259		uid = strtol(user, &ep, 10);
260		if (*user == '\0' || *ep != '\0') { /* was it a number? */
261			pw = getpwnam(user);
262			if (pw == NULL)
263				errx(1, "user %s does not exist", user);
264			uid = pw->pw_uid;
265			if (dflag)
266				printf("uid:%d\n", uid);
267		}
268	} else {
269		uid = getuid();
270		if (uid != 0) {
271			pw = getpwuid(uid);
272			if (pw)
273				user = pw->pw_name;
274			if (dflag)
275				printf("uid:%d\n", uid);
276		}
277	}
278	size = 0;
279	mib[0] = CTL_KERN;
280	mib[1] = KERN_PROC;
281
282	if (user) {
283		mib[2] = eflag ? KERN_PROC_UID : KERN_PROC_RUID;
284		mib[3] = uid;
285		miblen = 4;
286	} else if (tty) {
287		mib[2] = KERN_PROC_TTY;
288		mib[3] = tdev;
289		miblen = 4;
290	} else {
291		mib[2] = KERN_PROC_PROC;
292		mib[3] = 0;
293		miblen = 3;
294	}
295
296	procs = NULL;
297	st = sysctl(mib, miblen, NULL, &size, NULL, 0);
298	do {
299		size += size / 10;
300		newprocs = realloc(procs, size);
301		if (newprocs == NULL) {
302			free(procs);
303			err(1, "could not reallocate memory");
304		}
305		procs = newprocs;
306		st = sysctl(mib, miblen, procs, &size, NULL, 0);
307	} while (st == -1 && errno == ENOMEM);
308	if (st == -1)
309		err(1, "could not sysctl(KERN_PROC)");
310	if (size % sizeof(struct kinfo_proc) != 0) {
311		fprintf(stderr, "proc size mismatch (%zu total, %zu chunks)\n",
312			size, sizeof(struct kinfo_proc));
313		fprintf(stderr, "userland out of sync with kernel\n");
314		exit(1);
315	}
316	nprocs = size / sizeof(struct kinfo_proc);
317	if (dflag)
318		printf("nprocs %d\n", nprocs);
319	mypid = getpid();
320
321	for (i = 0; i < nprocs; i++) {
322		if ((procs[i].ki_stat & SZOMB) == SZOMB && !zflag)
323			continue;
324		thispid = procs[i].ki_pid;
325		strlcpy(thiscmd, procs[i].ki_comm, sizeof(thiscmd));
326		thistdev = procs[i].ki_tdev;
327		if (eflag)
328			thisuid = procs[i].ki_uid;	/* effective uid */
329		else
330			thisuid = procs[i].ki_ruid;	/* real uid */
331
332		if (thispid == mypid)
333			continue;
334		matched = 1;
335		if (user) {
336			if (thisuid != uid)
337				matched = 0;
338		}
339		if (tty) {
340			if (thistdev != tdev)
341				matched = 0;
342		}
343		if (cmd) {
344			if (mflag) {
345				if (regcomp(&rgx, cmd,
346				    REG_EXTENDED|REG_NOSUB) != 0) {
347					mflag = 0;
348					warnx("%s: illegal regexp", cmd);
349				}
350			}
351			if (mflag) {
352				pmatch.rm_so = 0;
353				pmatch.rm_eo = strlen(thiscmd);
354				if (regexec(&rgx, thiscmd, 0, &pmatch,
355				    REG_STARTEND) != 0)
356					matched = 0;
357				regfree(&rgx);
358			} else {
359				if (strncmp(thiscmd, cmd, MAXCOMLEN) != 0)
360					matched = 0;
361			}
362		}
363		if (jflag && thispid == getpid())
364			matched = 0;
365		if (matched == 0)
366			continue;
367		if (ac > 0)
368			matched = 0;
369		for (j = 0; j < ac; j++) {
370			if (mflag) {
371				if (regcomp(&rgx, av[j],
372				    REG_EXTENDED|REG_NOSUB) != 0) {
373					mflag = 0;
374					warnx("%s: illegal regexp", av[j]);
375				}
376			}
377			if (mflag) {
378				pmatch.rm_so = 0;
379				pmatch.rm_eo = strlen(thiscmd);
380				if (regexec(&rgx, thiscmd, 0, &pmatch,
381				    REG_STARTEND) == 0)
382					matched = 1;
383				regfree(&rgx);
384			} else {
385				if (strcmp(thiscmd, av[j]) == 0)
386					matched = 1;
387			}
388			if (matched)
389				break;
390		}
391		if (matched != 0 && Iflag) {
392			printf("Send signal %d to %s (pid %d uid %d)? ",
393				sig, thiscmd, thispid, thisuid);
394			fflush(stdout);
395			first = ch = getchar();
396			while (ch != '\n' && ch != EOF)
397				ch = getchar();
398			if (first != 'y' && first != 'Y')
399				matched = 0;
400		}
401		if (matched == 0)
402			continue;
403		if (dflag)
404			printf("sig:%d, cmd:%s, pid:%d, dev:0x%x uid:%d\n", sig,
405			    thiscmd, thispid, thistdev, thisuid);
406
407		if (vflag || sflag)
408			printf("kill -%s %d\n", sys_signame[sig], thispid);
409
410		killed++;
411		if (!dflag && !sflag) {
412			if (kill(thispid, sig) < 0 /* && errno != ESRCH */ ) {
413				warn("warning: kill -%s %d",
414				    sys_signame[sig], thispid);
415				errors = 1;
416			}
417		}
418	}
419	if (killed == 0) {
420		fprintf(stderr, "No matching processes %swere found\n",
421		    getuid() != 0 ? "belonging to you " : "");
422		errors = 1;
423	}
424	exit(errors);
425}
426