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