1 /* $NetBSD: pkill.c,v 1.7 2004/02/15 17:03:30 soren Exp $ */ 2 /* $DragonFly: src/usr.bin/pkill/pkill.c,v 1.9 2007/02/01 10:33:26 corecode 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 #include <sys/types.h> 41 #include <sys/param.h> 42 #include <sys/sysctl.h> 43 #include <sys/user.h> 44 #include <sys/queue.h> 45 #include <sys/stat.h> 46 #include <sys/fcntl.h> 47 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <limits.h> 51 #include <paths.h> 52 #include <string.h> 53 #include <unistd.h> 54 #include <signal.h> 55 #include <regex.h> 56 #include <ctype.h> 57 #include <kvm.h> 58 #include <err.h> 59 #include <pwd.h> 60 #include <grp.h> 61 #include <errno.h> 62 63 #define STATUS_MATCH 0 64 #define STATUS_NOMATCH 1 65 #define STATUS_BADUSAGE 2 66 #define STATUS_ERROR 3 67 68 enum listtype { 69 LT_USER, /* real or effective user: uid_t */ 70 LT_GROUP, /* group: gid_t */ 71 LT_TTY, /* tty: dev_t */ 72 LT_PPID, /* parent pid: pid_t */ 73 LT_PGRP, /* process group: pid_t */ 74 LT_SID /* session id: pid_t */ 75 }; 76 77 struct list { 78 SLIST_ENTRY(list) li_chain; 79 union { 80 uid_t ld_uid; 81 gid_t ld_gid; 82 pid_t ld_pid; 83 dev_t ld_dev; 84 } li_datum; 85 }; 86 87 SLIST_HEAD(listhead, list); 88 89 struct kinfo_proc *plist; 90 char *selected; 91 const char *delim = "\n"; 92 int nproc; 93 int pgrep; 94 int signum = SIGTERM; 95 int newest; 96 int inverse; 97 int longfmt; 98 int matchargs; 99 int fullmatch; 100 kvm_t *kd; 101 pid_t mypid; 102 103 struct listhead euidlist = SLIST_HEAD_INITIALIZER(list); 104 struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list); 105 struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list); 106 struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list); 107 struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list); 108 struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list); 109 struct listhead sidlist = SLIST_HEAD_INITIALIZER(list); 110 111 void usage(void); 112 void killact(struct kinfo_proc *, int); 113 void grepact(struct kinfo_proc *, int); 114 int parse_pid(const char *, char **, struct list *, pid_t); 115 void makelist(struct listhead *, enum listtype, char *); 116 117 /* 118 * pkill - list or signal selected processes based on regular expression. 119 */ 120 int 121 main(int argc, char **argv) 122 { 123 char buf[_POSIX2_LINE_MAX], *mstr, **pargv, *p, *q; 124 int i, ch, bestidx, rv, criteria; 125 unsigned int j; 126 void (*action)(struct kinfo_proc *, int); 127 struct kinfo_proc *kp; 128 struct list *li; 129 struct timeval best; 130 regex_t reg; 131 regmatch_t regmatch; 132 const char *kvmf = _PATH_DEVNULL; 133 134 if (strcmp(getprogname(), "pgrep") == 0) { 135 action = grepact; 136 pgrep = 1; 137 } else { 138 action = killact; 139 p = argv[1]; 140 141 /* 142 * For pkill only: parse the signal (number or name) to send. 143 */ 144 if (argc > 1 && p[0] == '-') { 145 p++; 146 i = (int)strtol(p, &q, 10); 147 if (*q == '\0') { 148 signum = i; 149 argv++; 150 argc--; 151 } else { 152 if (strncasecmp(p, "sig", 3) == 0) 153 p += 3; 154 for (i = 1; i < NSIG; i++) { 155 if (strcasecmp(sys_signame[i], p) == 0) 156 break; 157 } 158 if (i != NSIG) { 159 signum = i; 160 argv++; 161 argc--; 162 } 163 } 164 } 165 } 166 167 criteria = 0; 168 169 while ((ch = getopt(argc, argv, "G:P:U:d:fg:lns:t:u:vx")) != -1) { 170 switch (ch) { 171 case 'G': 172 makelist(&rgidlist, LT_GROUP, optarg); 173 criteria = 1; 174 break; 175 case 'P': 176 makelist(&ppidlist, LT_PPID, optarg); 177 criteria = 1; 178 break; 179 case 'U': 180 makelist(&ruidlist, LT_USER, optarg); 181 criteria = 1; 182 break; 183 case 'd': 184 if (!pgrep) 185 usage(); 186 delim = optarg; 187 break; 188 case 'f': 189 matchargs = 1; 190 break; 191 case 'g': 192 makelist(&pgrplist, LT_PGRP, optarg); 193 criteria = 1; 194 break; 195 case 'l': 196 if (!pgrep) 197 usage(); 198 longfmt = 1; 199 break; 200 case 'n': 201 newest = 1; 202 criteria = 1; 203 break; 204 case 's': 205 makelist(&sidlist, LT_SID, optarg); 206 criteria = 1; 207 break; 208 case 't': 209 makelist(&tdevlist, LT_TTY, optarg); 210 criteria = 1; 211 break; 212 case 'u': 213 makelist(&euidlist, LT_USER, optarg); 214 criteria = 1; 215 break; 216 case 'v': 217 inverse = 1; 218 break; 219 case 'x': 220 fullmatch = 1; 221 break; 222 default: 223 usage(); 224 /* NOTREACHED */ 225 } 226 } 227 228 argc -= optind; 229 argv += optind; 230 if (argc != 0) 231 criteria = 1; 232 if (!criteria) 233 usage(); 234 235 mypid = getpid(); 236 237 /* 238 * Retrieve the list of running processes from the kernel. 239 */ 240 kd = kvm_openfiles(kvmf, kvmf, NULL, O_RDONLY, buf); 241 if (kd == NULL) 242 errx(STATUS_ERROR, "kvm_openfiles(): %s", buf); 243 244 plist = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc); 245 if (plist == NULL) 246 errx(STATUS_ERROR, "cannot list processes"); 247 248 /* 249 * Allocate memory which will be used to keep track of the 250 * selection. 251 */ 252 if ((selected = malloc(nproc)) == NULL) 253 errx(STATUS_ERROR, "memory allocation failure"); 254 memset(selected, 0, nproc); 255 256 /* 257 * Refine the selection. 258 */ 259 for (; *argv != NULL; argv++) { 260 if ((rv = regcomp(®, *argv, REG_EXTENDED)) != 0) { 261 regerror(rv, ®, buf, sizeof(buf)); 262 errx(STATUS_BADUSAGE, "bad expression: %s", buf); 263 } 264 265 for (i = 0, kp = plist; i < nproc; i++, kp++) { 266 if ((kp->kp_flags & P_SYSTEM) != 0 || kp->kp_pid == mypid) 267 continue; 268 269 if (matchargs) { 270 if ((pargv = kvm_getargv(kd, kp, 0)) == NULL) 271 continue; 272 273 j = 0; 274 while (j < sizeof(buf) && *pargv != NULL) { 275 j += snprintf(buf + j, sizeof(buf) - j, 276 pargv[1] != NULL ? "%s " : "%s", 277 pargv[0]); 278 pargv++; 279 } 280 281 mstr = buf; 282 } else 283 mstr = kp->kp_comm; 284 285 rv = regexec(®, mstr, 1, ®match, 0); 286 if (rv == 0) { 287 if (fullmatch) { 288 if (regmatch.rm_so == 0 && 289 regmatch.rm_eo == (regoff_t)strlen(mstr)) 290 selected[i] = 1; 291 } else 292 selected[i] = 1; 293 } else if (rv != REG_NOMATCH) { 294 regerror(rv, ®, buf, sizeof(buf)); 295 errx(STATUS_ERROR, "regexec(): %s", buf); 296 } 297 } 298 299 regfree(®); 300 } 301 302 /* 303 * Iterate through the list of processes, deselecting each one 304 * if it fails to meet the established criteria. 305 */ 306 for (i = 0, kp = plist; i < nproc; i++, kp++) { 307 if ((kp->kp_flags & P_SYSTEM) != 0) 308 continue; 309 310 SLIST_FOREACH(li, &ruidlist, li_chain) { 311 if (kp->kp_ruid == li->li_datum.ld_uid) 312 break; 313 } 314 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) { 315 selected[i] = 0; 316 continue; 317 } 318 319 SLIST_FOREACH(li, &rgidlist, li_chain) { 320 if (kp->kp_rgid == li->li_datum.ld_gid) 321 break; 322 } 323 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) { 324 selected[i] = 0; 325 continue; 326 } 327 328 SLIST_FOREACH(li, &euidlist, li_chain) { 329 if (kp->kp_uid == li->li_datum.ld_uid) 330 break; 331 } 332 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) { 333 selected[i] = 0; 334 continue; 335 } 336 337 SLIST_FOREACH(li, &ppidlist, li_chain) { 338 if (kp->kp_ppid == li->li_datum.ld_pid) 339 break; 340 } 341 if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) { 342 selected[i] = 0; 343 continue; 344 } 345 346 SLIST_FOREACH(li, &pgrplist, li_chain) { 347 if (kp->kp_pgid == li->li_datum.ld_pid) 348 break; 349 } 350 if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) { 351 selected[i] = 0; 352 continue; 353 } 354 355 SLIST_FOREACH(li, &tdevlist, li_chain) { 356 if (li->li_datum.ld_dev == NODEV && 357 (kp->kp_flags & P_CONTROLT) == 0) 358 break; 359 if (kp->kp_tdev == li->li_datum.ld_dev) 360 break; 361 } 362 if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) { 363 selected[i] = 0; 364 continue; 365 } 366 367 SLIST_FOREACH(li, &sidlist, li_chain) { 368 if (kp->kp_sid == li->li_datum.ld_pid) 369 break; 370 } 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 best.tv_sec = 0; 382 best.tv_usec = 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->kp_start.tv_sec > best.tv_sec || 390 (kp->kp_start.tv_sec == best.tv_sec 391 && kp->kp_start.tv_usec > best.tv_usec)) { 392 best.tv_sec = kp->kp_start.tv_sec; 393 best.tv_usec = kp->kp_start.tv_usec; 394 bestidx = i; 395 } 396 } 397 398 memset(selected, 0, 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, j = 0, rv = 0, kp = plist; i < nproc; i++, kp++) { 407 if (kp->kp_pid == mypid) 408 continue; 409 if (selected[i]) { 410 if (inverse) 411 continue; 412 } else if (!inverse) 413 continue; 414 415 if ((kp->kp_flags & P_SYSTEM) != 0) 416 continue; 417 418 rv = 1; 419 (*action)(kp, j++); 420 } 421 422 if (pgrep) 423 putchar('\n'); 424 425 exit(rv ? STATUS_MATCH : STATUS_NOMATCH); 426 } 427 428 void 429 usage(void) 430 { 431 const char *ustr; 432 433 if (pgrep) 434 ustr = "[-flnvx] [-d delim]"; 435 else 436 ustr = "[-signal] [-fnvx]"; 437 438 fprintf(stderr, 439 "usage: %s %s [-G gid] [-P ppid] [-U uid] [-g pgrp] [-s sid]\n" 440 " [-t tty] [-u euid] pattern ...\n", getprogname(), 441 ustr); 442 443 exit(STATUS_ERROR); 444 } 445 446 /* 447 * Action callback to signal the given process (pkill). 448 */ 449 void 450 killact(struct kinfo_proc *kp, int dummy __unused) 451 { 452 if (kill(kp->kp_pid, signum) == -1) 453 err(STATUS_ERROR, "signalling pid %d", (int)kp->kp_pid); 454 } 455 456 /* 457 * Action callback to print the pid of the given process (pgrep). 458 */ 459 void 460 grepact(struct kinfo_proc *kp, int printdelim) 461 { 462 char **argv; 463 464 if (printdelim) 465 fputs(delim, stdout); 466 467 if (longfmt && matchargs) { 468 if ((argv = kvm_getargv(kd, kp, 0)) == NULL) 469 return; 470 471 printf("%d ", (int)kp->kp_pid); 472 for (; *argv != NULL; argv++) { 473 printf("%s", *argv); 474 if (argv[1] != NULL) 475 putchar(' '); 476 } 477 } else if (longfmt) 478 printf("%d %s", (int)kp->kp_pid, kp->kp_comm); 479 else 480 printf("%d", (int)kp->kp_pid); 481 482 } 483 484 /* 485 * Parse a pid from the given string. If zero, use a given default. 486 */ 487 int 488 parse_pid(const char *string, char **p, struct list *li, pid_t default_pid) 489 { 490 long l; 491 492 l = strtol(string, p, 0); 493 li->li_datum.ld_pid = (l == 0 ? default_pid : (pid_t)l); 494 return(**p == '\0'); 495 } 496 497 /* 498 * Populate a list from a comma-seperated string of items. 499 * The possible valid values for each item depends on the type of list. 500 */ 501 void 502 makelist(struct listhead *head, enum listtype type, char *src) 503 { 504 struct list *li; 505 struct passwd *pw; 506 struct group *gr; 507 struct stat st; 508 const char *sp, *tty_name; 509 char *p, buf[MAXPATHLEN]; 510 int empty; 511 512 empty = 1; 513 514 while ((sp = strsep(&src, ",")) != NULL) { 515 if (*sp == '\0') 516 usage(); 517 518 if ((li = malloc(sizeof(*li))) == NULL) 519 errx(STATUS_ERROR, "memory allocation failure"); 520 SLIST_INSERT_HEAD(head, li, li_chain); 521 empty = 0; 522 523 switch (type) { 524 case LT_PPID: 525 if (!parse_pid(sp, &p, li, (pid_t)0)) 526 usage(); 527 break; 528 case LT_PGRP: 529 if (!parse_pid(sp, &p, li, getpgrp())) 530 usage(); 531 break; 532 case LT_SID: 533 if (!parse_pid(sp, &p, li, getsid(mypid))) 534 usage(); 535 break; 536 case LT_USER: 537 li->li_datum.ld_uid = (uid_t)strtol(sp, &p, 0); 538 if (*p != '\0') { 539 if ((pw = getpwnam(sp)) == NULL) { 540 errx(STATUS_BADUSAGE, 541 "unknown user `%s'", optarg); 542 } 543 li->li_datum.ld_uid = pw->pw_uid; 544 } 545 break; 546 case LT_GROUP: 547 li->li_datum.ld_gid = (gid_t)strtol(sp, &p, 0); 548 if (*p != '\0') { 549 if ((gr = getgrnam(sp)) == NULL) { 550 errx(STATUS_BADUSAGE, 551 "unknown group `%s'", optarg); 552 } 553 li->li_datum.ld_gid = gr->gr_gid; 554 } 555 break; 556 case LT_TTY: 557 if (strcmp(sp, "-") == 0) { 558 li->li_datum.ld_dev = NODEV; 559 break; 560 } else if (strcmp(sp, "co") == 0) 561 tty_name = "console"; 562 else if (strncmp(sp, "tty", 3) == 0) 563 tty_name = sp; 564 else 565 tty_name = NULL; 566 567 if (tty_name == NULL) 568 snprintf(buf, sizeof(buf), "/dev/tty%s", sp); 569 else 570 snprintf(buf, sizeof(buf), "/dev/%s", tty_name); 571 572 if (stat(buf, &st) < 0) { 573 if (errno == ENOENT) 574 errx(STATUS_BADUSAGE, 575 "no such tty: `%s'", sp); 576 err(STATUS_ERROR, "stat(%s)", sp); 577 } 578 579 if ((st.st_mode & S_IFCHR) == 0) 580 errx(STATUS_BADUSAGE, "not a tty: `%s'", sp); 581 582 li->li_datum.ld_dev = st.st_rdev; 583 break; 584 default: 585 usage(); 586 } 587 } 588 589 if (empty) 590 usage(); 591 } 592