pkill.c revision 1.30 1 /* $NetBSD: pkill.c,v 1.30 2015/01/09 12:45:32 prlw1 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.30 2015/01/09 12:45:32 prlw1 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 action = reniceact;
132 prenice = 1;
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 p = argv[1];
175
176 i = (int)strtol(p, &q, 10);
177 if (*q == '\0') {
178 nicenum = i;
179 argv++;
180 argc--;
181 } else
182 usage();
183 } else {
184 while ((ch = getopt(argc, argv, "G:P:U:d:fg:ilns:t:u:vx")) != -1)
185 switch (ch) {
186 case 'G':
187 makelist(&rgidlist, LT_GROUP, optarg);
188 criteria = 1;
189 break;
190 case 'P':
191 makelist(&ppidlist, LT_GENERIC, optarg);
192 criteria = 1;
193 break;
194 case 'U':
195 makelist(&ruidlist, LT_USER, optarg);
196 criteria = 1;
197 break;
198 case 'd':
199 if (!pgrep)
200 usage();
201 delim = optarg;
202 break;
203 case 'f':
204 matchargs = 1;
205 break;
206 case 'g':
207 makelist(&pgrplist, LT_PGRP, optarg);
208 criteria = 1;
209 break;
210 case 'i':
211 cflags |= REG_ICASE;
212 break;
213 case 'l':
214 longfmt = 1;
215 break;
216 case 'n':
217 newest = 1;
218 criteria = 1;
219 break;
220 case 's':
221 makelist(&sidlist, LT_SID, optarg);
222 criteria = 1;
223 break;
224 case 't':
225 makelist(&tdevlist, LT_TTY, optarg);
226 criteria = 1;
227 break;
228 case 'u':
229 makelist(&euidlist, LT_USER, optarg);
230 criteria = 1;
231 break;
232 case 'v':
233 inverse = 1;
234 break;
235 case 'x':
236 fullmatch = 1;
237 break;
238 default:
239 usage();
240 /* NOTREACHED */
241 }
242 argc -= optind;
243 argv += optind;
244 }
245
246 if (argc != 0)
247 criteria = 1;
248 if (!criteria)
249 usage();
250
251 mypid = getpid();
252
253 /*
254 * Retrieve the list of running processes from the kernel.
255 */
256 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, buf);
257 if (kd == NULL)
258 errx(STATUS_ERROR, "Cannot open kernel files (%s)", buf);
259
260 plist = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(*plist), &nproc);
261 if (plist == NULL)
262 errx(STATUS_ERROR, "Cannot get process list (%s)",
263 kvm_geterr(kd));
264
265 /*
266 * Allocate memory which will be used to keep track of the
267 * selection.
268 */
269 if ((selected = calloc((size_t)1, (size_t)nproc)) == NULL)
270 err(STATUS_ERROR, "Cannot allocate memory for %d processes",
271 nproc);
272
273 /*
274 * Refine the selection.
275 */
276 for (; *argv != NULL; argv++) {
277 if ((rv = regcomp(®, *argv, cflags)) != 0) {
278 (void)regerror(rv, ®, buf, sizeof(buf));
279 errx(STATUS_BADUSAGE,
280 "Cannot compile regular expression `%s' (%s)",
281 *argv, buf);
282 }
283
284 for (i = 0, kp = plist; i < nproc; i++, kp++) {
285 if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid)
286 continue;
287
288 if ((pargv = kvm_getargv2(kd, kp, 0)) == NULL)
289 continue;
290 if (matchargs) {
291
292 j = 0;
293 while (j < (int)sizeof(buf) && *pargv != NULL) {
294 j += snprintf(buf + j, sizeof(buf) - j,
295 pargv[1] != NULL ? "%s " : "%s",
296 pargv[0]);
297 pargv++;
298 }
299 } else
300 strlcpy(buf, pargv[0], sizeof(buf));
301
302 rv = regexec(®, buf, 1, ®match, 0);
303 if (rv == 0) {
304 if (fullmatch) {
305 if (regmatch.rm_so == 0 &&
306 regmatch.rm_eo ==
307 (regoff_t)strlen(buf))
308 selected[i] = 1;
309 } else
310 selected[i] = 1;
311 } else if (rv != REG_NOMATCH) {
312 (void)regerror(rv, ®, buf, sizeof(buf));
313 errx(STATUS_ERROR,
314 "Regular expression evaluation error (%s)",
315 buf);
316 }
317 }
318
319 regfree(®);
320 }
321
322 for (i = 0, kp = plist; i < nproc; i++, kp++) {
323 if ((kp->p_flag & P_SYSTEM) != 0)
324 continue;
325
326 SLIST_FOREACH(li, &ruidlist, li_chain)
327 if (kp->p_ruid == (uid_t)li->li_number)
328 break;
329 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) {
330 selected[i] = 0;
331 continue;
332 }
333
334 SLIST_FOREACH(li, &rgidlist, li_chain)
335 if (kp->p_rgid == (gid_t)li->li_number)
336 break;
337 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) {
338 selected[i] = 0;
339 continue;
340 }
341
342 SLIST_FOREACH(li, &euidlist, li_chain)
343 if (kp->p_uid == (uid_t)li->li_number)
344 break;
345 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) {
346 selected[i] = 0;
347 continue;
348 }
349
350 SLIST_FOREACH(li, &ppidlist, li_chain)
351 if ((uid_t)kp->p_ppid == (uid_t)li->li_number)
352 break;
353 if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) {
354 selected[i] = 0;
355 continue;
356 }
357
358 SLIST_FOREACH(li, &pgrplist, li_chain)
359 if (kp->p__pgid == (pid_t)li->li_number)
360 break;
361 if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) {
362 selected[i] = 0;
363 continue;
364 }
365
366 SLIST_FOREACH(li, &tdevlist, li_chain) {
367 if (li->li_number == -1 &&
368 (kp->p_flag & P_CONTROLT) == 0)
369 break;
370 if (kp->p_tdev == (uid_t)li->li_number)
371 break;
372 }
373 if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) {
374 selected[i] = 0;
375 continue;
376 }
377
378 SLIST_FOREACH(li, &sidlist, li_chain)
379 if (kp->p_sid == (pid_t)li->li_number)
380 break;
381 if (SLIST_FIRST(&sidlist) != NULL && li == NULL) {
382 selected[i] = 0;
383 continue;
384 }
385
386 if (argc == 0)
387 selected[i] = 1;
388 }
389
390 if (newest) {
391 bestsec = 0;
392 bestusec = 0;
393 bestidx = -1;
394
395 for (i = 0, kp = plist; i < nproc; i++, kp++) {
396 if (!selected[i])
397 continue;
398
399 if (kp->p_ustart_sec > bestsec ||
400 (kp->p_ustart_sec == bestsec
401 && kp->p_ustart_usec > bestusec)) {
402 bestsec = kp->p_ustart_sec;
403 bestusec = kp->p_ustart_usec;
404 bestidx = i;
405 }
406 }
407
408 (void)memset(selected, 0, (size_t)nproc);
409 if (bestidx != -1)
410 selected[bestidx] = 1;
411 }
412
413 /*
414 * Take the appropriate action for each matched process, if any.
415 */
416 for (i = 0, rv = 0, kp = plist; i < nproc; i++, kp++) {
417 if (kp->p_pid == mypid)
418 continue;
419 if (selected[i]) {
420 if (inverse)
421 continue;
422 } else if (!inverse)
423 continue;
424
425 if ((kp->p_flag & P_SYSTEM) != 0)
426 continue;
427
428 rv |= (*action)(kp);
429 }
430
431 return rv ? STATUS_MATCH : STATUS_NOMATCH;
432 }
433
434 static void
435 usage(void)
436 {
437 const char *ustr;
438
439 if (prenice)
440 fprintf(stderr, "Usage: %s [-l] priority pattern ...\n",
441 getprogname());
442 else {
443 if (pgrep)
444 ustr = "[-filnvx] [-d delim]";
445 else
446 ustr = "[-signal] [-filnvx]";
447
448 (void)fprintf(stderr,
449 "Usage: %s %s [-G gid] [-g pgrp] [-P ppid] [-s sid] "
450 "[-t tty]\n"
451 " [-U uid] [-u euid] pattern ...\n",
452 getprogname(), ustr);
453 }
454
455 exit(STATUS_BADUSAGE);
456 }
457
458 static int
459 killact(const struct kinfo_proc2 *kp)
460 {
461 if (longfmt)
462 grepact(kp);
463 if (kill(kp->p_pid, signum) == -1) {
464
465 /*
466 * Check for ESRCH, which indicates that the process
467 * disappeared between us matching it and us
468 * signalling it; don't issue a warning about it.
469 */
470 if (errno != ESRCH)
471 warn("signalling pid %d", (int)kp->p_pid);
472
473 /*
474 * Return 0 to indicate that the process should not be
475 * considered a match, since we didn't actually get to
476 * signal it.
477 */
478 return 0;
479 }
480
481 return 1;
482 }
483
484 static int
485 reniceact(const struct kinfo_proc2 *kp)
486 {
487 int oldprio;
488
489 if (longfmt)
490 grepact(kp);
491
492 errno = 0;
493 if ((oldprio = getpriority(PRIO_PROCESS, kp->p_pid)) == -1 &&
494 errno != 0) {
495 warn("%d: getpriority", kp->p_pid);
496 return 0;
497 }
498
499 if (setpriority(PRIO_PROCESS, kp->p_pid, nicenum) == -1) {
500 warn("%d: setpriority", kp->p_pid);
501 return 0;
502 }
503
504 (void)printf("%d: old priority %d, new priority %d\n",
505 kp->p_pid, oldprio, nicenum);
506
507 return 1;
508 }
509
510 static int
511 grepact(const struct kinfo_proc2 *kp)
512 {
513 char **argv;
514
515 if (longfmt && matchargs) {
516
517 /*
518 * If kvm_getargv2() failed the process has probably
519 * disappeared. Return 0 to indicate that the process
520 * should not be considered a match, since we are no
521 * longer in a position to output it as a match.
522 */
523 if ((argv = kvm_getargv2(kd, kp, 0)) == NULL)
524 return 0;
525
526 (void)printf("%d ", (int)kp->p_pid);
527 for (; *argv != NULL; argv++) {
528 (void)printf("%s", *argv);
529 if (argv[1] != NULL)
530 (void)putchar(' ');
531 }
532 } else if (longfmt)
533 (void)printf("%d %s", (int)kp->p_pid, kp->p_comm);
534 else
535 (void)printf("%d", (int)kp->p_pid);
536
537 (void)printf("%s", delim);
538
539 return 1;
540 }
541
542 static void
543 makelist(struct listhead *head, enum listtype type, char *src)
544 {
545 struct list *li;
546 struct passwd *pw;
547 struct group *gr;
548 struct stat st;
549 char *sp, *ep, buf[MAXPATHLEN];
550 const char *p;
551 int empty;
552 const char *prefix = _PATH_DEV;
553
554 empty = 1;
555
556 while ((sp = strsep(&src, ",")) != NULL) {
557 if (*sp == '\0')
558 usage();
559
560 if ((li = malloc(sizeof(*li))) == NULL)
561 err(STATUS_ERROR, "Cannot allocate %zu bytes",
562 sizeof(*li));
563 SLIST_INSERT_HEAD(head, li, li_chain);
564 empty = 0;
565
566 li->li_number = (uid_t)strtol(sp, &ep, 0);
567 if (*ep == '\0' && type != LT_TTY) {
568 switch (type) {
569 case LT_PGRP:
570 if (li->li_number == 0)
571 li->li_number = getpgrp();
572 break;
573 case LT_SID:
574 if (li->li_number == 0)
575 li->li_number = getsid(mypid);
576 break;
577 default:
578 break;
579 }
580 continue;
581 }
582
583 switch (type) {
584 case LT_USER:
585 if ((pw = getpwnam(sp)) == NULL)
586 errx(STATUS_BADUSAGE, "Unknown user `%s'",
587 sp);
588 li->li_number = pw->pw_uid;
589 break;
590 case LT_GROUP:
591 if ((gr = getgrnam(sp)) == NULL)
592 errx(STATUS_BADUSAGE, "Unknown group `%s'",
593 sp);
594 li->li_number = gr->gr_gid;
595 break;
596 case LT_TTY:
597 p = sp;
598 if (*sp == '/')
599 prefix = "";
600 else if (strcmp(sp, "-") == 0) {
601 li->li_number = -1;
602 break;
603 } else if (strcmp(sp, "co") == 0)
604 p = "console";
605 else if (strncmp(sp, "tty", 3) == 0)
606 /* all set */;
607 else if (strncmp(sp, "pts/", 4) == 0)
608 /* all set */;
609 else if (*ep != '\0' || (strlen(sp) == 2 && *sp == '0'))
610 prefix = _PATH_TTY;
611 else
612 prefix = _PATH_DEV_PTS;
613
614 (void)snprintf(buf, sizeof(buf), "%s%s", prefix, p);
615
616 if (stat(buf, &st) == -1) {
617 if (errno == ENOENT)
618 errx(STATUS_BADUSAGE,
619 "No such tty: `%s'", buf);
620 err(STATUS_ERROR, "Cannot access `%s'", buf);
621 }
622
623 if ((st.st_mode & S_IFCHR) == 0)
624 errx(STATUS_BADUSAGE, "Not a tty: `%s'", buf);
625
626 li->li_number = st.st_rdev;
627 break;
628 default:
629 usage();
630 }
631 }
632
633 if (empty)
634 usage();
635 }
636