killall.c revision 237844
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 237844 2012-06-30 16:20:01Z 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	mib[2] = KERN_PROC_PROC;
282	mib[3] = 0;
283	miblen = 3;
284
285	if (user) {
286		mib[2] = eflag ? KERN_PROC_UID : KERN_PROC_RUID;
287		mib[3] = uid;
288		miblen = 4;
289	} else if (tty) {
290		mib[2] = KERN_PROC_TTY;
291		mib[3] = tdev;
292		miblen = 4;
293	}
294
295	procs = NULL;
296	st = sysctl(mib, miblen, NULL, &size, NULL, 0);
297	do {
298		size += size / 10;
299		newprocs = realloc(procs, size);
300		if (newprocs == NULL) {
301			free(procs);
302			err(1, "could not reallocate memory");
303		}
304		procs = newprocs;
305		st = sysctl(mib, miblen, procs, &size, NULL, 0);
306	} while (st == -1 && errno == ENOMEM);
307	if (st == -1)
308		err(1, "could not sysctl(KERN_PROC)");
309	if (size % sizeof(struct kinfo_proc) != 0) {
310		fprintf(stderr, "proc size mismatch (%zu total, %zu chunks)\n",
311			size, sizeof(struct kinfo_proc));
312		fprintf(stderr, "userland out of sync with kernel, recompile libkvm etc\n");
313		exit(1);
314	}
315	nprocs = size / sizeof(struct kinfo_proc);
316	if (dflag)
317		printf("nprocs %d\n", nprocs);
318	mypid = getpid();
319
320	for (i = 0; i < nprocs; i++) {
321		if ((procs[i].ki_stat & SZOMB) == SZOMB && !zflag)
322			continue;
323		thispid = procs[i].ki_pid;
324		strlcpy(thiscmd, procs[i].ki_comm, sizeof(thiscmd));
325		thistdev = procs[i].ki_tdev;
326		if (eflag)
327			thisuid = procs[i].ki_uid;	/* effective uid */
328		else
329			thisuid = procs[i].ki_ruid;	/* real uid */
330
331		if (thispid == mypid)
332			continue;
333		matched = 1;
334		if (user) {
335			if (thisuid != uid)
336				matched = 0;
337		}
338		if (tty) {
339			if (thistdev != tdev)
340				matched = 0;
341		}
342		if (cmd) {
343			if (mflag) {
344				if (regcomp(&rgx, cmd,
345				    REG_EXTENDED|REG_NOSUB) != 0) {
346					mflag = 0;
347					warnx("%s: illegal regexp", cmd);
348				}
349			}
350			if (mflag) {
351				pmatch.rm_so = 0;
352				pmatch.rm_eo = strlen(thiscmd);
353				if (regexec(&rgx, thiscmd, 0, &pmatch,
354				    REG_STARTEND) != 0)
355					matched = 0;
356				regfree(&rgx);
357			} else {
358				if (strncmp(thiscmd, cmd, MAXCOMLEN) != 0)
359					matched = 0;
360			}
361		}
362		if (jflag && thispid == getpid())
363			matched = 0;
364		if (matched == 0)
365			continue;
366		if (ac > 0)
367			matched = 0;
368		for (j = 0; j < ac; j++) {
369			if (mflag) {
370				if (regcomp(&rgx, av[j],
371				    REG_EXTENDED|REG_NOSUB) != 0) {
372					mflag = 0;
373					warnx("%s: illegal regexp", av[j]);
374				}
375			}
376			if (mflag) {
377				pmatch.rm_so = 0;
378				pmatch.rm_eo = strlen(thiscmd);
379				if (regexec(&rgx, thiscmd, 0, &pmatch,
380				    REG_STARTEND) == 0)
381					matched = 1;
382				regfree(&rgx);
383			} else {
384				if (strcmp(thiscmd, av[j]) == 0)
385					matched = 1;
386			}
387			if (matched)
388				break;
389		}
390		if (matched != 0 && Iflag) {
391			printf("Send signal %d to %s (pid %d uid %d)? ",
392				sig, thiscmd, thispid, thisuid);
393			fflush(stdout);
394			first = ch = getchar();
395			while (ch != '\n' && ch != EOF)
396				ch = getchar();
397			if (first != 'y' && first != 'Y')
398				matched = 0;
399		}
400		if (matched == 0)
401			continue;
402		if (dflag)
403			printf("sig:%d, cmd:%s, pid:%d, dev:0x%x uid:%d\n", sig,
404			    thiscmd, thispid, thistdev, thisuid);
405
406		if (vflag || sflag)
407			printf("kill -%s %d\n", sys_signame[sig], thispid);
408
409		killed++;
410		if (!dflag && !sflag) {
411			if (kill(thispid, sig) < 0 /* && errno != ESRCH */ ) {
412				warn("warning: kill -%s %d",
413				    sys_signame[sig], thispid);
414				errors = 1;
415			}
416		}
417	}
418	if (killed == 0) {
419		fprintf(stderr, "No matching processes %swere found\n",
420		    getuid() != 0 ? "belonging to you " : "");
421		errors = 1;
422	}
423	exit(errors);
424}
425