GCC Code Coverage Report
Directory: ./ Exec Total Coverage
File: usr.bin/pkill/pkill.c Lines: 0 295 0.0 %
Date: 2016-12-06 Branches: 0 297 0.0 %

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