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