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.4 2004/12/20 20:09:23 cpressey 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_GENERIC, 70 LT_USER, 71 LT_GROUP, 72 LT_TTY, 73 LT_PGRP, 74 LT_SID 75 }; 76 77 struct list { 78 SLIST_ENTRY(list) li_chain; 79 long li_number; 80 }; 81 82 SLIST_HEAD(listhead, list); 83 84 struct kinfo_proc *plist; 85 char *selected; 86 const char *delim = "\n"; 87 int nproc; 88 int pgrep; 89 int signum = SIGTERM; 90 int newest; 91 int inverse; 92 int longfmt; 93 int matchargs; 94 int fullmatch; 95 kvm_t *kd; 96 pid_t mypid; 97 98 struct listhead euidlist = SLIST_HEAD_INITIALIZER(list); 99 struct listhead ruidlist = SLIST_HEAD_INITIALIZER(list); 100 struct listhead rgidlist = SLIST_HEAD_INITIALIZER(list); 101 struct listhead pgrplist = SLIST_HEAD_INITIALIZER(list); 102 struct listhead ppidlist = SLIST_HEAD_INITIALIZER(list); 103 struct listhead tdevlist = SLIST_HEAD_INITIALIZER(list); 104 struct listhead sidlist = SLIST_HEAD_INITIALIZER(list); 105 106 void usage(void); 107 void killact(struct kinfo_proc *); 108 void grepact(struct kinfo_proc *); 109 void makelist(struct listhead *, enum listtype, char *); 110 111 int 112 main(int argc, char **argv) 113 { 114 char buf[_POSIX2_LINE_MAX], *mstr, **pargv, *p, *q; 115 int i, ch, bestidx, rv, criteria; 116 unsigned int j; 117 void (*action)(struct kinfo_proc *); 118 struct kinfo_proc *kp; 119 struct list *li; 120 struct timeval best; 121 regex_t reg; 122 regmatch_t regmatch; 123 const char *kvmf = _PATH_DEVNULL; 124 125 if (strcmp(getprogname(), "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:lns: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 's': 192 makelist(&sidlist, LT_SID, optarg); 193 criteria = 1; 194 break; 195 case 't': 196 makelist(&tdevlist, LT_TTY, optarg); 197 criteria = 1; 198 break; 199 case 'u': 200 makelist(&euidlist, LT_USER, optarg); 201 criteria = 1; 202 break; 203 case 'v': 204 inverse = 1; 205 break; 206 case 'x': 207 fullmatch = 1; 208 break; 209 default: 210 usage(); 211 /* NOTREACHED */ 212 } 213 214 argc -= optind; 215 argv += optind; 216 if (argc != 0) 217 criteria = 1; 218 if (!criteria) 219 usage(); 220 221 mypid = getpid(); 222 223 /* 224 * Retrieve the list of running processes from the kernel. 225 */ 226 kd = kvm_openfiles(kvmf, kvmf, NULL, O_RDONLY, buf); 227 if (kd == NULL) 228 errx(STATUS_ERROR, "kvm_openfiles(): %s", buf); 229 230 plist = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc); 231 if (plist == NULL) 232 errx(STATUS_ERROR, "cannot list processes"); 233 234 /* 235 * Allocate memory which will be used to keep track of the 236 * selection. 237 */ 238 if ((selected = malloc(nproc)) == NULL) 239 errx(STATUS_ERROR, "memory allocation failure"); 240 memset(selected, 0, nproc); 241 242 /* 243 * Refine the selection. 244 */ 245 for (; *argv != NULL; argv++) { 246 if ((rv = regcomp(®, *argv, REG_EXTENDED)) != 0) { 247 regerror(rv, ®, buf, sizeof(buf)); 248 errx(STATUS_BADUSAGE, "bad expression: %s", buf); 249 } 250 251 for (i = 0, kp = plist; i < nproc; i++, kp++) { 252 if ((kp->kp_proc.p_flag & P_SYSTEM) != 0 || kp->kp_proc.p_pid == mypid) 253 continue; 254 255 if (matchargs) { 256 if ((pargv = kvm_getargv(kd, kp, 0)) == NULL) 257 continue; 258 259 j = 0; 260 while (j < sizeof(buf) && *pargv != NULL) { 261 j += snprintf(buf + j, sizeof(buf) - j, 262 pargv[1] != NULL ? "%s " : "%s", 263 pargv[0]); 264 pargv++; 265 } 266 267 mstr = buf; 268 } else 269 mstr = kp->kp_thread.td_comm; 270 271 rv = regexec(®, mstr, 1, ®match, 0); 272 if (rv == 0) { 273 if (fullmatch) { 274 if (regmatch.rm_so == 0 && 275 regmatch.rm_eo == strlen(mstr)) 276 selected[i] = 1; 277 } else 278 selected[i] = 1; 279 } else if (rv != REG_NOMATCH) { 280 regerror(rv, ®, buf, sizeof(buf)); 281 errx(STATUS_ERROR, "regexec(): %s", buf); 282 } 283 } 284 285 regfree(®); 286 } 287 288 for (i = 0, kp = plist; i < nproc; i++, kp++) { 289 if ((kp->kp_proc.p_flag & P_SYSTEM) != 0) 290 continue; 291 292 SLIST_FOREACH(li, &ruidlist, li_chain) 293 if (kp->kp_eproc.e_ucred.cr_ruid == (uid_t)li->li_number) 294 break; 295 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) { 296 selected[i] = 0; 297 continue; 298 } 299 300 SLIST_FOREACH(li, &rgidlist, li_chain) 301 if (kp->kp_eproc.e_ucred.cr_rgid == (gid_t)li->li_number) 302 break; 303 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) { 304 selected[i] = 0; 305 continue; 306 } 307 308 SLIST_FOREACH(li, &euidlist, li_chain) 309 if (kp->kp_eproc.e_ucred.cr_uid == (uid_t)li->li_number) 310 break; 311 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) { 312 selected[i] = 0; 313 continue; 314 } 315 316 SLIST_FOREACH(li, &ppidlist, li_chain) 317 if (kp->kp_eproc.e_ppid == (pid_t)li->li_number) 318 break; 319 if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) { 320 selected[i] = 0; 321 continue; 322 } 323 324 SLIST_FOREACH(li, &pgrplist, li_chain) 325 if (kp->kp_eproc.e_pgid == (pid_t)li->li_number) 326 break; 327 if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) { 328 selected[i] = 0; 329 continue; 330 } 331 332 SLIST_FOREACH(li, &tdevlist, li_chain) { 333 if (li->li_number == -1 && 334 (kp->kp_proc.p_flag & P_CONTROLT) == 0) 335 break; 336 if (kp->kp_eproc.e_tdev == (uid_t)li->li_number) 337 break; 338 } 339 if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) { 340 selected[i] = 0; 341 continue; 342 } 343 344 SLIST_FOREACH(li, &sidlist, li_chain) 345 if (kp->kp_eproc.e_sess->s_sid == (pid_t)li->li_number) 346 break; 347 if (SLIST_FIRST(&sidlist) != NULL && li == NULL) { 348 selected[i] = 0; 349 continue; 350 } 351 352 if (argc == 0) 353 selected[i] = 1; 354 } 355 356 if (newest) { 357 best.tv_sec = 0; 358 best.tv_usec = 0; 359 bestidx = -1; 360 361 for (i = 0, kp = plist; i < nproc; i++, kp++) { 362 if (!selected[i]) 363 continue; 364 365 if (kp->kp_thread.td_start.tv_sec > best.tv_sec || 366 (kp->kp_thread.td_start.tv_sec == best.tv_sec 367 && kp->kp_thread.td_start.tv_usec > best.tv_usec)) { 368 best.tv_sec = kp->kp_thread.td_start.tv_sec; 369 best.tv_usec = kp->kp_thread.td_start.tv_usec; 370 bestidx = i; 371 } 372 } 373 374 memset(selected, 0, nproc); 375 if (bestidx != -1) 376 selected[bestidx] = 1; 377 } 378 379 /* 380 * Take the appropriate action for each matched process, if any. 381 */ 382 for (i = 0, rv = 0, kp = plist; i < nproc; i++, kp++) { 383 if (kp->kp_proc.p_pid == mypid) 384 continue; 385 if (selected[i]) { 386 if (inverse) 387 continue; 388 } else if (!inverse) 389 continue; 390 391 if ((kp->kp_proc.p_flag & P_SYSTEM) != 0) 392 continue; 393 394 rv = 1; 395 (*action)(kp); 396 } 397 398 exit(rv ? STATUS_MATCH : STATUS_NOMATCH); 399 } 400 401 void 402 usage(void) 403 { 404 const char *ustr; 405 406 if (pgrep) 407 ustr = "[-flnvx] [-d delim]"; 408 else 409 ustr = "[-signal] [-fnvx]"; 410 411 fprintf(stderr, 412 "usage: %s %s [-G gid] [-P ppid] [-U uid] [-g pgrp] [-s sid]\n" 413 " [-t tty] [-u euid] pattern ...\n", getprogname(), 414 ustr); 415 416 exit(STATUS_ERROR); 417 } 418 419 void 420 killact(struct kinfo_proc *kp) 421 { 422 423 if (kill(kp->kp_proc.p_pid, signum) == -1) 424 err(STATUS_ERROR, "signalling pid %d", (int)kp->kp_proc.p_pid); 425 } 426 427 void 428 grepact(struct kinfo_proc *kp) 429 { 430 char **argv; 431 432 if (longfmt && matchargs) { 433 if ((argv = kvm_getargv(kd, kp, 0)) == NULL) 434 return; 435 436 printf("%d ", (int)kp->kp_proc.p_pid); 437 for (; *argv != NULL; argv++) { 438 printf("%s", *argv); 439 if (argv[1] != NULL) 440 putchar(' '); 441 } 442 } else if (longfmt) 443 printf("%d %s", (int)kp->kp_proc.p_pid, kp->kp_thread.td_comm); 444 else 445 printf("%d", (int)kp->kp_proc.p_pid); 446 447 printf("%s", delim); 448 } 449 450 void 451 makelist(struct listhead *head, enum listtype type, char *src) 452 { 453 struct list *li; 454 struct passwd *pw; 455 struct group *gr; 456 struct stat st; 457 const char *sp, *tty_name; 458 char *p, buf[MAXPATHLEN]; 459 int empty; 460 461 empty = 1; 462 463 while ((sp = strsep(&src, ",")) != NULL) { 464 if (*sp == '\0') 465 usage(); 466 467 if ((li = malloc(sizeof(*li))) == NULL) 468 errx(STATUS_ERROR, "memory allocation failure"); 469 SLIST_INSERT_HEAD(head, li, li_chain); 470 empty = 0; 471 472 li->li_number = (uid_t)strtol(sp, &p, 0); 473 if (*p == '\0') { 474 switch (type) { 475 case LT_PGRP: 476 if (li->li_number == 0) 477 li->li_number = getpgrp(); 478 break; 479 case LT_SID: 480 if (li->li_number == 0) 481 li->li_number = getsid(mypid); 482 break; 483 case LT_TTY: 484 usage(); 485 default: 486 break; 487 } 488 continue; 489 } 490 491 switch (type) { 492 case LT_USER: 493 if ((pw = getpwnam(sp)) == NULL) 494 errx(STATUS_BADUSAGE, "unknown user `%s'", 495 optarg); 496 li->li_number = pw->pw_uid; 497 break; 498 case LT_GROUP: 499 if ((gr = getgrnam(sp)) == NULL) 500 errx(STATUS_BADUSAGE, "unknown group `%s'", 501 optarg); 502 li->li_number = gr->gr_gid; 503 break; 504 case LT_TTY: 505 if (strcmp(sp, "-") == 0) { 506 li->li_number = -1; 507 break; 508 } else if (strcmp(sp, "co") == 0) 509 tty_name = "console"; 510 else if (strncmp(sp, "tty", 3) == 0) 511 tty_name = sp; 512 else 513 tty_name = NULL; 514 515 if (tty_name == NULL) 516 snprintf(buf, sizeof(buf), "/dev/tty%s", sp); 517 else 518 snprintf(buf, sizeof(buf), "/dev/%s", tty_name); 519 520 if (stat(buf, &st) < 0) { 521 if (errno == ENOENT) 522 errx(STATUS_BADUSAGE, 523 "no such tty: `%s'", sp); 524 err(STATUS_ERROR, "stat(%s)", sp); 525 } 526 527 if ((st.st_mode & S_IFCHR) == 0) 528 errx(STATUS_BADUSAGE, "not a tty: `%s'", sp); 529 530 li->li_number = st.st_rdev; 531 break; 532 default: 533 usage(); 534 }; 535 } 536 537 if (empty) 538 usage(); 539 } 540