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$");
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 [-delmsqvz] [-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		qflag = 0;
105	int		vflag = 0;
106	int		sflag = 0;
107	int		dflag = 0;
108	int		eflag = 0;
109	int		Iflag = 0;
110	int		jflag = 0;
111	int		mflag = 0;
112	int		zflag = 0;
113	uid_t		uid = 0;
114	dev_t		tdev = 0;
115	pid_t		mypid;
116	char		thiscmd[MAXCOMLEN + 1];
117	pid_t		thispid;
118	uid_t		thisuid;
119	dev_t		thistdev;
120	int		sig = SIGTERM;
121	const char *const *p;
122	char		*ep;
123	int		errors = 0;
124	int		jid;
125	int		mib[4];
126	size_t		miblen;
127	int		st, nprocs;
128	size_t		size;
129	int		matched;
130	int		killed = 0;
131
132	setlocale(LC_ALL, "");
133
134	av++;
135	ac--;
136
137	while (ac > 0) {
138		if (strcmp(*av, "-l") == 0) {
139			printsig(stdout);
140			exit(0);
141		}
142		if (strcmp(*av, "-help") == 0)
143			usage();
144		if (**av == '-') {
145			++*av;
146			switch (**av) {
147			case 'I':
148				Iflag = 1;
149				break;
150			case 'j':
151				++*av;
152				if (**av == '\0') {
153					++av;
154					--ac;
155				}
156				jflag++;
157				if (*av == NULL)
158				    	errx(1, "must specify jail");
159				jid = jail_getid(*av);
160				if (jid < 0)
161					errx(1, "%s", jail_errmsg);
162				if (jail_attach(jid) == -1)
163					err(1, "jail_attach(%d)", jid);
164				break;
165			case 'u':
166				++*av;
167				if (**av == '\0') {
168					++av;
169					--ac;
170				}
171				if (*av == NULL)
172				    	errx(1, "must specify user");
173				user = *av;
174				break;
175			case 't':
176				++*av;
177				if (**av == '\0') {
178					++av;
179					--ac;
180				}
181				if (*av == NULL)
182				    	errx(1, "must specify tty");
183				tty = *av;
184				break;
185			case 'c':
186				++*av;
187				if (**av == '\0') {
188					++av;
189					--ac;
190				}
191				if (*av == NULL)
192				    	errx(1, "must specify procname");
193				cmd = *av;
194				break;
195			case 'q':
196				qflag++;
197				break;
198			case 'v':
199				vflag++;
200				break;
201			case 's':
202				sflag++;
203				break;
204			case 'd':
205				dflag++;
206				break;
207			case 'e':
208				eflag++;
209				break;
210			case 'm':
211				mflag++;
212				break;
213			case 'z':
214				zflag++;
215				break;
216			default:
217				if (isalpha((unsigned char)**av)) {
218					if (strncasecmp(*av, "SIG", 3) == 0)
219						*av += 3;
220					for (sig = NSIG, p = sys_signame + 1;
221					     --sig; ++p)
222						if (strcasecmp(*p, *av) == 0) {
223							sig = p - sys_signame;
224							break;
225						}
226					if (!sig)
227						nosig(*av);
228				} else if (isdigit((unsigned char)**av)) {
229					sig = strtol(*av, &ep, 10);
230					if (!*av || *ep)
231						errx(1, "illegal signal number: %s", *av);
232					if (sig < 0 || sig >= NSIG)
233						nosig(*av);
234				} else
235					nosig(*av);
236			}
237			++av;
238			--ac;
239		} else {
240			break;
241		}
242	}
243
244	if (user == NULL && tty == NULL && cmd == NULL && !jflag && ac == 0)
245		usage();
246
247	if (tty) {
248		if (strncmp(tty, "/dev/", 5) == 0)
249			snprintf(buf, sizeof(buf), "%s", tty);
250		else if (strncmp(tty, "tty", 3) == 0)
251			snprintf(buf, sizeof(buf), "/dev/%s", tty);
252		else
253			snprintf(buf, sizeof(buf), "/dev/tty%s", tty);
254		if (stat(buf, &sb) < 0)
255			err(1, "stat(%s)", buf);
256		if (!S_ISCHR(sb.st_mode))
257			errx(1, "%s: not a character device", buf);
258		tdev = sb.st_rdev;
259		if (dflag)
260			printf("ttydev:0x%x\n", tdev);
261	}
262	if (user) {
263		uid = strtol(user, &ep, 10);
264		if (*user == '\0' || *ep != '\0') { /* was it a number? */
265			pw = getpwnam(user);
266			if (pw == NULL)
267				errx(1, "user %s does not exist", user);
268			uid = pw->pw_uid;
269			if (dflag)
270				printf("uid:%d\n", uid);
271		}
272	} else {
273		uid = getuid();
274		if (uid != 0) {
275			pw = getpwuid(uid);
276			if (pw)
277				user = pw->pw_name;
278			if (dflag)
279				printf("uid:%d\n", uid);
280		}
281	}
282	size = 0;
283	mib[0] = CTL_KERN;
284	mib[1] = KERN_PROC;
285
286	if (user) {
287		mib[2] = eflag ? KERN_PROC_UID : KERN_PROC_RUID;
288		mib[3] = uid;
289		miblen = 4;
290	} else if (tty) {
291		mib[2] = KERN_PROC_TTY;
292		mib[3] = tdev;
293		miblen = 4;
294	} else {
295		mib[2] = KERN_PROC_PROC;
296		mib[3] = 0;
297		miblen = 3;
298	}
299
300	procs = NULL;
301	st = sysctl(mib, miblen, NULL, &size, NULL, 0);
302	do {
303		size += size / 10;
304		newprocs = realloc(procs, size);
305		if (newprocs == NULL) {
306			free(procs);
307			err(1, "could not reallocate memory");
308		}
309		procs = newprocs;
310		st = sysctl(mib, miblen, procs, &size, NULL, 0);
311	} while (st == -1 && errno == ENOMEM);
312	if (st == -1)
313		err(1, "could not sysctl(KERN_PROC)");
314	if (size % sizeof(struct kinfo_proc) != 0) {
315		fprintf(stderr, "proc size mismatch (%zu total, %zu chunks)\n",
316			size, sizeof(struct kinfo_proc));
317		fprintf(stderr, "userland out of sync with kernel\n");
318		exit(1);
319	}
320	nprocs = size / sizeof(struct kinfo_proc);
321	if (dflag)
322		printf("nprocs %d\n", nprocs);
323	mypid = getpid();
324
325	for (i = 0; i < nprocs; i++) {
326		if (procs[i].ki_stat == SZOMB && !zflag)
327			continue;
328		thispid = procs[i].ki_pid;
329		strlcpy(thiscmd, procs[i].ki_comm, sizeof(thiscmd));
330		thistdev = procs[i].ki_tdev;
331		if (eflag)
332			thisuid = procs[i].ki_uid;	/* effective uid */
333		else
334			thisuid = procs[i].ki_ruid;	/* real uid */
335
336		if (thispid == mypid)
337			continue;
338		matched = 1;
339		if (user) {
340			if (thisuid != uid)
341				matched = 0;
342		}
343		if (tty) {
344			if (thistdev != tdev)
345				matched = 0;
346		}
347		if (cmd) {
348			if (mflag) {
349				if (regcomp(&rgx, cmd,
350				    REG_EXTENDED|REG_NOSUB) != 0) {
351					mflag = 0;
352					warnx("%s: illegal regexp", cmd);
353				}
354			}
355			if (mflag) {
356				pmatch.rm_so = 0;
357				pmatch.rm_eo = strlen(thiscmd);
358				if (regexec(&rgx, thiscmd, 0, &pmatch,
359				    REG_STARTEND) != 0)
360					matched = 0;
361				regfree(&rgx);
362			} else {
363				if (strncmp(thiscmd, cmd, MAXCOMLEN) != 0)
364					matched = 0;
365			}
366		}
367		if (jflag && thispid == getpid())
368			matched = 0;
369		if (matched == 0)
370			continue;
371		if (ac > 0)
372			matched = 0;
373		for (j = 0; j < ac; j++) {
374			if (mflag) {
375				if (regcomp(&rgx, av[j],
376				    REG_EXTENDED|REG_NOSUB) != 0) {
377					mflag = 0;
378					warnx("%s: illegal regexp", av[j]);
379				}
380			}
381			if (mflag) {
382				pmatch.rm_so = 0;
383				pmatch.rm_eo = strlen(thiscmd);
384				if (regexec(&rgx, thiscmd, 0, &pmatch,
385				    REG_STARTEND) == 0)
386					matched = 1;
387				regfree(&rgx);
388			} else {
389				if (strcmp(thiscmd, av[j]) == 0)
390					matched = 1;
391			}
392			if (matched)
393				break;
394		}
395		if (matched != 0 && Iflag) {
396			printf("Send signal %d to %s (pid %d uid %d)? ",
397				sig, thiscmd, thispid, thisuid);
398			fflush(stdout);
399			first = ch = getchar();
400			while (ch != '\n' && ch != EOF)
401				ch = getchar();
402			if (first != 'y' && first != 'Y')
403				matched = 0;
404		}
405		if (matched == 0)
406			continue;
407		if (dflag)
408			printf("sig:%d, cmd:%s, pid:%d, dev:0x%x uid:%d\n", sig,
409			    thiscmd, thispid, thistdev, thisuid);
410
411		if (vflag || sflag)
412			printf("kill -%s %d\n", sys_signame[sig], thispid);
413
414		killed++;
415		if (!dflag && !sflag) {
416			if (kill(thispid, sig) < 0 /* && errno != ESRCH */ ) {
417				warn("warning: kill -%s %d",
418				    sys_signame[sig], thispid);
419				errors = 1;
420			}
421		}
422	}
423	if (killed == 0) {
424		if (!qflag)
425			fprintf(stderr, "No matching processes %swere found\n",
426			    getuid() != 0 ? "belonging to you " : "");
427		errors = 1;
428	}
429	exit(errors);
430}
431