killall.c revision 256281
1254219Scy/*-
2254219Scy * Copyright (c) 2000 Peter Wemm <peter@FreeBSD.org>
3254219Scy * Copyright (c) 2000 Paul Saab <ps@FreeBSD.org>
4254219Scy * All rights reserved.
5254219Scy *
6254219Scy * Redistribution and use in source and binary forms, with or without
7254219Scy * modification, are permitted provided that the following conditions
8254219Scy * are met:
9254219Scy * 1. Redistributions of source code must retain the above copyright
10254219Scy *    notice, this list of conditions and the following disclaimer.
11254219Scy * 2. Redistributions in binary form must reproduce the above copyright
12254219Scy *    notice, this list of conditions and the following disclaimer in the
13254219Scy *    documentation and/or other materials provided with the distribution.
14254219Scy *
15254219Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16254219Scy * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17254219Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18254219Scy * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19254219Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20254219Scy * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21254219Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22254219Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23254219Scy * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24254219Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25254219Scy * SUCH DAMAGE.
26254219Scy */
27254219Scy
28254219Scy#include <sys/cdefs.h>
29254219Scy__FBSDID("$FreeBSD: stable/10/usr.bin/killall/killall.c 252428 2013-06-30 20:27:31Z mjg $");
30254219Scy
31254219Scy#include <sys/param.h>
32254219Scy#include <sys/jail.h>
33254219Scy#include <sys/stat.h>
34254219Scy#include <sys/uio.h>
35254219Scy#include <sys/user.h>
36254219Scy#include <sys/sysctl.h>
37254219Scy#include <fcntl.h>
38254219Scy#include <dirent.h>
39254219Scy#include <jail.h>
40254219Scy#include <stdio.h>
41254219Scy#include <stdlib.h>
42254219Scy#include <string.h>
43254219Scy#include <pwd.h>
44254219Scy#include <signal.h>
45254219Scy#include <regex.h>
46254219Scy#include <ctype.h>
47254219Scy#include <err.h>
48254219Scy#include <errno.h>
49254219Scy#include <unistd.h>
50254219Scy#include <locale.h>
51254219Scy
52254219Scystatic void __dead2
53254219Scyusage(void)
54254219Scy{
55254219Scy
56254219Scy	fprintf(stderr, "usage: killall [-delmsqvz] [-help] [-I] [-j jail]\n");
57254219Scy	fprintf(stderr,
58254219Scy	    "               [-u user] [-t tty] [-c cmd] [-SIGNAL] [cmd]...\n");
59254219Scy	fprintf(stderr, "At least one option or argument to specify processes must be given.\n");
60254219Scy	exit(1);
61254219Scy}
62254219Scy
63254219Scy
64254219Scystatic void
65254219Scyprintsig(FILE *fp)
66254219Scy{
67254219Scy	const char	*const * p;
68254219Scy	int		cnt;
69254219Scy	int		offset = 0;
70254219Scy
71254219Scy	for (cnt = NSIG, p = sys_signame + 1; --cnt; ++p) {
72254219Scy		offset += fprintf(fp, "%s ", *p);
73254219Scy		if (offset >= 75 && cnt > 1) {
74254219Scy			offset = 0;
75254219Scy			fprintf(fp, "\n");
76254219Scy		}
77254219Scy	}
78254219Scy	fprintf(fp, "\n");
79254219Scy}
80254219Scy
81254219Scystatic void
82254219Scynosig(char *name)
83254219Scy{
84254219Scy
85254219Scy	warnx("unknown signal %s; valid signals:", name);
86254219Scy	printsig(stderr);
87254219Scy	exit(1);
88254219Scy}
89254219Scy
90254219Scyint
91254219Scymain(int ac, char **av)
92254219Scy{
93254219Scy	struct kinfo_proc *procs, *newprocs;
94254219Scy	struct stat	sb;
95254219Scy	struct passwd	*pw;
96254219Scy	regex_t		rgx;
97254219Scy	regmatch_t	pmatch;
98254219Scy	int		i, j, ch;
99254219Scy	char		buf[256];
100254219Scy	char		first;
101254219Scy	char		*user = NULL;
102254219Scy	char		*tty = NULL;
103254219Scy	char		*cmd = NULL;
104254219Scy	int		qflag = 0;
105254219Scy	int		vflag = 0;
106254219Scy	int		sflag = 0;
107254219Scy	int		dflag = 0;
108254219Scy	int		eflag = 0;
109254219Scy	int		Iflag = 0;
110254219Scy	int		jflag = 0;
111254219Scy	int		mflag = 0;
112254219Scy	int		zflag = 0;
113254219Scy	uid_t		uid = 0;
114254219Scy	dev_t		tdev = 0;
115254219Scy	pid_t		mypid;
116254219Scy	char		thiscmd[MAXCOMLEN + 1];
117254219Scy	pid_t		thispid;
118254219Scy	uid_t		thisuid;
119254219Scy	dev_t		thistdev;
120254219Scy	int		sig = SIGTERM;
121254219Scy	const char *const *p;
122254219Scy	char		*ep;
123254219Scy	int		errors = 0;
124254219Scy	int		jid;
125254219Scy	int		mib[4];
126254219Scy	size_t		miblen;
127254219Scy	int		st, nprocs;
128254219Scy	size_t		size;
129254219Scy	int		matched;
130254219Scy	int		killed = 0;
131254219Scy
132254219Scy	setlocale(LC_ALL, "");
133254219Scy
134254219Scy	av++;
135254219Scy	ac--;
136254219Scy
137254219Scy	while (ac > 0) {
138254219Scy		if (strcmp(*av, "-l") == 0) {
139254219Scy			printsig(stdout);
140254219Scy			exit(0);
141254219Scy		}
142254219Scy		if (strcmp(*av, "-help") == 0)
143254219Scy			usage();
144254219Scy		if (**av == '-') {
145254219Scy			++*av;
146254219Scy			switch (**av) {
147254219Scy			case 'I':
148254219Scy				Iflag = 1;
149254219Scy				break;
150254219Scy			case 'j':
151254219Scy				++*av;
152254219Scy				if (**av == '\0') {
153254219Scy					++av;
154254219Scy					--ac;
155254219Scy				}
156254219Scy				jflag++;
157254219Scy				if (*av == NULL)
158254219Scy				    	errx(1, "must specify jail");
159254219Scy				jid = jail_getid(*av);
160254219Scy				if (jid < 0)
161254219Scy					errx(1, "%s", jail_errmsg);
162254219Scy				if (jail_attach(jid) == -1)
163254219Scy					err(1, "jail_attach(%d)", jid);
164254219Scy				break;
165254219Scy			case 'u':
166254219Scy				++*av;
167254219Scy				if (**av == '\0') {
168254219Scy					++av;
169254219Scy					--ac;
170254219Scy				}
171254219Scy				if (*av == NULL)
172254219Scy				    	errx(1, "must specify user");
173254219Scy				user = *av;
174254219Scy				break;
175254219Scy			case 't':
176254219Scy				++*av;
177254219Scy				if (**av == '\0') {
178254219Scy					++av;
179254219Scy					--ac;
180254219Scy				}
181254219Scy				if (*av == NULL)
182254219Scy				    	errx(1, "must specify tty");
183254219Scy				tty = *av;
184254219Scy				break;
185254219Scy			case 'c':
186254219Scy				++*av;
187254219Scy				if (**av == '\0') {
188254219Scy					++av;
189254219Scy					--ac;
190254219Scy				}
191254219Scy				if (*av == NULL)
192254219Scy				    	errx(1, "must specify procname");
193254219Scy				cmd = *av;
194254219Scy				break;
195254219Scy			case 'q':
196254219Scy				qflag++;
197254219Scy				break;
198254219Scy			case 'v':
199254219Scy				vflag++;
200254219Scy				break;
201254219Scy			case 's':
202254219Scy				sflag++;
203254219Scy				break;
204254219Scy			case 'd':
205254219Scy				dflag++;
206254219Scy				break;
207254219Scy			case 'e':
208254219Scy				eflag++;
209254219Scy				break;
210254219Scy			case 'm':
211254219Scy				mflag++;
212254219Scy				break;
213254219Scy			case 'z':
214254219Scy				zflag++;
215254219Scy				break;
216254219Scy			default:
217254219Scy				if (isalpha((unsigned char)**av)) {
218254219Scy					if (strncasecmp(*av, "SIG", 3) == 0)
219254219Scy						*av += 3;
220254219Scy					for (sig = NSIG, p = sys_signame + 1;
221254219Scy					     --sig; ++p)
222254219Scy						if (strcasecmp(*p, *av) == 0) {
223254219Scy							sig = p - sys_signame;
224254219Scy							break;
225254219Scy						}
226254219Scy					if (!sig)
227254219Scy						nosig(*av);
228254219Scy				} else if (isdigit((unsigned char)**av)) {
229254219Scy					sig = strtol(*av, &ep, 10);
230254219Scy					if (!*av || *ep)
231254219Scy						errx(1, "illegal signal number: %s", *av);
232254219Scy					if (sig < 0 || sig >= NSIG)
233254219Scy						nosig(*av);
234254219Scy				} else
235254219Scy					nosig(*av);
236254219Scy			}
237254219Scy			++av;
238254219Scy			--ac;
239254219Scy		} else {
240254219Scy			break;
241254219Scy		}
242254219Scy	}
243254219Scy
244254219Scy	if (user == NULL && tty == NULL && cmd == NULL && !jflag && ac == 0)
245254219Scy		usage();
246254219Scy
247254219Scy	if (tty) {
248254219Scy		if (strncmp(tty, "/dev/", 5) == 0)
249254219Scy			snprintf(buf, sizeof(buf), "%s", tty);
250254219Scy		else if (strncmp(tty, "tty", 3) == 0)
251254219Scy			snprintf(buf, sizeof(buf), "/dev/%s", tty);
252254219Scy		else
253254219Scy			snprintf(buf, sizeof(buf), "/dev/tty%s", tty);
254254219Scy		if (stat(buf, &sb) < 0)
255254219Scy			err(1, "stat(%s)", buf);
256254219Scy		if (!S_ISCHR(sb.st_mode))
257254219Scy			errx(1, "%s: not a character device", buf);
258254219Scy		tdev = sb.st_rdev;
259254219Scy		if (dflag)
260254219Scy			printf("ttydev:0x%x\n", tdev);
261254219Scy	}
262254219Scy	if (user) {
263254219Scy		uid = strtol(user, &ep, 10);
264254219Scy		if (*user == '\0' || *ep != '\0') { /* was it a number? */
265254219Scy			pw = getpwnam(user);
266254219Scy			if (pw == NULL)
267254219Scy				errx(1, "user %s does not exist", user);
268254219Scy			uid = pw->pw_uid;
269254219Scy			if (dflag)
270254219Scy				printf("uid:%d\n", uid);
271254219Scy		}
272254219Scy	} else {
273254219Scy		uid = getuid();
274254219Scy		if (uid != 0) {
275254219Scy			pw = getpwuid(uid);
276254219Scy			if (pw)
277254219Scy				user = pw->pw_name;
278254219Scy			if (dflag)
279254219Scy				printf("uid:%d\n", uid);
280254219Scy		}
281254219Scy	}
282254219Scy	size = 0;
283254219Scy	mib[0] = CTL_KERN;
284254219Scy	mib[1] = KERN_PROC;
285254219Scy
286254219Scy	if (user) {
287254219Scy		mib[2] = eflag ? KERN_PROC_UID : KERN_PROC_RUID;
288254219Scy		mib[3] = uid;
289254219Scy		miblen = 4;
290254219Scy	} else if (tty) {
291254219Scy		mib[2] = KERN_PROC_TTY;
292254219Scy		mib[3] = tdev;
293254219Scy		miblen = 4;
294254219Scy	} else {
295254219Scy		mib[2] = KERN_PROC_PROC;
296254219Scy		mib[3] = 0;
297254219Scy		miblen = 3;
298254219Scy	}
299254219Scy
300254219Scy	procs = NULL;
301254219Scy	st = sysctl(mib, miblen, NULL, &size, NULL, 0);
302254219Scy	do {
303254219Scy		size += size / 10;
304254219Scy		newprocs = realloc(procs, size);
305254219Scy		if (newprocs == NULL) {
306254219Scy			free(procs);
307254219Scy			err(1, "could not reallocate memory");
308254219Scy		}
309254219Scy		procs = newprocs;
310254219Scy		st = sysctl(mib, miblen, procs, &size, NULL, 0);
311254219Scy	} while (st == -1 && errno == ENOMEM);
312254219Scy	if (st == -1)
313254219Scy		err(1, "could not sysctl(KERN_PROC)");
314254219Scy	if (size % sizeof(struct kinfo_proc) != 0) {
315254219Scy		fprintf(stderr, "proc size mismatch (%zu total, %zu chunks)\n",
316254219Scy			size, sizeof(struct kinfo_proc));
317254219Scy		fprintf(stderr, "userland out of sync with kernel\n");
318254219Scy		exit(1);
319254219Scy	}
320254219Scy	nprocs = size / sizeof(struct kinfo_proc);
321254219Scy	if (dflag)
322254219Scy		printf("nprocs %d\n", nprocs);
323254219Scy	mypid = getpid();
324254219Scy
325254219Scy	for (i = 0; i < nprocs; i++) {
326254219Scy		if (procs[i].ki_stat == SZOMB && !zflag)
327254219Scy			continue;
328254219Scy		thispid = procs[i].ki_pid;
329254219Scy		strlcpy(thiscmd, procs[i].ki_comm, sizeof(thiscmd));
330254219Scy		thistdev = procs[i].ki_tdev;
331254219Scy		if (eflag)
332254219Scy			thisuid = procs[i].ki_uid;	/* effective uid */
333254219Scy		else
334254219Scy			thisuid = procs[i].ki_ruid;	/* real uid */
335254219Scy
336254219Scy		if (thispid == mypid)
337254219Scy			continue;
338254219Scy		matched = 1;
339254219Scy		if (user) {
340254219Scy			if (thisuid != uid)
341254219Scy				matched = 0;
342254219Scy		}
343254219Scy		if (tty) {
344254219Scy			if (thistdev != tdev)
345254219Scy				matched = 0;
346254219Scy		}
347254219Scy		if (cmd) {
348254219Scy			if (mflag) {
349254219Scy				if (regcomp(&rgx, cmd,
350254219Scy				    REG_EXTENDED|REG_NOSUB) != 0) {
351254219Scy					mflag = 0;
352254219Scy					warnx("%s: illegal regexp", cmd);
353254219Scy				}
354254219Scy			}
355254219Scy			if (mflag) {
356254219Scy				pmatch.rm_so = 0;
357254219Scy				pmatch.rm_eo = strlen(thiscmd);
358254219Scy				if (regexec(&rgx, thiscmd, 0, &pmatch,
359254219Scy				    REG_STARTEND) != 0)
360254219Scy					matched = 0;
361254219Scy				regfree(&rgx);
362254219Scy			} else {
363254219Scy				if (strncmp(thiscmd, cmd, MAXCOMLEN) != 0)
364254219Scy					matched = 0;
365254219Scy			}
366254219Scy		}
367254219Scy		if (jflag && thispid == getpid())
368254219Scy			matched = 0;
369254219Scy		if (matched == 0)
370254219Scy			continue;
371254219Scy		if (ac > 0)
372254219Scy			matched = 0;
373254219Scy		for (j = 0; j < ac; j++) {
374254219Scy			if (mflag) {
375254219Scy				if (regcomp(&rgx, av[j],
376254219Scy				    REG_EXTENDED|REG_NOSUB) != 0) {
377254219Scy					mflag = 0;
378254219Scy					warnx("%s: illegal regexp", av[j]);
379254219Scy				}
380254219Scy			}
381254219Scy			if (mflag) {
382254219Scy				pmatch.rm_so = 0;
383254219Scy				pmatch.rm_eo = strlen(thiscmd);
384254219Scy				if (regexec(&rgx, thiscmd, 0, &pmatch,
385254219Scy				    REG_STARTEND) == 0)
386254219Scy					matched = 1;
387254219Scy				regfree(&rgx);
388254219Scy			} else {
389254219Scy				if (strcmp(thiscmd, av[j]) == 0)
390254219Scy					matched = 1;
391254219Scy			}
392254219Scy			if (matched)
393254219Scy				break;
394254219Scy		}
395254219Scy		if (matched != 0 && Iflag) {
396254219Scy			printf("Send signal %d to %s (pid %d uid %d)? ",
397254219Scy				sig, thiscmd, thispid, thisuid);
398254219Scy			fflush(stdout);
399254219Scy			first = ch = getchar();
400254219Scy			while (ch != '\n' && ch != EOF)
401254219Scy				ch = getchar();
402254219Scy			if (first != 'y' && first != 'Y')
403254219Scy				matched = 0;
404254219Scy		}
405254219Scy		if (matched == 0)
406254219Scy			continue;
407254219Scy		if (dflag)
408254219Scy			printf("sig:%d, cmd:%s, pid:%d, dev:0x%x uid:%d\n", sig,
409254219Scy			    thiscmd, thispid, thistdev, thisuid);
410254219Scy
411254219Scy		if (vflag || sflag)
412254219Scy			printf("kill -%s %d\n", sys_signame[sig], thispid);
413254219Scy
414254219Scy		killed++;
415254219Scy		if (!dflag && !sflag) {
416254219Scy			if (kill(thispid, sig) < 0 /* && errno != ESRCH */ ) {
417254219Scy				warn("warning: kill -%s %d",
418254219Scy				    sys_signame[sig], thispid);
419254219Scy				errors = 1;
420254219Scy			}
421254219Scy		}
422254219Scy	}
423254219Scy	if (killed == 0) {
424254219Scy		if (!qflag)
425254219Scy			fprintf(stderr, "No matching processes %swere found\n",
426254219Scy			    getuid() != 0 ? "belonging to you " : "");
427254219Scy		errors = 1;
428254219Scy	}
429254219Scy	exit(errors);
430254219Scy}
431254219Scy