killall.c revision 274471
1254721Semaste/*-
2254721Semaste * Copyright (c) 2000 Peter Wemm <peter@FreeBSD.org>
3254721Semaste * Copyright (c) 2000 Paul Saab <ps@FreeBSD.org>
4254721Semaste * All rights reserved.
5254721Semaste *
6254721Semaste * Redistribution and use in source and binary forms, with or without
7254721Semaste * modification, are permitted provided that the following conditions
8254721Semaste * are met:
9254721Semaste * 1. Redistributions of source code must retain the above copyright
10254721Semaste *    notice, this list of conditions and the following disclaimer.
11254721Semaste * 2. Redistributions in binary form must reproduce the above copyright
12254721Semaste *    notice, this list of conditions and the following disclaimer in the
13254721Semaste *    documentation and/or other materials provided with the distribution.
14254721Semaste *
15254721Semaste * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
16254721Semaste * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
17254721Semaste * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18254721Semaste * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
19254721Semaste * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20254721Semaste * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21254721Semaste * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22254721Semaste * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
23254721Semaste * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
24254721Semaste * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
25254721Semaste * SUCH DAMAGE.
26254721Semaste */
27254721Semaste
28254721Semaste#include <sys/cdefs.h>
29254721Semaste__FBSDID("$FreeBSD: stable/10/usr.bin/killall/killall.c 274471 2014-11-13 16:40:15Z smh $");
30254721Semaste
31254721Semaste#include <sys/param.h>
32254721Semaste#include <sys/jail.h>
33254721Semaste#include <sys/stat.h>
34254721Semaste#include <sys/uio.h>
35254721Semaste#include <sys/user.h>
36254721Semaste#include <sys/sysctl.h>
37254721Semaste#include <fcntl.h>
38254721Semaste#include <dirent.h>
39254721Semaste#include <jail.h>
40254721Semaste#include <stdio.h>
41254721Semaste#include <stdlib.h>
42254721Semaste#include <string.h>
43254721Semaste#include <pwd.h>
44254721Semaste#include <signal.h>
45254721Semaste#include <regex.h>
46254721Semaste#include <ctype.h>
47254721Semaste#include <err.h>
48254721Semaste#include <errno.h>
49254721Semaste#include <unistd.h>
50254721Semaste#include <locale.h>
51254721Semaste
52254721Semastestatic void __dead2
53254721Semasteusage(void)
54254721Semaste{
55254721Semaste
56254721Semaste	fprintf(stderr, "usage: killall [-delmsqvz] [-help] [-I] [-j jail]\n");
57254721Semaste	fprintf(stderr,
58254721Semaste	    "               [-u user] [-t tty] [-c cmd] [-SIGNAL] [cmd]...\n");
59254721Semaste	fprintf(stderr, "At least one option or argument to specify processes must be given.\n");
60254721Semaste	exit(1);
61254721Semaste}
62254721Semaste
63254721Semaste
64254721Semastestatic void
65254721Semasteprintsig(FILE *fp)
66254721Semaste{
67254721Semaste	const char	*const * p;
68254721Semaste	int		cnt;
69254721Semaste	int		offset = 0;
70254721Semaste
71254721Semaste	for (cnt = NSIG, p = sys_signame + 1; --cnt; ++p) {
72254721Semaste		offset += fprintf(fp, "%s ", *p);
73254721Semaste		if (offset >= 75 && cnt > 1) {
74254721Semaste			offset = 0;
75254721Semaste			fprintf(fp, "\n");
76254721Semaste		}
77254721Semaste	}
78254721Semaste	fprintf(fp, "\n");
79254721Semaste}
80254721Semaste
81254721Semastestatic void
82254721Semastenosig(char *name)
83254721Semaste{
84254721Semaste
85254721Semaste	warnx("unknown signal %s; valid signals:", name);
86254721Semaste	printsig(stderr);
87254721Semaste	exit(1);
88254721Semaste}
89254721Semaste
90254721Semasteint
91254721Semastemain(int ac, char **av)
92254721Semaste{
93254721Semaste	char		**saved_av;
94254721Semaste	struct kinfo_proc *procs, *newprocs;
95254721Semaste	struct stat	sb;
96254721Semaste	struct passwd	*pw;
97254721Semaste	regex_t		rgx;
98254721Semaste	regmatch_t	pmatch;
99254721Semaste	int		i, j, ch;
100254721Semaste	char		buf[256];
101254721Semaste	char		first;
102254721Semaste	char		*user = NULL;
103254721Semaste	char		*tty = NULL;
104254721Semaste	char		*cmd = NULL;
105254721Semaste	int		qflag = 0;
106254721Semaste	int		vflag = 0;
107254721Semaste	int		sflag = 0;
108254721Semaste	int		dflag = 0;
109254721Semaste	int		eflag = 0;
110254721Semaste	int		Iflag = 0;
111254721Semaste	int		jflag = 0;
112254721Semaste	int		mflag = 0;
113254721Semaste	int		zflag = 0;
114254721Semaste	uid_t		uid = 0;
115254721Semaste	dev_t		tdev = 0;
116254721Semaste	pid_t		mypid;
117254721Semaste	char		thiscmd[MAXCOMLEN + 1];
118254721Semaste	pid_t		thispid;
119254721Semaste	uid_t		thisuid;
120254721Semaste	dev_t		thistdev;
121254721Semaste	int		sig = SIGTERM;
122254721Semaste	const char *const *p;
123254721Semaste	char		*ep;
124254721Semaste	int		errors = 0;
125254721Semaste	int		jid;
126254721Semaste	int		mib[4];
127254721Semaste	size_t		miblen;
128254721Semaste	int		st, nprocs;
129254721Semaste	size_t		size;
130254721Semaste	int		matched;
131254721Semaste	int		killed = 0;
132254721Semaste
133254721Semaste	setlocale(LC_ALL, "");
134254721Semaste
135254721Semaste	av++;
136254721Semaste	ac--;
137254721Semaste
138254721Semaste	while (ac > 0) {
139254721Semaste		if (strcmp(*av, "-l") == 0) {
140254721Semaste			printsig(stdout);
141254721Semaste			exit(0);
142254721Semaste		}
143254721Semaste		if (strcmp(*av, "-help") == 0)
144254721Semaste			usage();
145254721Semaste		if (**av == '-') {
146254721Semaste			++*av;
147254721Semaste			switch (**av) {
148254721Semaste			case 'j':
149254721Semaste				++*av;
150254721Semaste				if (**av == '\0') {
151254721Semaste					++av;
152254721Semaste					--ac;
153254721Semaste				}
154254721Semaste				jflag++;
155254721Semaste				if (*av == NULL)
156254721Semaste				    	errx(1, "must specify jail");
157254721Semaste				jid = jail_getid(*av);
158254721Semaste				if (jid < 0)
159254721Semaste					errx(1, "%s", jail_errmsg);
160254721Semaste				if (jail_attach(jid) == -1)
161254721Semaste					err(1, "jail_attach(%d)", jid);
162254721Semaste				break;
163254721Semaste			case 'u':
164254721Semaste				++*av;
165254721Semaste				if (**av == '\0') {
166254721Semaste					++av;
167254721Semaste					--ac;
168254721Semaste				}
169254721Semaste				if (*av == NULL)
170254721Semaste				    	errx(1, "must specify user");
171254721Semaste				user = *av;
172254721Semaste				break;
173254721Semaste			case 't':
174254721Semaste				++*av;
175254721Semaste				if (**av == '\0') {
176254721Semaste					++av;
177254721Semaste					--ac;
178254721Semaste				}
179254721Semaste				if (*av == NULL)
180254721Semaste				    	errx(1, "must specify tty");
181254721Semaste				tty = *av;
182254721Semaste				break;
183254721Semaste			case 'c':
184254721Semaste				++*av;
185254721Semaste				if (**av == '\0') {
186254721Semaste					++av;
187254721Semaste					--ac;
188254721Semaste				}
189254721Semaste				if (*av == NULL)
190254721Semaste				    	errx(1, "must specify procname");
191254721Semaste				cmd = *av;
192254721Semaste				break;
193254721Semaste			case 'q':
194254721Semaste				qflag++;
195254721Semaste				break;
196254721Semaste			case 'v':
197254721Semaste				vflag++;
198254721Semaste				break;
199254721Semaste			case 's':
200254721Semaste				sflag++;
201254721Semaste				break;
202254721Semaste			case 'd':
203254721Semaste				dflag++;
204254721Semaste				break;
205254721Semaste			case 'e':
206254721Semaste				eflag++;
207254721Semaste				break;
208254721Semaste			case 'm':
209254721Semaste				mflag++;
210254721Semaste				break;
211254721Semaste			case 'z':
212254721Semaste				zflag++;
213254721Semaste				break;
214254721Semaste			default:
215254721Semaste				saved_av = av;
216254721Semaste				if (isalpha((unsigned char)**av)) {
217254721Semaste					if (strncasecmp(*av, "SIG", 3) == 0)
218254721Semaste						*av += 3;
219254721Semaste					for (sig = NSIG, p = sys_signame + 1;
220254721Semaste					     --sig; ++p)
221254721Semaste						if (strcasecmp(*p, *av) == 0) {
222254721Semaste							sig = p - sys_signame;
223254721Semaste							break;
224254721Semaste						}
225254721Semaste					if (!sig) {
226254721Semaste						if (**saved_av == 'I') {
227254721Semaste							av = saved_av;
228254721Semaste							Iflag = 1;
229254721Semaste							break;
230254721Semaste						} else
231254721Semaste							nosig(*av);
232254721Semaste					}
233254721Semaste				} else if (isdigit((unsigned char)**av)) {
234254721Semaste					sig = strtol(*av, &ep, 10);
235254721Semaste					if (!*av || *ep)
236254721Semaste						errx(1, "illegal signal number: %s", *av);
237254721Semaste					if (sig < 0 || sig >= NSIG)
238254721Semaste						nosig(*av);
239254721Semaste				} else
240254721Semaste					nosig(*av);
241254721Semaste			}
242254721Semaste			++av;
243254721Semaste			--ac;
244254721Semaste		} else {
245254721Semaste			break;
246254721Semaste		}
247254721Semaste	}
248254721Semaste
249254721Semaste	if (user == NULL && tty == NULL && cmd == NULL && !jflag && ac == 0)
250254721Semaste		usage();
251254721Semaste
252254721Semaste	if (tty) {
253254721Semaste		if (strncmp(tty, "/dev/", 5) == 0)
254254721Semaste			snprintf(buf, sizeof(buf), "%s", tty);
255254721Semaste		else if (strncmp(tty, "tty", 3) == 0)
256254721Semaste			snprintf(buf, sizeof(buf), "/dev/%s", tty);
257254721Semaste		else
258254721Semaste			snprintf(buf, sizeof(buf), "/dev/tty%s", tty);
259254721Semaste		if (stat(buf, &sb) < 0)
260254721Semaste			err(1, "stat(%s)", buf);
261254721Semaste		if (!S_ISCHR(sb.st_mode))
262254721Semaste			errx(1, "%s: not a character device", buf);
263254721Semaste		tdev = sb.st_rdev;
264254721Semaste		if (dflag)
265254721Semaste			printf("ttydev:0x%x\n", tdev);
266254721Semaste	}
267254721Semaste	if (user) {
268254721Semaste		uid = strtol(user, &ep, 10);
269254721Semaste		if (*user == '\0' || *ep != '\0') { /* was it a number? */
270254721Semaste			pw = getpwnam(user);
271254721Semaste			if (pw == NULL)
272254721Semaste				errx(1, "user %s does not exist", user);
273254721Semaste			uid = pw->pw_uid;
274254721Semaste			if (dflag)
275254721Semaste				printf("uid:%d\n", uid);
276254721Semaste		}
277254721Semaste	} else {
278254721Semaste		uid = getuid();
279254721Semaste		if (uid != 0) {
280254721Semaste			pw = getpwuid(uid);
281254721Semaste			if (pw)
282254721Semaste				user = pw->pw_name;
283254721Semaste			if (dflag)
284254721Semaste				printf("uid:%d\n", uid);
285254721Semaste		}
286254721Semaste	}
287254721Semaste	size = 0;
288254721Semaste	mib[0] = CTL_KERN;
289254721Semaste	mib[1] = KERN_PROC;
290254721Semaste
291254721Semaste	if (user) {
292254721Semaste		mib[2] = eflag ? KERN_PROC_UID : KERN_PROC_RUID;
293254721Semaste		mib[3] = uid;
294254721Semaste		miblen = 4;
295254721Semaste	} else if (tty) {
296254721Semaste		mib[2] = KERN_PROC_TTY;
297254721Semaste		mib[3] = tdev;
298254721Semaste		miblen = 4;
299254721Semaste	} else {
300254721Semaste		mib[2] = KERN_PROC_PROC;
301254721Semaste		mib[3] = 0;
302254721Semaste		miblen = 3;
303254721Semaste	}
304254721Semaste
305254721Semaste	procs = NULL;
306254721Semaste	st = sysctl(mib, miblen, NULL, &size, NULL, 0);
307254721Semaste	do {
308254721Semaste		size += size / 10;
309254721Semaste		newprocs = realloc(procs, size);
310254721Semaste		if (newprocs == NULL) {
311254721Semaste			free(procs);
312254721Semaste			err(1, "could not reallocate memory");
313254721Semaste		}
314254721Semaste		procs = newprocs;
315254721Semaste		st = sysctl(mib, miblen, procs, &size, NULL, 0);
316254721Semaste	} while (st == -1 && errno == ENOMEM);
317254721Semaste	if (st == -1)
318254721Semaste		err(1, "could not sysctl(KERN_PROC)");
319254721Semaste	if (size % sizeof(struct kinfo_proc) != 0) {
320254721Semaste		fprintf(stderr, "proc size mismatch (%zu total, %zu chunks)\n",
321254721Semaste			size, sizeof(struct kinfo_proc));
322254721Semaste		fprintf(stderr, "userland out of sync with kernel\n");
323254721Semaste		exit(1);
324254721Semaste	}
325254721Semaste	nprocs = size / sizeof(struct kinfo_proc);
326254721Semaste	if (dflag)
327254721Semaste		printf("nprocs %d\n", nprocs);
328254721Semaste	mypid = getpid();
329254721Semaste
330254721Semaste	for (i = 0; i < nprocs; i++) {
331254721Semaste		if (procs[i].ki_stat == SZOMB && !zflag)
332254721Semaste			continue;
333254721Semaste		thispid = procs[i].ki_pid;
334254721Semaste		strlcpy(thiscmd, procs[i].ki_comm, sizeof(thiscmd));
335254721Semaste		thistdev = procs[i].ki_tdev;
336254721Semaste		if (eflag)
337254721Semaste			thisuid = procs[i].ki_uid;	/* effective uid */
338254721Semaste		else
339254721Semaste			thisuid = procs[i].ki_ruid;	/* real uid */
340254721Semaste
341254721Semaste		if (thispid == mypid)
342254721Semaste			continue;
343254721Semaste		matched = 1;
344254721Semaste		if (user) {
345254721Semaste			if (thisuid != uid)
346254721Semaste				matched = 0;
347254721Semaste		}
348254721Semaste		if (tty) {
349254721Semaste			if (thistdev != tdev)
350254721Semaste				matched = 0;
351254721Semaste		}
352254721Semaste		if (cmd) {
353254721Semaste			if (mflag) {
354254721Semaste				if (regcomp(&rgx, cmd,
355254721Semaste				    REG_EXTENDED|REG_NOSUB) != 0) {
356254721Semaste					mflag = 0;
357254721Semaste					warnx("%s: illegal regexp", cmd);
358254721Semaste				}
359254721Semaste			}
360254721Semaste			if (mflag) {
361254721Semaste				pmatch.rm_so = 0;
362				pmatch.rm_eo = strlen(thiscmd);
363				if (regexec(&rgx, thiscmd, 0, &pmatch,
364				    REG_STARTEND) != 0)
365					matched = 0;
366				regfree(&rgx);
367			} else {
368				if (strncmp(thiscmd, cmd, MAXCOMLEN) != 0)
369					matched = 0;
370			}
371		}
372		if (jflag && thispid == getpid())
373			matched = 0;
374		if (matched == 0)
375			continue;
376		if (ac > 0)
377			matched = 0;
378		for (j = 0; j < ac; j++) {
379			if (mflag) {
380				if (regcomp(&rgx, av[j],
381				    REG_EXTENDED|REG_NOSUB) != 0) {
382					mflag = 0;
383					warnx("%s: illegal regexp", av[j]);
384				}
385			}
386			if (mflag) {
387				pmatch.rm_so = 0;
388				pmatch.rm_eo = strlen(thiscmd);
389				if (regexec(&rgx, thiscmd, 0, &pmatch,
390				    REG_STARTEND) == 0)
391					matched = 1;
392				regfree(&rgx);
393			} else {
394				if (strcmp(thiscmd, av[j]) == 0)
395					matched = 1;
396			}
397			if (matched)
398				break;
399		}
400		if (matched != 0 && Iflag) {
401			printf("Send signal %d to %s (pid %d uid %d)? ",
402				sig, thiscmd, thispid, thisuid);
403			fflush(stdout);
404			first = ch = getchar();
405			while (ch != '\n' && ch != EOF)
406				ch = getchar();
407			if (first != 'y' && first != 'Y')
408				matched = 0;
409		}
410		if (matched == 0)
411			continue;
412		if (dflag)
413			printf("sig:%d, cmd:%s, pid:%d, dev:0x%x uid:%d\n", sig,
414			    thiscmd, thispid, thistdev, thisuid);
415
416		if (vflag || sflag)
417			printf("kill -%s %d\n", sys_signame[sig], thispid);
418
419		killed++;
420		if (!dflag && !sflag) {
421			if (kill(thispid, sig) < 0 /* && errno != ESRCH */ ) {
422				warn("warning: kill -%s %d",
423				    sys_signame[sig], thispid);
424				errors = 1;
425			}
426		}
427	}
428	if (killed == 0) {
429		if (!qflag)
430			fprintf(stderr, "No matching processes %swere found\n",
431			    getuid() != 0 ? "belonging to you " : "");
432		errors = 1;
433	}
434	exit(errors);
435}
436