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.3 2004/10/25 21:48:37 liamfoy 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 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 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 void (*action)(struct kinfo_proc *); 119 struct kinfo_proc *kp; 120 struct list *li; 121 u_int32_t bestsec, bestusec; 122 regex_t reg; 123 regmatch_t regmatch; 124 const char *kvmf = _PATH_DEVNULL; 125 126 if (strcmp(getprogname(), "pgrep") == 0) { 127 action = grepact; 128 pgrep = 1; 129 } else { 130 action = killact; 131 p = argv[1]; 132 133 if (argc > 1 && p[0] == '-') { 134 p++; 135 i = (int)strtol(p, &q, 10); 136 if (*q == '\0') { 137 signum = i; 138 argv++; 139 argc--; 140 } else { 141 if (strncasecmp(p, "sig", 3) == 0) 142 p += 3; 143 for (i = 1; i < NSIG; i++) 144 if (strcasecmp(sys_signame[i], p) == 0) 145 break; 146 if (i != NSIG) { 147 signum = i; 148 argv++; 149 argc--; 150 } 151 } 152 } 153 } 154 155 criteria = 0; 156 157 while ((ch = getopt(argc, argv, "G:P:U:d:fg:lns:t:u:vx")) != -1) 158 switch (ch) { 159 case 'G': 160 makelist(&rgidlist, LT_GROUP, optarg); 161 criteria = 1; 162 break; 163 case 'P': 164 makelist(&ppidlist, LT_GENERIC, optarg); 165 criteria = 1; 166 break; 167 case 'U': 168 makelist(&ruidlist, LT_USER, optarg); 169 criteria = 1; 170 break; 171 case 'd': 172 if (!pgrep) 173 usage(); 174 delim = optarg; 175 break; 176 case 'f': 177 matchargs = 1; 178 break; 179 case 'g': 180 makelist(&pgrplist, LT_PGRP, optarg); 181 criteria = 1; 182 break; 183 case 'l': 184 if (!pgrep) 185 usage(); 186 longfmt = 1; 187 break; 188 case 'n': 189 newest = 1; 190 criteria = 1; 191 break; 192 case 's': 193 makelist(&sidlist, LT_SID, optarg); 194 criteria = 1; 195 break; 196 case 't': 197 makelist(&tdevlist, LT_TTY, optarg); 198 criteria = 1; 199 break; 200 case 'u': 201 makelist(&euidlist, LT_USER, optarg); 202 criteria = 1; 203 break; 204 case 'v': 205 inverse = 1; 206 break; 207 case 'x': 208 fullmatch = 1; 209 break; 210 default: 211 usage(); 212 /* NOTREACHED */ 213 } 214 215 argc -= optind; 216 argv += optind; 217 if (argc != 0) 218 criteria = 1; 219 if (!criteria) 220 usage(); 221 222 mypid = getpid(); 223 224 /* 225 * Retrieve the list of running processes from the kernel. 226 */ 227 kd = kvm_openfiles(kvmf, kvmf, NULL, O_RDONLY, buf); 228 if (kd == NULL) 229 errx(STATUS_ERROR, "kvm_openfiles(): %s", buf); 230 231 plist = kvm_getprocs(kd, KERN_PROC_ALL, 0, &nproc); 232 if (plist == NULL) 233 errx(STATUS_ERROR, "cannot list processes"); 234 235 /* 236 * Allocate memory which will be used to keep track of the 237 * selection. 238 */ 239 if ((selected = malloc(nproc)) == NULL) 240 errx(STATUS_ERROR, "memory allocation failure"); 241 memset(selected, 0, nproc); 242 243 /* 244 * Refine the selection. 245 */ 246 for (; *argv != NULL; argv++) { 247 if ((rv = regcomp(®, *argv, REG_EXTENDED)) != 0) { 248 regerror(rv, ®, buf, sizeof(buf)); 249 errx(STATUS_BADUSAGE, "bad expression: %s", buf); 250 } 251 252 for (i = 0, kp = plist; i < nproc; i++, kp++) { 253 if ((kp->kp_proc.p_flag & P_SYSTEM) != 0 || kp->kp_proc.p_pid == mypid) 254 continue; 255 256 if (matchargs) { 257 if ((pargv = kvm_getargv(kd, kp, 0)) == NULL) 258 continue; 259 260 j = 0; 261 while (j < sizeof(buf) && *pargv != NULL) { 262 j += snprintf(buf + j, sizeof(buf) - j, 263 pargv[1] != NULL ? "%s " : "%s", 264 pargv[0]); 265 pargv++; 266 } 267 268 mstr = buf; 269 } else 270 mstr = kp->kp_thread.td_comm; 271 272 rv = regexec(®, mstr, 1, ®match, 0); 273 if (rv == 0) { 274 if (fullmatch) { 275 if (regmatch.rm_so == 0 && 276 regmatch.rm_eo == strlen(mstr)) 277 selected[i] = 1; 278 } else 279 selected[i] = 1; 280 } else if (rv != REG_NOMATCH) { 281 regerror(rv, ®, buf, sizeof(buf)); 282 errx(STATUS_ERROR, "regexec(): %s", buf); 283 } 284 } 285 286 regfree(®); 287 } 288 289 for (i = 0, kp = plist; i < nproc; i++, kp++) { 290 if ((kp->kp_proc.p_flag & P_SYSTEM) != 0) 291 continue; 292 293 SLIST_FOREACH(li, &ruidlist, li_chain) 294 if (kp->kp_eproc.e_ucred.cr_ruid == (uid_t)li->li_number) 295 break; 296 if (SLIST_FIRST(&ruidlist) != NULL && li == NULL) { 297 selected[i] = 0; 298 continue; 299 } 300 301 SLIST_FOREACH(li, &rgidlist, li_chain) 302 if (kp->kp_eproc.e_ucred.cr_rgid == (gid_t)li->li_number) 303 break; 304 if (SLIST_FIRST(&rgidlist) != NULL && li == NULL) { 305 selected[i] = 0; 306 continue; 307 } 308 309 SLIST_FOREACH(li, &euidlist, li_chain) 310 if (kp->kp_eproc.e_ucred.cr_uid == (uid_t)li->li_number) 311 break; 312 if (SLIST_FIRST(&euidlist) != NULL && li == NULL) { 313 selected[i] = 0; 314 continue; 315 } 316 317 SLIST_FOREACH(li, &ppidlist, li_chain) 318 if (kp->kp_eproc.e_ppid == (uid_t)li->li_number) 319 break; 320 if (SLIST_FIRST(&ppidlist) != NULL && li == NULL) { 321 selected[i] = 0; 322 continue; 323 } 324 325 SLIST_FOREACH(li, &pgrplist, li_chain) 326 if (kp->kp_eproc.e_pgid == (uid_t)li->li_number) 327 break; 328 if (SLIST_FIRST(&pgrplist) != NULL && li == NULL) { 329 selected[i] = 0; 330 continue; 331 } 332 333 SLIST_FOREACH(li, &tdevlist, li_chain) { 334 if (li->li_number == -1 && 335 (kp->kp_proc.p_flag & P_CONTROLT) == 0) 336 break; 337 if (kp->kp_eproc.e_tdev == (uid_t)li->li_number) 338 break; 339 } 340 if (SLIST_FIRST(&tdevlist) != NULL && li == NULL) { 341 selected[i] = 0; 342 continue; 343 } 344 345 SLIST_FOREACH(li, &sidlist, li_chain) 346 if (kp->kp_eproc.e_sess->s_sid == (uid_t)li->li_number) 347 break; 348 if (SLIST_FIRST(&sidlist) != NULL && li == NULL) { 349 selected[i] = 0; 350 continue; 351 } 352 353 if (argc == 0) 354 selected[i] = 1; 355 } 356 357 if (newest) { 358 bestsec = 0; 359 bestusec = 0; 360 bestidx = -1; 361 362 for (i = 0, kp = plist; i < nproc; i++, kp++) { 363 if (!selected[i]) 364 continue; 365 366 if (kp->kp_thread.td_start.tv_sec > bestsec || 367 (kp->kp_thread.td_start.tv_sec == bestsec 368 && kp->kp_thread.td_start.tv_usec > bestusec)) { 369 bestsec = kp->kp_thread.td_start.tv_sec; 370 bestusec = kp->kp_thread.td_start.tv_usec; 371 bestidx = i; 372 } 373 } 374 375 memset(selected, 0, nproc); 376 if (bestidx != -1) 377 selected[bestidx] = 1; 378 } 379 380 /* 381 * Take the appropriate action for each matched process, if any. 382 */ 383 for (i = 0, rv = 0, kp = plist; i < nproc; i++, kp++) { 384 if (kp->kp_proc.p_pid == mypid) 385 continue; 386 if (selected[i]) { 387 if (inverse) 388 continue; 389 } else if (!inverse) 390 continue; 391 392 if ((kp->kp_proc.p_flag & P_SYSTEM) != 0) 393 continue; 394 395 rv = 1; 396 (*action)(kp); 397 } 398 399 exit(rv ? STATUS_MATCH : STATUS_NOMATCH); 400 } 401 402 void 403 usage(void) 404 { 405 const char *ustr; 406 407 if (pgrep) 408 ustr = "[-flnvx] [-d delim]"; 409 else 410 ustr = "[-signal] [-fnvx]"; 411 412 fprintf(stderr, 413 "usage: %s %s [-G gid] [-P ppid] [-U uid] [-g pgrp] [-s sid]\n" 414 " [-t tty] [-u euid] pattern ...\n", getprogname(), 415 ustr); 416 417 exit(STATUS_ERROR); 418 } 419 420 void 421 killact(struct kinfo_proc *kp) 422 { 423 424 if (kill(kp->kp_proc.p_pid, signum) == -1) 425 err(STATUS_ERROR, "signalling pid %d", (int)kp->kp_proc.p_pid); 426 } 427 428 void 429 grepact(struct kinfo_proc *kp) 430 { 431 char **argv; 432 433 if (longfmt && matchargs) { 434 if ((argv = kvm_getargv(kd, kp, 0)) == NULL) 435 return; 436 437 printf("%d ", (int)kp->kp_proc.p_pid); 438 for (; *argv != NULL; argv++) { 439 printf("%s", *argv); 440 if (argv[1] != NULL) 441 putchar(' '); 442 } 443 } else if (longfmt) 444 printf("%d %s", (int)kp->kp_proc.p_pid, kp->kp_thread.td_comm); 445 else 446 printf("%d", (int)kp->kp_proc.p_pid); 447 448 printf("%s", delim); 449 } 450 451 void 452 makelist(struct listhead *head, enum listtype type, char *src) 453 { 454 struct list *li; 455 struct passwd *pw; 456 struct group *gr; 457 struct stat st; 458 char *sp, *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 p = "console"; 510 else if (strncmp(sp, "tty", 3) == 0) 511 p = sp; 512 else 513 p = NULL; 514 515 if (p == NULL) 516 snprintf(buf, sizeof(buf), "/dev/tty%s", sp); 517 else 518 snprintf(buf, sizeof(buf), "/dev/%s", p); 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