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: src/usr.bin/killall/killall.c,v 1.31 2004/07/29 18:36:35 maxim Exp $");
30
31#include <sys/param.h>
32#ifndef __APPLE__
33#include <sys/jail.h>
34#endif /* !__APPLE__ */
35#include <sys/stat.h>
36#include <sys/user.h>
37#include <sys/sysctl.h>
38#include <fcntl.h>
39#include <dirent.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
52#include <getopt.h>
53#define OPTIONS ("c:dej:lmst:u:vz")
54
55#ifdef __APPLE__
56#include <TargetConditionals.h>
57#endif
58
59static void __dead2
60usage(void)
61{
62
63#ifdef __APPLE__
64	fprintf(stderr, "usage: killall [-delmsvz] [-help]\n");
65#else /* !__APPLE__ */
66	fprintf(stderr, "usage: killall [-delmsvz] [-help] [-j jid]\n");
67#endif /* __APPLE__ */
68	fprintf(stderr,
69	    "               [-u user] [-t tty] [-c cmd] [-SIGNAL] [cmd]...\n");
70	fprintf(stderr, "At least one option or argument to specify processes must be given.\n");
71	exit(1);
72}
73
74static char *
75upper(const char *str)
76{
77	static char buf[80];
78	char *s;
79
80	strncpy(buf, str, sizeof(buf));
81	buf[sizeof(buf) - 1] = '\0';
82	for (s = buf; *s; s++)
83		*s = toupper((unsigned char)*s);
84	return buf;
85}
86
87
88static void
89printsig(FILE *fp)
90{
91	const char	*const * p;
92	int		cnt;
93	int		offset = 0;
94
95	for (cnt = NSIG, p = sys_signame + 1; --cnt; ++p) {
96		offset += fprintf(fp, "%s ", upper(*p));
97		if (offset >= 75 && cnt > 1) {
98			offset = 0;
99			fprintf(fp, "\n");
100		}
101	}
102	fprintf(fp, "\n");
103}
104
105static void
106nosig(char *name)
107{
108
109	warnx("unknown signal %s; valid signals:", name);
110	printsig(stderr);
111	exit(1);
112}
113
114/*
115 * kludge_signal_args - remove any signal option (-SIGXXX, -##) from the argv array.
116 */
117void
118kludge_signal_args(int *argc, char **argv, int *sig)
119{
120	int i;
121	int shift = 0;
122	int kludge = 1;
123	char *ptr;
124	const char *const *p;
125	char		*ep;
126
127	/* i = 1, skip program name */
128	for (i = 1; i < *argc; i++) {
129		/* Stop kludging if we encounter -- */
130		if (strcmp(argv[i], "--") == 0)
131			kludge = 0;
132		ptr = argv[i] + 1;
133		/* Only process arguments that start with - and do not look like an existing option. */
134		if (kludge && *argv[i] == '-' && *ptr && strchr(OPTIONS, *ptr) == NULL) {
135			if (isalpha(*ptr)) {
136				if (strcmp(ptr, "help") == 0)
137					usage();
138				if (strncasecmp(ptr, "sig", 3) == 0)
139					ptr += 3;
140				for (*sig = NSIG, p = sys_signame + 1; --*sig; ++p)
141					if (strcasecmp(*p, ptr) == 0) {
142						*sig = p - sys_signame;
143						break;
144					}
145				if (!*sig)
146					nosig(ptr);
147			} else if (isdigit(*ptr)) {
148				*sig = strtol(ptr, &ep, 10);
149				if (*ep)
150					errx(1, "illegal signal number: %s", ptr);
151				if (*sig < 0 || *sig >= NSIG)
152					nosig(ptr);
153			} else
154				nosig(ptr);
155
156			shift++;
157			continue;
158		}
159
160		argv[i - shift] = argv[i];
161	}
162
163	for (i = *argc - shift; i < *argc; i++) {
164		argv[i] = NULL;
165	}
166
167	*argc -= shift;
168}
169
170int
171main(int ac, char **av)
172{
173	struct kinfo_proc *procs = NULL, *newprocs;
174	struct stat	sb;
175	struct passwd	*pw;
176	regex_t		rgx;
177	regmatch_t	pmatch;
178	int		i, j;
179	char		buf[256];
180	char		*user = NULL;
181	char		*tty = NULL;
182	char		*cmd = NULL;
183	int		vflag = 0;
184	int		sflag = 0;
185	int		dflag = 0;
186	int		eflag = 0;
187#ifndef __APPLE__
188	int		jflag = 0;
189#endif /* !__APPLE__*/
190	int		mflag = 0;
191	int		zflag = 0;
192	uid_t		uid = 0;
193	dev_t		tdev = 0;
194	pid_t		mypid;
195#ifdef __APPLE__
196	char		*thiscmd;
197#else /* !__APPLE__ */
198	char		thiscmd[MAXCOMLEN + 1];
199#endif /* __APPLE__ */
200	pid_t		thispid;
201#ifndef __APPLE__
202	uid_t		thisuid;
203#endif /* !__APPLE__ */
204	dev_t		thistdev;
205	int		sig = SIGTERM;
206	char		*ep;
207	int		errors = 0;
208#ifndef __APPLE__
209	int		jid;
210#endif /* !__APPLE__ */
211	int		mib[4];
212	size_t		miblen;
213	int		st, nprocs;
214	size_t		size;
215	int		matched;
216	int		killed = 0;
217	int		ch;
218
219	setlocale(LC_ALL, "");
220
221	kludge_signal_args(&ac, av, &sig);
222
223	while ((ch = getopt(ac, av, OPTIONS)) != -1) {
224		switch (ch) {
225		case 'c':
226			cmd = optarg;
227			break;
228		case 'd':
229			dflag++;
230			break;
231		case 'e':
232			eflag++;
233			break;
234#ifndef __APPLE__
235		case 'j':
236			jflag++;
237			jid = strtol(optarg, &ep, 10);
238			if (*ep)
239				errx(1, "illegal jid: %s", optarg);
240			if (jail_attach(jid) == -1)
241				err(1, "jail_attach(): %d", jid);
242			break;
243#endif /* __APPLE__ */
244		case 'l':
245			printsig(stdout);
246			exit(0);
247		case 'm':
248			mflag++;
249			break;
250		case 's':
251			sflag++;
252			break;
253		case 't':
254			tty = optarg;
255			break;
256		case 'u':
257			user = optarg;
258			break;
259		case 'v':
260			vflag++;
261			break;
262		case 'z':
263			zflag++;
264			break;
265		default:
266			usage();
267		}
268	}
269
270	ac -= optind;
271	av += optind;
272
273#ifdef __APPLE__
274	if (user == NULL && tty == NULL && cmd == NULL && ac == 0)
275#else /* !__APPLE__*/
276	if (user == NULL && tty == NULL && cmd == NULL && !jflag && ac == 0)
277#endif /* __APPLE__ */
278		usage();
279
280	if (tty) {
281		if (strncmp(tty, "/dev/", 5) == 0)
282			snprintf(buf, sizeof(buf), "%s", tty);
283		else if (strncmp(tty, "tty", 3) == 0)
284			snprintf(buf, sizeof(buf), "/dev/%s", tty);
285		else
286			snprintf(buf, sizeof(buf), "/dev/tty%s", tty);
287		if (stat(buf, &sb) < 0)
288			err(1, "stat(%s)", buf);
289		if (!S_ISCHR(sb.st_mode))
290			errx(1, "%s: not a character device", buf);
291		tdev = sb.st_rdev;
292		if (dflag)
293			printf("ttydev:0x%x\n", tdev);
294	}
295	if (user) {
296		uid = strtol(user, &ep, 10);
297		if (*user == '\0' || *ep != '\0') { /* was it a number? */
298			pw = getpwnam(user);
299			if (pw == NULL)
300				errx(1, "user %s does not exist", user);
301			uid = pw->pw_uid;
302			if (dflag)
303				printf("uid:%d\n", uid);
304		}
305	} else {
306		uid = getuid();
307		if (uid != 0) {
308			pw = getpwuid(uid);
309			if (pw)
310				user = pw->pw_name;
311			if (dflag)
312				printf("uid:%d\n", uid);
313		}
314	}
315	size = 0;
316	mib[0] = CTL_KERN;
317	mib[1] = KERN_PROC;
318#ifdef __APPLE__
319	mib[2] = KERN_PROC_ALL;
320#else /* !__APPLE__ */
321	mib[2] = KERN_PROC_PROC;
322#endif /* __APPLE__ */
323	mib[3] = 0;
324	miblen = 3;
325
326	if (user) {
327		mib[2] = eflag ? KERN_PROC_UID : KERN_PROC_RUID;
328		mib[3] = uid;
329		miblen = 4;
330	} else if (tty) {
331		mib[2] = KERN_PROC_TTY;
332		mib[3] = tdev;
333		miblen = 4;
334	}
335
336	st = sysctl(mib, miblen, NULL, &size, NULL, 0);
337	do {
338		size += size / 10;
339		newprocs = realloc(procs, size);
340		if (newprocs == 0) {
341			if (procs)
342				free(procs);
343			errx(1, "could not reallocate memory");
344		}
345		procs = newprocs;
346		st = sysctl(mib, miblen, procs, &size, NULL, 0);
347	} while (st == -1 && errno == ENOMEM);
348	if (st == -1)
349		err(1, "could not sysctl(KERN_PROC)");
350	if (size % sizeof(struct kinfo_proc) != 0) {
351		fprintf(stderr, "proc size mismatch (%zu total, %zu chunks)\n",
352			size, sizeof(struct kinfo_proc));
353		fprintf(stderr, "userland out of sync with kernel, recompile libkvm etc\n");
354		exit(1);
355	}
356	nprocs = size / sizeof(struct kinfo_proc);
357	if (dflag)
358		printf("nprocs %d\n", nprocs);
359	mypid = getpid();
360
361	for (i = 0; i < nprocs; i++) {
362#ifdef __APPLE__
363		if (procs[i].kp_proc.p_stat == SZOMB && !zflag)
364			continue;
365		thispid = procs[i].kp_proc.p_pid;
366
367		int mib[3], argmax;
368		size_t syssize;
369		char *procargs, *cp;
370
371		mib[0] = CTL_KERN;
372		mib[1] = KERN_ARGMAX;
373
374		syssize = sizeof(argmax);
375		if (sysctl(mib, 2, &argmax, &syssize, NULL, 0) == -1)
376			continue;
377
378		procargs = malloc(argmax);
379		if (procargs == NULL)
380			continue;
381
382		mib[0] = CTL_KERN;
383#if defined(__APPLE__) && TARGET_OS_EMBEDDED
384		mib[1] = KERN_PROCARGS2;
385#else
386		mib[1] = KERN_PROCARGS;
387#endif
388		mib[2] = thispid;
389
390		syssize = (size_t)argmax;
391		if (sysctl(mib, 3, procargs, &syssize, NULL, 0) == -1) {
392			free(procargs);
393			continue;
394		}
395
396		for (cp = procargs; cp < &procargs[syssize]; cp++) {
397			if (*cp == '\0') {
398				break;
399			}
400		}
401
402		if (cp == &procargs[syssize]) {
403			free(procargs);
404			continue;
405		}
406
407		for (; cp < &procargs[syssize]; cp++) {
408			if (*cp != '\0') {
409				break;
410			}
411		}
412
413		if (cp == &procargs[syssize]) {
414			free(procargs);
415			continue;
416		}
417
418		/* Strip off any path that was specified */
419		for (thiscmd = cp; (cp < &procargs[syssize]) && (*cp != '\0'); cp++) {
420			if (*cp == '/') {
421				thiscmd = cp + 1;
422			}
423		}
424
425		thistdev = procs[i].kp_eproc.e_tdev;
426#else /* !__APPLE__ */
427		if (procs[i].ki_stat == SZOMB && !zflag)
428			continue;
429		thispid = procs[i].ki_pid;
430		strncpy(thiscmd, procs[i].ki_comm, MAXCOMLEN);
431		thiscmd[MAXCOMLEN] = '\0';
432		thistdev = procs[i].ki_tdev;
433#endif /* __APPLE__ */
434#ifndef __APPLE__
435		if (eflag)
436			thisuid = procs[i].ki_uid;	/* effective uid */
437		else
438			thisuid = procs[i].ki_ruid;	/* real uid */
439#endif /* !__APPLE__ */
440
441		if (thispid == mypid) {
442#ifdef __APPLE__
443			free(procargs);
444#endif /* __APPLE__ */
445			continue;
446		}
447		matched = 1;
448#ifndef __APPLE__
449		if (user) {
450			if (thisuid != uid)
451				matched = 0;
452		}
453#endif /* !__APPLE__ */
454		if (tty) {
455			if (thistdev != tdev)
456				matched = 0;
457		}
458		if (cmd) {
459			if (mflag) {
460				if (regcomp(&rgx, cmd,
461				    REG_EXTENDED|REG_NOSUB) != 0) {
462					mflag = 0;
463					warnx("%s: illegal regexp", cmd);
464				}
465			}
466			if (mflag) {
467				pmatch.rm_so = 0;
468				pmatch.rm_eo = strlen(thiscmd);
469				if (regexec(&rgx, thiscmd, 0, &pmatch,
470				    REG_STARTEND) != 0)
471					matched = 0;
472				regfree(&rgx);
473			} else {
474				if (strncmp(thiscmd, cmd, MAXCOMLEN) != 0)
475					matched = 0;
476			}
477		}
478#ifndef __APPLE__
479		if (jflag && thispid == getpid())
480			matched = 0;
481#endif /* !__APPLE__ */
482		if (matched == 0) {
483#ifdef __APPLE__
484			free(procargs);
485#endif /* !__APPLE__ */
486			continue;
487		}
488		if (ac > 0)
489			matched = 0;
490		for (j = 0; j < ac; j++) {
491			if (mflag) {
492				if (regcomp(&rgx, av[j],
493				    REG_EXTENDED|REG_NOSUB) != 0) {
494					mflag = 0;
495					warnx("%s: illegal regexp", av[j]);
496				}
497			}
498			if (mflag) {
499				pmatch.rm_so = 0;
500				pmatch.rm_eo = strlen(thiscmd);
501				if (regexec(&rgx, thiscmd, 0, &pmatch,
502				    REG_STARTEND) == 0)
503					matched = 1;
504				regfree(&rgx);
505			} else {
506				if (strcmp(thiscmd, av[j]) == 0)
507					matched = 1;
508			}
509			if (matched)
510				break;
511		}
512		if (matched == 0) {
513#ifdef __APPLE__
514			free(procargs);
515#endif /* __APPLE__ */
516			continue;
517		}
518		if (dflag)
519#ifdef __APPLE__
520			printf("sig:%d, cmd:%s, pid:%d, dev:0x%x\n", sig,
521			    thiscmd, thispid, thistdev);
522#else /* !__APPLE__ */
523			printf("sig:%d, cmd:%s, pid:%d, dev:0x%x uid:%d\n", sig,
524			    thiscmd, thispid, thistdev, thisuid);
525#endif /* __APPLE__ */
526
527		if (vflag || sflag)
528			printf("kill -%s %d\n", upper(sys_signame[sig]),
529			    thispid);
530
531		killed++;
532		if (!dflag && !sflag) {
533			if (kill(thispid, sig) < 0 /* && errno != ESRCH */ ) {
534				warn("warning: kill -%s %d",
535				    upper(sys_signame[sig]), thispid);
536				errors = 1;
537			}
538		}
539#ifdef __APPLE__
540		free(procargs);
541#endif /* __APPLE__ */
542	}
543	if (killed == 0) {
544		fprintf(stderr, "No matching processes %swere found\n",
545		    getuid() != 0 ? "belonging to you " : "");
546		errors = 1;
547	}
548	exit(errors);
549}
550