pkill.c revision 127434
1/*	$NetBSD: pkill.c,v 1.7 2004/02/15 17:03:30 soren Exp $	*/
2
3/*-
4 * Copyright (c) 2002 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Andrew Doran.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 *    notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 *    notice, this list of conditions and the following disclaimer in the
17 *    documentation and/or other materials provided with the distribution.
18 * 3. All advertising materials mentioning features or use of this software
19 *    must display the following acknowledgement:
20 *	This product includes software developed by the NetBSD
21 *	Foundation, Inc. and its contributors.
22 * 4. Neither the name of The NetBSD Foundation nor the names of its
23 *    contributors may be used to endorse or promote products derived
24 *    from this software without specific prior written permission.
25 *
26 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 * POSSIBILITY OF SUCH DAMAGE.
37 */
38
39#include <sys/cdefs.h>
40__FBSDID("$FreeBSD: head/usr.bin/pkill/pkill.c 127434 2004-03-26 02:20:52Z gad $");
41
42#include <sys/types.h>
43#include <sys/param.h>
44#include <sys/sysctl.h>
45#include <sys/proc.h>
46#include <sys/queue.h>
47#include <sys/stat.h>
48#include <sys/user.h>
49
50#include <stdio.h>
51#include <stdlib.h>
52#include <limits.h>
53#include <paths.h>
54#include <string.h>
55#include <unistd.h>
56#include <signal.h>
57#include <regex.h>
58#include <ctype.h>
59#include <fcntl.h>
60#include <kvm.h>
61#include <err.h>
62#include <pwd.h>
63#include <grp.h>
64#include <errno.h>
65
66#define	STATUS_MATCH	0
67#define	STATUS_NOMATCH	1
68#define	STATUS_BADUSAGE	2
69#define	STATUS_ERROR	3
70
71#if defined(__FreeBSD__)
72# if __FreeBSD_version < 300000
73#  define	NEED_KMEM
74# endif
75#endif
76
77enum listtype {
78	LT_GENERIC,
79	LT_USER,
80	LT_GROUP,
81	LT_TTY,
82	LT_PGRP,
83	LT_SID
84};
85
86struct list {
87	SLIST_ENTRY(list) li_chain;
88	long	li_number;
89};
90
91SLIST_HEAD(listhead, list);
92
93struct kinfo_proc	*plist;
94char	*selected;
95const char	*delim = "\n";
96int	nproc;
97int	pgrep;
98int	signum = SIGTERM;
99int	newest;
100int	inverse;
101int	longfmt;
102int	matchargs;
103int	fullmatch;
104kvm_t	*kd;
105pid_t	mypid;
106
107struct listhead euidlist = SLIST_HEAD_INITIALIZER(list);
108struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list);
109struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list);
110struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list);
111struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list);
112struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list);
113struct listhead sidlist = SLIST_HEAD_INITIALIZER(list);
114
115int	main(int, char **);
116void	usage(void);
117void	killact(struct kinfo_proc *);
118void	grepact(struct kinfo_proc *);
119void	makelist(struct listhead *, enum listtype, char *);
120
121int
122main(int argc, char **argv)
123{
124	extern char *optarg;
125	extern int optind;
126	char buf[_POSIX2_LINE_MAX], *mstr, **pargv, *p, *q;
127	const char *execf, *coref, *swapf;
128	int debug_opt;
129	int i, ch, bestidx, rv, criteria, drop_privs;
130	size_t jsz;
131	void (*action)(struct kinfo_proc *);
132	struct kinfo_proc *kp;
133	struct list *li;
134	struct timeval best_tval;
135	regex_t reg;
136	regmatch_t regmatch;
137
138	if (strcmp(getprogname(), "pgrep") == 0) {
139		action = grepact;
140		pgrep = 1;
141	} else {
142		action = killact;
143		p = argv[1];
144
145		if (argc > 1 && p[0] == '-') {
146			p++;
147			i = (int)strtol(p, &q, 10);
148			if (*q == '\0') {
149				signum = i;
150				argv++;
151				argc--;
152			} else {
153				if (strncasecmp(p, "sig", 3) == 0)
154					p += 3;
155				for (i = 1; i < NSIG; i++)
156					if (strcasecmp(sys_signame[i], p) == 0)
157						break;
158				if (i != NSIG) {
159					signum = i;
160					argv++;
161					argc--;
162				}
163			}
164		}
165	}
166
167#if defined(NEED_KMEM)
168	execf = coref = swapf = NULL;
169#else
170	execf = coref = swapf = _PATH_DEVNULL;
171#endif
172
173	criteria = 0;
174	debug_opt = 0;
175	drop_privs = 0;
176
177	while ((ch = getopt(argc, argv, "DG:M:N:P:U:d:fg:lns:t:u:vx")) != -1)
178		switch (ch) {
179		case 'D':
180			debug_opt++;
181			break;
182		case 'G':
183			makelist(&rgidlist, LT_GROUP, optarg);
184			criteria = 1;
185			break;
186		case 'M':
187			coref = optarg;
188			drop_privs = 1;
189			break;
190		case 'N':
191			execf = optarg;
192			drop_privs = 1;
193			break;
194		case 'P':
195			makelist(&ppidlist, LT_GENERIC, optarg);
196			criteria = 1;
197			break;
198		case 'U':
199			makelist(&ruidlist, LT_USER, optarg);
200			criteria = 1;
201			break;
202		case 'd':
203			if (!pgrep)
204				usage();
205			delim = optarg;
206			break;
207		case 'f':
208			matchargs = 1;
209			break;
210		case 'g':
211			makelist(&pgrplist, LT_PGRP, optarg);
212			criteria = 1;
213			break;
214		case 'l':
215			if (!pgrep)
216				usage();
217			longfmt = 1;
218			break;
219		case 'n':
220			newest = 1;
221			criteria = 1;
222			break;
223		case 's':
224			makelist(&sidlist, LT_SID, optarg);
225			criteria = 1;
226			break;
227		case 't':
228			makelist(&tdevlist, LT_TTY, optarg);
229			criteria = 1;
230			break;
231		case 'u':
232			makelist(&euidlist, LT_USER, optarg);
233			criteria = 1;
234			break;
235		case 'v':
236			inverse = 1;
237			break;
238		case 'x':
239			fullmatch = 1;
240			break;
241		default:
242			usage();
243			/* NOTREACHED */
244		}
245
246	argc -= optind;
247	argv += optind;
248	if (argc != 0)
249		criteria = 1;
250	if (!criteria)
251		usage();
252
253	/*
254	 * Discard privileges if not the running kernel so that bad
255	 * guys can't print interesting stuff from kernel memory.
256	 */
257	if (drop_privs) {
258		setgid(getgid());
259		setuid(getuid());
260	}
261
262	mypid = getpid();
263
264	/*
265	 * Retrieve the list of running processes from the kernel.
266	 */
267	kd = kvm_openfiles(execf, coref, swapf, O_RDONLY, buf);
268	if (kd == NULL)
269		errx(STATUS_ERROR, "kvm_openfiles(): %s", buf);
270
271	plist = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc);
272	if (plist == NULL)
273		errx(STATUS_ERROR, "kvm_getprocs() failed");
274
275	/*
276	 * Allocate memory which will be used to keep track of the
277	 * selection.
278	 */
279	if ((selected = malloc(nproc)) == NULL)
280		errx(STATUS_ERROR, "memory allocation failure");
281	memset(selected, 0, nproc);
282
283	/*
284	 * Refine the selection.
285	 */
286	for (; *argv != NULL; argv++) {
287		if ((rv = regcomp(&reg, *argv, REG_EXTENDED)) != 0) {
288			regerror(rv, &reg, buf, sizeof(buf));
289			errx(STATUS_BADUSAGE, "bad expression: %s", buf);
290		}
291
292		for (i = 0, kp = plist; i < nproc; i++, kp++) {
293			if ((kp->ki_flag & P_SYSTEM) != 0) {
294				if (debug_opt > 0)
295				    fprintf(stderr, "* Skipped %5d %3d %s\n",
296					kp->ki_pid, kp->ki_uid, kp->ki_comm);
297				continue;
298			}
299
300			if (matchargs) {
301				if ((pargv = kvm_getargv(kd, kp, 0)) == NULL)
302					continue;
303
304				jsz = 0;
305				while (jsz < sizeof(buf) && *pargv != NULL) {
306					jsz += snprintf(buf + jsz,
307					    sizeof(buf) - jsz,
308					    pargv[1] != NULL ? "%s " : "%s",
309					    pargv[0]);
310					pargv++;
311				}
312
313				mstr = buf;
314			} else
315				mstr = kp->ki_comm;
316
317			rv = regexec(&reg, mstr, 1, &regmatch, 0);
318			if (rv == 0) {
319				if (fullmatch) {
320					if (regmatch.rm_so == 0 &&
321					    regmatch.rm_eo ==
322					    (off_t)strlen(mstr))
323						selected[i] = 1;
324				} else
325					selected[i] = 1;
326			} else if (rv != REG_NOMATCH) {
327				regerror(rv, &reg, buf, sizeof(buf));
328				errx(STATUS_ERROR, "regexec(): %s", buf);
329			}
330			if (debug_opt > 1) {
331				const char *rv_res = "NoMatch";
332				if (selected[i])
333					rv_res = "Matched";
334				fprintf(stderr, "* %s %5d %3d %s\n", rv_res,
335				    kp->ki_pid, kp->ki_uid, mstr);
336			}
337		}
338
339		regfree(&reg);
340	}
341
342	for (i = 0, kp = plist; i < nproc; i++, kp++) {
343		if ((kp->ki_flag & P_SYSTEM) != 0)
344			continue;
345
346		SLIST_FOREACH(li, &ruidlist, li_chain)
347			if (kp->ki_ruid == (uid_t)li->li_number)
348				break;
349		if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) {
350			selected[i] = 0;
351			continue;
352		}
353
354		SLIST_FOREACH(li, &rgidlist, li_chain)
355			if (kp->ki_rgid == (gid_t)li->li_number)
356				break;
357		if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) {
358			selected[i] = 0;
359			continue;
360		}
361
362		SLIST_FOREACH(li, &euidlist, li_chain)
363			if (kp->ki_uid == (uid_t)li->li_number)
364				break;
365		if (SLIST_FIRST(&euidlist) != NULL && li == NULL) {
366			selected[i] = 0;
367			continue;
368		}
369
370		SLIST_FOREACH(li, &ppidlist, li_chain)
371			if (kp->ki_ppid == (pid_t)li->li_number)
372				break;
373		if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) {
374			selected[i] = 0;
375			continue;
376		}
377
378		SLIST_FOREACH(li, &pgrplist, li_chain)
379			if (kp->ki_pgid == (pid_t)li->li_number)
380				break;
381		if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) {
382			selected[i] = 0;
383			continue;
384		}
385
386		SLIST_FOREACH(li, &tdevlist, li_chain) {
387			if (li->li_number == -1 &&
388			    (kp->ki_flag & P_CONTROLT) == 0)
389				break;
390			if (kp->ki_tdev == (udev_t)li->li_number)
391				break;
392		}
393		if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) {
394			selected[i] = 0;
395			continue;
396		}
397
398		SLIST_FOREACH(li, &sidlist, li_chain)
399			if (kp->ki_sid == (pid_t)li->li_number)
400				break;
401		if (SLIST_FIRST(&sidlist) != NULL && li == NULL) {
402			selected[i] = 0;
403			continue;
404		}
405
406		if (argc == 0)
407			selected[i] = 1;
408	}
409
410	if (newest) {
411		best_tval.tv_sec = 0;
412		best_tval.tv_usec = 0;
413		bestidx = -1;
414
415		for (i = 0, kp = plist; i < nproc; i++, kp++) {
416			if (!selected[i])
417				continue;
418
419			if (kp->ki_start.tv_sec > best_tval.tv_sec ||
420			    (kp->ki_start.tv_sec == best_tval.tv_sec
421			    && kp->ki_start.tv_usec > best_tval.tv_usec)) {
422				best_tval.tv_sec = kp->ki_start.tv_sec;
423				best_tval.tv_usec = kp->ki_start.tv_usec;
424				bestidx = i;
425			}
426		}
427
428		memset(selected, 0, nproc);
429		if (bestidx != -1)
430			selected[bestidx] = 1;
431	}
432
433	/*
434	 * Take the appropriate action for each matched process, if any.
435	 */
436	for (i = 0, rv = 0, kp = plist; i < nproc; i++, kp++) {
437		if (kp->ki_pid == mypid)
438			continue;
439		if (selected[i]) {
440			if (inverse)
441				continue;
442		} else if (!inverse)
443			continue;
444
445		if ((kp->ki_flag & P_SYSTEM) != 0)
446			continue;
447
448		rv = 1;
449		(*action)(kp);
450	}
451
452	exit(rv ? STATUS_MATCH : STATUS_NOMATCH);
453}
454
455void
456usage(void)
457{
458	const char *ustr;
459
460	if (pgrep)
461		ustr = "[-flnvx] [-d delim]";
462	else
463		ustr = "[-signal] [-fnvx]";
464
465	fprintf(stderr,
466		"usage: %s %s [-G gid] [-M core] [-N system]\n"
467		"             [-P ppid] [-U uid] [-g pgrp] [-s sid] [-t tty]\n"
468		"             [-u euid] pattern ...\n", getprogname(), ustr);
469
470	exit(STATUS_ERROR);
471}
472
473void
474killact(struct kinfo_proc *kp)
475{
476
477	if (kill(kp->ki_pid, signum) == -1)
478		err(STATUS_ERROR, "signalling pid %d", (int)kp->ki_pid);
479}
480
481void
482grepact(struct kinfo_proc *kp)
483{
484	char **argv;
485
486	if (longfmt && matchargs) {
487		if ((argv = kvm_getargv(kd, kp, 0)) == NULL)
488			return;
489
490		printf("%d ", (int)kp->ki_pid);
491		for (; *argv != NULL; argv++) {
492			printf("%s", *argv);
493			if (argv[1] != NULL)
494				putchar(' ');
495		}
496	} else if (longfmt)
497		printf("%d %s", (int)kp->ki_pid, kp->ki_comm);
498	else
499		printf("%d", (int)kp->ki_pid);
500
501	printf("%s", delim);
502}
503
504void
505makelist(struct listhead *head, enum listtype type, char *src)
506{
507	struct list *li;
508	struct passwd *pw;
509	struct group *gr;
510	struct stat st;
511	const char *cp;
512	char *sp, *p, buf[MAXPATHLEN];
513	int empty;
514
515	empty = 1;
516
517	while ((sp = strsep(&src, ",")) != NULL) {
518		if (*sp == '\0')
519			usage();
520
521		if ((li = malloc(sizeof(*li))) == NULL)
522			errx(STATUS_ERROR, "memory allocation failure");
523		SLIST_INSERT_HEAD(head, li, li_chain);
524		empty = 0;
525
526		li->li_number = (uid_t)strtol(sp, &p, 0);
527		if (*p == '\0') {
528			switch (type) {
529			case LT_PGRP:
530				if (li->li_number == 0)
531					li->li_number = getpgrp();
532				break;
533			case LT_SID:
534				if (li->li_number == 0)
535					li->li_number = getsid(mypid);
536				break;
537			case LT_TTY:
538				usage();
539			default:
540				break;
541			}
542			continue;
543		}
544
545		switch (type) {
546		case LT_USER:
547			if ((pw = getpwnam(sp)) == NULL)
548				errx(STATUS_BADUSAGE, "unknown user `%s'",
549				    optarg);
550			li->li_number = pw->pw_uid;
551			break;
552		case LT_GROUP:
553			if ((gr = getgrnam(sp)) == NULL)
554				errx(STATUS_BADUSAGE, "unknown group `%s'",
555				    optarg);
556			li->li_number = gr->gr_gid;
557			break;
558		case LT_TTY:
559			if (strcmp(sp, "-") == 0) {
560				li->li_number = -1;
561				break;
562			} else if (strcmp(sp, "co") == 0)
563				cp = "console";
564			else if (strncmp(sp, "tty", 3) == 0)
565				cp = sp;
566			else
567				cp = NULL;
568
569			if (cp == NULL)
570				snprintf(buf, sizeof(buf), "/dev/tty%s", sp);
571			else
572				snprintf(buf, sizeof(buf), "/dev/%s", cp);
573
574			if (stat(buf, &st) < 0) {
575				if (errno == ENOENT)
576					errx(STATUS_BADUSAGE,
577					    "no such tty: `%s'", sp);
578				err(STATUS_ERROR, "stat(%s)", sp);
579			}
580
581			if ((st.st_mode & S_IFCHR) == 0)
582				errx(STATUS_BADUSAGE, "not a tty: `%s'", sp);
583
584			li->li_number = st.st_rdev;
585			break;
586		default:
587			usage();
588		};
589	}
590
591	if (empty)
592		usage();
593}
594