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