1 /* $OpenBSD: pkill.c,v 1.14 2005/07/16 11:48:46 jmc 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 * 3. All advertising materials mentioning features or use of this software
20 * must display the following acknowledgement:
21 * This product includes software developed by the NetBSD
22 * Foundation, Inc. and its contributors.
23 * 4. Neither the name of The NetBSD Foundation nor the names of its
24 * contributors may be used to endorse or promote products derived
25 * from this software without specific prior written permission.
26 *
27 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
28 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
29 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
30 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
31 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
32 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
33 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
34 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
35 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
36 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
37 * POSSIBILITY OF SUCH DAMAGE.
38 */
39
40 #ifndef lint
41 static const char rcsid[] = "$OpenBSD: pkill.c,v 1.14 2005/07/16 11:48:46 jmc 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
65 #define STATUS_MATCH 0
66 #define STATUS_NOMATCH 1
67 #define STATUS_BADUSAGE 2
68 #define STATUS_ERROR 3
69
70 enum listtype {
71 LT_GENERIC,
72 LT_USER,
73 LT_GROUP,
74 LT_TTY,
75 LT_PGRP,
76 LT_SID
77 };
78
79 struct list {
80 SLIST_ENTRY(list) li_chain;
81 long li_number;
82 };
83
84 SLIST_HEAD(listhead, list);
85
86 struct kinfo_proc2 *plist;
87 char *selected;
88 char *delim = "\n";
89 int nproc;
90 int pgrep;
91 int signum = SIGTERM;
92 int newest;
93 int inverse;
94 int longfmt;
95 int matchargs;
96 int fullmatch;
97 kvm_t *kd;
98 pid_t mypid;
99
100 struct listhead euidlist = SLIST_HEAD_INITIALIZER(list);
101 struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list);
102 struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list);
103 struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list);
104 struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list);
105 struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list);
106 struct listhead sidlist = SLIST_HEAD_INITIALIZER(list);
107
108 int main(int, char **);
109 void usage(void);
110 int killact(struct kinfo_proc2 *, int);
111 int grepact(struct kinfo_proc2 *, int);
112 void makelist(struct listhead *, enum listtype, char *);
113
114 extern char *__progname;
115
116 int
main(int argc,char ** argv)117 main(int argc, char **argv)
118 {
119 extern char *optarg;
120 extern int optind;
121 char buf[_POSIX2_LINE_MAX], *mstr, **pargv, *p, *q;
122 int i, j, ch, bestidx, rv, criteria;
123 int (*action)(struct kinfo_proc2 *, int);
124 struct kinfo_proc2 *kp;
125 struct list *li;
126 u_int32_t bestsec, bestusec;
127 regex_t reg;
128 regmatch_t regmatch;
129
130 if (strcmp(__progname, "pgrep") == 0) {
131 action = grepact;
132 pgrep = 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 while ((ch = getopt(argc, argv, "G:P:U:d:fg:lns:t:u:vx")) != -1)
162 switch (ch) {
163 case 'G':
164 makelist(&rgidlist, LT_GROUP, optarg);
165 criteria = 1;
166 break;
167 case 'P':
168 makelist(&ppidlist, LT_GENERIC, optarg);
169 criteria = 1;
170 break;
171 case 'U':
172 makelist(&ruidlist, LT_USER, optarg);
173 criteria = 1;
174 break;
175 case 'd':
176 if (!pgrep)
177 usage();
178 delim = optarg;
179 break;
180 case 'f':
181 matchargs = 1;
182 break;
183 case 'g':
184 makelist(&pgrplist, LT_PGRP, optarg);
185 criteria = 1;
186 break;
187 case 'l':
188 if (!pgrep)
189 usage();
190 longfmt = 1;
191 break;
192 case 'n':
193 newest = 1;
194 criteria = 1;
195 break;
196 case 's':
197 makelist(&sidlist, LT_SID, optarg);
198 criteria = 1;
199 break;
200 case 't':
201 makelist(&tdevlist, LT_TTY, optarg);
202 criteria = 1;
203 break;
204 case 'u':
205 makelist(&euidlist, LT_USER, optarg);
206 criteria = 1;
207 break;
208 case 'v':
209 inverse = 1;
210 break;
211 case 'x':
212 fullmatch = 1;
213 break;
214 default:
215 usage();
216 /* NOTREACHED */
217 }
218
219 argc -= optind;
220 argv += optind;
221 if (argc != 0)
222 criteria = 1;
223 if (!criteria)
224 usage();
225
226 mypid = getpid();
227
228 /*
229 * Retrieve the list of running processes from the kernel.
230 */
231 kd = kvm_openfiles(NULL, NULL, NULL, KVM_NO_FILES, buf);
232 if (kd == NULL)
233 errx(STATUS_ERROR, "kvm_openfiles(): %s", buf);
234
235 plist = kvm_getproc2(kd, KERN_PROC_ALL, 0, sizeof(*plist), &nproc);
236 if (plist == NULL)
237 errx(STATUS_ERROR, "kvm_getproc2() failed");
238
239 /*
240 * Allocate memory which will be used to keep track of the
241 * selection.
242 */
243 if ((selected = malloc(nproc)) == NULL)
244 errx(STATUS_ERROR, "memory allocation failure");
245 memset(selected, 0, nproc);
246
247 /*
248 * Refine the selection.
249 */
250 for (; *argv != NULL; argv++) {
251 if ((rv = regcomp(®, *argv, REG_EXTENDED)) != 0) {
252 regerror(rv, ®, buf, sizeof(buf));
253 errx(STATUS_BADUSAGE, "bad expression: %s", buf);
254 }
255
256 for (i = 0, kp = plist; i < nproc; i++, kp++) {
257 if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid)
258 continue;
259
260 if (matchargs) {
261 if ((pargv = kvm_getargv2(kd, kp, 0)) == NULL)
262 continue;
263
264 j = 0;
265 while (j < sizeof(buf) && *pargv != NULL) {
266 int ret;
267
268 ret = snprintf(buf + j, sizeof(buf) - j,
269 pargv[1] != NULL ? "%s " : "%s",
270 pargv[0]);
271 if (ret >= sizeof(buf) - j)
272 j += sizeof(buf) - j - 1;
273 else if (ret > 0)
274 j += ret;
275 pargv++;
276 }
277
278 mstr = buf;
279 } else
280 mstr = kp->p_comm;
281
282 rv = regexec(®, mstr, 1, ®match, 0);
283 if (rv == 0) {
284 if (fullmatch) {
285 if (regmatch.rm_so == 0 &&
286 regmatch.rm_eo == strlen(mstr))
287 selected[i] = 1;
288 } else
289 selected[i] = 1;
290 } else if (rv != REG_NOMATCH) {
291 regerror(rv, ®, buf, sizeof(buf));
292 errx(STATUS_ERROR, "regexec(): %s", buf);
293 }
294 }
295
296 regfree(®);
297 }
298
299 for (i = 0, kp = plist; i < nproc; i++, kp++) {
300 if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid)
301 continue;
302
303 SLIST_FOREACH(li, &ruidlist, li_chain)
304 if (kp->p_ruid == (uid_t)li->li_number)
305 break;
306 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) {
307 selected[i] = 0;
308 continue;
309 }
310
311 SLIST_FOREACH(li, &rgidlist, li_chain)
312 if (kp->p_rgid == (gid_t)li->li_number)
313 break;
314 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) {
315 selected[i] = 0;
316 continue;
317 }
318
319 SLIST_FOREACH(li, &euidlist, li_chain)
320 if (kp->p_uid == (uid_t)li->li_number)
321 break;
322 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) {
323 selected[i] = 0;
324 continue;
325 }
326
327 SLIST_FOREACH(li, &ppidlist, li_chain)
328 if (kp->p_ppid == (uid_t)li->li_number)
329 break;
330 if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) {
331 selected[i] = 0;
332 continue;
333 }
334
335 SLIST_FOREACH(li, &pgrplist, li_chain)
336 if (kp->p__pgid == (uid_t)li->li_number)
337 break;
338 if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) {
339 selected[i] = 0;
340 continue;
341 }
342
343 SLIST_FOREACH(li, &tdevlist, li_chain) {
344 if (li->li_number == -1 &&
345 (kp->p_flag & P_CONTROLT) == 0)
346 break;
347 if (kp->p_tdev == (uid_t)li->li_number)
348 break;
349 }
350 if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) {
351 selected[i] = 0;
352 continue;
353 }
354
355 SLIST_FOREACH(li, &sidlist, li_chain)
356 if (kp->p_sid == (uid_t)li->li_number)
357 break;
358 if (SLIST_FIRST(&sidlist) != NULL && li == NULL) {
359 selected[i] = 0;
360 continue;
361 }
362
363 if (argc == 0)
364 selected[i] = 1;
365 }
366
367 if (newest) {
368 bestsec = 0;
369 bestusec = 0;
370 bestidx = -1;
371
372 for (i = 0, kp = plist; i < nproc; i++, kp++) {
373 if (!selected[i])
374 continue;
375
376 if (kp->p_ustart_sec > bestsec ||
377 (kp->p_ustart_sec == bestsec
378 && kp->p_ustart_usec > bestusec)) {
379 bestsec = kp->p_ustart_sec;
380 bestusec = kp->p_ustart_usec;
381 bestidx = i;
382 }
383 }
384
385 memset(selected, 0, nproc);
386 if (bestidx != -1)
387 selected[bestidx] = 1;
388 }
389
390 /*
391 * Take the appropriate action for each matched process, if any.
392 */
393 rv = STATUS_NOMATCH;
394 for (i = 0, j = 0, kp = plist; i < nproc; i++, kp++) {
395 if ((kp->p_flag & P_SYSTEM) != 0 || kp->p_pid == mypid)
396 continue;
397 if (selected[i]) {
398 if (inverse)
399 continue;
400 } else if (!inverse)
401 continue;
402
403 if ((*action)(kp, j++) == -1)
404 rv = STATUS_ERROR;
405 else if (rv != STATUS_ERROR)
406 rv = STATUS_MATCH;
407 }
408 if (pgrep && j)
409 putchar('\n');
410
411 exit(rv);
412 }
413
414 void
usage(void)415 usage(void)
416 {
417 const char *ustr;
418
419 if (pgrep)
420 ustr = "[-flnvx] [-d delim]";
421 else
422 ustr = "[-signal] [-fnvx]";
423
424 fprintf(stderr, "usage: %s %s [-G gid] [-g pgrp] [-P ppid] [-s sid] "
425 "[-t tty]\n\t[-U uid] [-u euid] [pattern ...]\n", __progname, ustr);
426
427 exit(STATUS_ERROR);
428 }
429
430 int
killact(struct kinfo_proc2 * kp,int dummy)431 killact(struct kinfo_proc2 *kp, int dummy)
432 {
433
434 if (kill(kp->p_pid, signum) == -1 && errno != ESRCH) {
435 warn("signalling pid %d", (int)kp->p_pid);
436 return (-1);
437 }
438 return (0);
439 }
440
441 int
grepact(struct kinfo_proc2 * kp,int printdelim)442 grepact(struct kinfo_proc2 *kp, int printdelim)
443 {
444 char **argv;
445
446 if (printdelim)
447 fputs(delim, stdout);
448 if (longfmt && matchargs) {
449 if ((argv = kvm_getargv2(kd, kp, 0)) == NULL)
450 return (-1);
451
452 printf("%d ", (int)kp->p_pid);
453 for (; *argv != NULL; argv++) {
454 printf("%s", *argv);
455 if (argv[1] != NULL)
456 putchar(' ');
457 }
458 } else if (longfmt)
459 printf("%d %s", (int)kp->p_pid, kp->p_comm);
460 else
461 printf("%d", (int)kp->p_pid);
462
463 return (0);
464 }
465
466 void
makelist(struct listhead * head,enum listtype type,char * src)467 makelist(struct listhead *head, enum listtype type, char *src)
468 {
469 struct list *li;
470 struct passwd *pw;
471 struct group *gr;
472 struct stat st;
473 char *sp, *p, buf[MAXPATHLEN];
474 int empty;
475
476 empty = 1;
477
478 while ((sp = strsep(&src, ",")) != NULL) {
479 if (*sp == '\0')
480 usage();
481
482 if ((li = malloc(sizeof(*li))) == NULL)
483 errx(STATUS_ERROR, "memory allocation failure");
484 SLIST_INSERT_HEAD(head, li, li_chain);
485 empty = 0;
486
487 li->li_number = (uid_t)strtol(sp, &p, 0);
488 if (*p == '\0') {
489 switch (type) {
490 case LT_PGRP:
491 if (li->li_number == 0)
492 li->li_number = getpgrp();
493 break;
494 case LT_SID:
495 if (li->li_number == 0)
496 li->li_number = getsid(mypid);
497 break;
498 case LT_TTY:
499 usage();
500 default:
501 break;
502 }
503 continue;
504 }
505
506 switch (type) {
507 case LT_USER:
508 if ((pw = getpwnam(sp)) == NULL)
509 errx(STATUS_BADUSAGE, "unknown user `%s'", sp);
510 li->li_number = pw->pw_uid;
511 break;
512 case LT_GROUP:
513 if ((gr = getgrnam(sp)) == NULL)
514 errx(STATUS_BADUSAGE, "unknown group `%s'", sp);
515 li->li_number = gr->gr_gid;
516 break;
517 case LT_TTY:
518 if (strcmp(sp, "-") == 0) {
519 li->li_number = -1;
520 break;
521 } else if (strcmp(sp, "co") == 0)
522 p = "console";
523 else if (strncmp(sp, "tty", 3) == 0)
524 p = sp;
525 else
526 p = NULL;
527
528 if (p == NULL)
529 snprintf(buf, sizeof(buf), "/dev/tty%s", sp);
530 else
531 snprintf(buf, sizeof(buf), "/dev/%s", p);
532
533 if (stat(buf, &st) < 0) {
534 if (errno == ENOENT)
535 errx(STATUS_BADUSAGE,
536 "no such tty: `%s'", sp);
537 err(STATUS_ERROR, "stat(%s)", sp);
538 }
539
540 if ((st.st_mode & S_IFCHR) == 0)
541 errx(STATUS_BADUSAGE, "not a tty: `%s'", sp);
542
543 li->li_number = st.st_rdev;
544 break;
545 default:
546 usage();
547 }
548 }
549
550 if (empty)
551 usage();
552 }
553