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