1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 /* Copyright (c) 2012 by Delphix. All rights reserved */ 26 27 #include <sys/types.h> 28 #include <sys/stat.h> 29 #include <sys/param.h> 30 #include <sys/task.h> 31 #include <sys/contract.h> 32 33 #include <signal.h> 34 #include <unistd.h> 35 #include <dirent.h> 36 #include <stdlib.h> 37 #include <string.h> 38 39 #include <libintl.h> 40 #include <locale.h> 41 #include <stdio.h> 42 #include <fcntl.h> 43 #include <ctype.h> 44 #include <wchar.h> 45 #include <limits.h> 46 #include <libuutil.h> 47 #include <libcontract_priv.h> 48 49 #include <procfs.h> 50 #include <project.h> 51 #include <pwd.h> 52 #include <grp.h> 53 #include <zone.h> 54 55 #include "psexp.h" 56 #include "pgrep.h" 57 58 #ifndef TEXT_DOMAIN 59 #define TEXT_DOMAIN "SYS_TEST" 60 #endif 61 62 #define OPT_SETB 0x0001 /* Set the bits specified by o_bits */ 63 #define OPT_CLRB 0x0002 /* Clear the bits specified by o_bits */ 64 #define OPT_FUNC 0x0004 /* Call the function specified by o_func */ 65 #define OPT_STR 0x0008 /* Set the string specified by o_ptr */ 66 #define OPT_CRIT 0x0010 /* Option is part of selection criteria */ 67 68 #define F_LONG_FMT 0x0001 /* Match against long format cmd */ 69 #define F_NEWEST 0x0002 /* Match only newest pid */ 70 #define F_REVERSE 0x0004 /* Reverse matching criteria */ 71 #define F_EXACT_MATCH 0x0008 /* Require exact match */ 72 #define F_HAVE_CRIT 0x0010 /* Criteria specified */ 73 #define F_OUTPUT 0x0020 /* Some output has been printed */ 74 #define F_KILL 0x0040 /* Pkill semantics active (vs pgrep) */ 75 #define F_LONG_OUT 0x0080 /* Long output format (pgrep -l) */ 76 #define F_OLDEST 0x0100 /* Match only oldest pid */ 77 78 static int opt_euid(char, char *); 79 static int opt_uid(char, char *); 80 static int opt_gid(char, char *); 81 static int opt_ppid(char, char *); 82 static int opt_pgrp(char, char *); 83 static int opt_sid(char, char *); 84 static int opt_term(char, char *); 85 static int opt_projid(char, char *); 86 static int opt_taskid(char, char *); 87 static int opt_zoneid(char, char *); 88 static int opt_ctid(char, char *); 89 90 static const char *g_procdir = "/proc"; /* Default procfs mount point */ 91 static const char *g_delim = "\n"; /* Default output delimiter */ 92 static const char *g_pname; /* Program name for error messages */ 93 static ushort_t g_flags; /* Miscellaneous flags */ 94 95 static optdesc_t g_optdtab[] = { 96 { 0, 0, 0, 0 }, /* 'A' */ 97 { 0, 0, 0, 0 }, /* 'B' */ 98 { 0, 0, 0, 0 }, /* 'C' */ 99 { OPT_STR, 0, 0, &g_procdir }, /* -D procfsdir */ 100 { 0, 0, 0, 0 }, /* 'E' */ 101 { 0, 0, 0, 0 }, /* 'F' */ 102 { OPT_FUNC | OPT_CRIT, 0, opt_gid, 0 }, /* -G gid */ 103 { 0, 0, 0, 0 }, /* 'H' */ 104 { 0, 0, 0, 0 }, /* 'I' */ 105 { OPT_FUNC | OPT_CRIT, 0, opt_projid, 0 }, /* -J projid */ 106 { 0, 0, 0, 0 }, /* 'K' */ 107 { 0, 0, 0, 0 }, /* 'L' */ 108 { 0, 0, 0, 0 }, /* 'M' */ 109 { 0, 0, 0, 0 }, /* 'N' */ 110 { 0, 0, 0, 0 }, /* 'O' */ 111 { OPT_FUNC | OPT_CRIT, 0, opt_ppid, 0 }, /* -P ppid */ 112 { 0, 0, 0, 0 }, /* 'Q' */ 113 { 0, 0, 0, 0 }, /* 'R' */ 114 { 0, 0, 0, 0 }, /* 'S' */ 115 { OPT_FUNC | OPT_CRIT, 0, opt_taskid, 0 }, /* -T taskid */ 116 { OPT_FUNC | OPT_CRIT, 0, opt_uid, 0 }, /* -U uid */ 117 { 0, 0, 0, 0 }, /* 'V' */ 118 { 0, 0, 0, 0 }, /* 'W' */ 119 { 0, 0, 0, 0 }, /* 'X' */ 120 { 0, 0, 0, 0 }, /* 'Y' */ 121 { 0, 0, 0, 0 }, /* 'Z' */ 122 { 0, 0, 0, 0 }, /* '[' */ 123 { 0, 0, 0, 0 }, /* '\\' */ 124 { 0, 0, 0, 0 }, /* ']' */ 125 { 0, 0, 0, 0 }, /* '^' */ 126 { 0, 0, 0, 0 }, /* '_' */ 127 { 0, 0, 0, 0 }, /* '`' */ 128 { 0, 0, 0, 0 }, /* 'a' */ 129 { 0, 0, 0, 0 }, /* 'b' */ 130 { OPT_FUNC | OPT_CRIT, 0, opt_ctid, 0 }, /* -c ctid */ 131 { OPT_STR, 0, 0, &g_delim }, /* -d delim */ 132 { 0, 0, 0, 0 }, /* 'e' */ 133 { OPT_SETB, F_LONG_FMT, 0, &g_flags }, /* -f */ 134 { OPT_FUNC | OPT_CRIT, 0, opt_pgrp, 0 }, /* -g pgrp */ 135 { 0, 0, 0, 0 }, /* 'h' */ 136 { 0, 0, 0, 0 }, /* 'i' */ 137 { 0, 0, 0, 0 }, /* 'j' */ 138 { 0, 0, 0, 0 }, /* 'k' */ 139 { OPT_SETB, F_LONG_OUT, 0, &g_flags }, /* 'l' */ 140 { 0, 0, 0, 0 }, /* 'm' */ 141 { OPT_SETB, F_NEWEST, 0, &g_flags }, /* -n */ 142 { OPT_SETB, F_OLDEST, 0, &g_flags }, /* -o */ 143 { 0, 0, 0, 0 }, /* 'p' */ 144 { 0, 0, 0, 0 }, /* 'q' */ 145 { 0, 0, 0, 0 }, /* 'r' */ 146 { OPT_FUNC | OPT_CRIT, 0, opt_sid, 0 }, /* -s sid */ 147 { OPT_FUNC | OPT_CRIT, 0, opt_term, 0 }, /* -t term */ 148 { OPT_FUNC | OPT_CRIT, 0, opt_euid, 0 }, /* -u euid */ 149 { OPT_SETB, F_REVERSE, 0, &g_flags }, /* -v */ 150 { 0, 0, 0, 0 }, /* 'w' */ 151 { OPT_SETB, F_EXACT_MATCH, 0, &g_flags }, /* -x */ 152 { 0, 0, 0, 0 }, /* 'y' */ 153 { OPT_FUNC | OPT_CRIT, 0, opt_zoneid, 0 } /* -z zoneid */ 154 }; 155 156 static const char PGREP_USAGE[] = "\ 157 Usage: %s [-flnovx] [-d delim] [-P ppidlist] [-g pgrplist] [-s sidlist]\n\ 158 [-u euidlist] [-U uidlist] [-G gidlist] [-J projidlist]\n\ 159 [-T taskidlist] [-t termlist] [-z zonelist] [-c ctidlist] [pattern]\n"; 160 161 static const char PKILL_USAGE[] = "\ 162 Usage: %s [-signal] [-fnovx] [-P ppidlist] [-g pgrplist] [-s sidlist]\n\ 163 [-u euidlist] [-U uidlist] [-G gidlist] [-J projidlist]\n\ 164 [-T taskidlist] [-t termlist] [-z zonelist] [-c ctidlist] [pattern]\n"; 165 166 static const char PGREP_OPTS[] = ":flnovxc:d:D:u:U:G:P:g:s:t:z:J:T:"; 167 static const char PKILL_OPTS[] = ":fnovxc:D:u:U:G:P:g:s:t:z:J:T:"; 168 169 static const char LSEP[] = ",\t "; /* Argument list delimiter chars */ 170 171 static psexp_t g_psexp; /* Process matching expression */ 172 static pid_t g_pid; /* Current pid */ 173 static int g_signal = SIGTERM; /* Signal to send */ 174 175 static void 176 print_proc(psinfo_t *psinfo) 177 { 178 if (g_flags & F_OUTPUT) 179 (void) printf("%s%d", g_delim, (int)psinfo->pr_pid); 180 else { 181 (void) printf("%d", (int)psinfo->pr_pid); 182 g_flags |= F_OUTPUT; 183 } 184 } 185 186 static char * 187 mbstrip(char *buf, size_t nbytes) 188 { 189 wchar_t wc; 190 char *p; 191 int n; 192 193 buf[nbytes - 1] = '\0'; 194 p = buf; 195 196 while (*p != '\0') { 197 n = mbtowc(&wc, p, MB_LEN_MAX); 198 199 if (n < 0 || !iswprint(wc)) { 200 if (n < 0) 201 n = sizeof (char); 202 203 if (nbytes <= n) { 204 *p = '\0'; 205 break; 206 } 207 208 (void) memmove(p, p + n, nbytes - n); 209 210 } else { 211 nbytes -= n; 212 p += n; 213 } 214 } 215 216 return (buf); 217 } 218 219 static void 220 print_proc_long(psinfo_t *psinfo) 221 { 222 char *name; 223 224 if (g_flags & F_LONG_FMT) 225 name = mbstrip(psinfo->pr_psargs, PRARGSZ); 226 else 227 name = psinfo->pr_fname; 228 229 if (g_flags & F_OUTPUT) 230 (void) printf("%s%5d %s", g_delim, (int)psinfo->pr_pid, name); 231 else { 232 (void) printf("%5d %s", (int)psinfo->pr_pid, name); 233 g_flags |= F_OUTPUT; 234 } 235 } 236 237 static void 238 kill_proc(psinfo_t *psinfo) 239 { 240 if (psinfo->pr_pid > 0 && kill(psinfo->pr_pid, g_signal) == -1) 241 uu_warn(gettext("Failed to signal pid %d"), 242 (int)psinfo->pr_pid); 243 } 244 245 static DIR * 246 open_proc_dir(const char *dirpath) 247 { 248 struct stat buf; 249 DIR *dirp; 250 251 if ((dirp = opendir(dirpath)) == NULL) { 252 uu_warn(gettext("Failed to open %s"), dirpath); 253 return (NULL); 254 } 255 256 if (fstat(dirp->dd_fd, &buf) == -1) { 257 uu_warn(gettext("Failed to stat %s"), dirpath); 258 (void) closedir(dirp); 259 return (NULL); 260 } 261 262 if (strcmp(buf.st_fstype, "proc") != 0) { 263 uu_warn(gettext("%s is not a procfs mount point\n"), dirpath); 264 (void) closedir(dirp); 265 return (NULL); 266 } 267 268 return (dirp); 269 } 270 271 #define NEWER(ps1, ps2) \ 272 ((ps1.pr_start.tv_sec > ps2.pr_start.tv_sec) || \ 273 (ps1.pr_start.tv_sec == ps2.pr_start.tv_sec && \ 274 ps1.pr_start.tv_nsec > ps2.pr_start.tv_nsec)) 275 276 static int 277 scan_proc_dir(const char *dirpath, DIR *dirp, psexp_t *psexp, 278 void (*funcp)(psinfo_t *)) 279 { 280 char procpath[MAXPATHLEN]; 281 psinfo_t ps, ops; 282 dirent_t *dent; 283 int procfd; 284 285 int reverse = (g_flags & F_REVERSE) ? 1 : 0; 286 int ovalid = 0, nmatches = 0, flags = 0; 287 288 if (g_flags & F_LONG_FMT) 289 flags |= PSEXP_PSARGS; 290 291 if (g_flags & F_EXACT_MATCH) 292 flags |= PSEXP_EXACT; 293 294 while ((dent = readdir(dirp)) != NULL) { 295 296 if (dent->d_name[0] == '.') 297 continue; 298 299 (void) snprintf(procpath, sizeof (procpath), "%s/%s/psinfo", 300 dirpath, dent->d_name); 301 302 if ((procfd = open(procpath, O_RDONLY)) == -1) 303 continue; 304 305 if ((read(procfd, &ps, sizeof (ps)) == sizeof (psinfo_t)) && 306 (ps.pr_nlwp != 0) && (ps.pr_pid != g_pid) && 307 (psexp_match(psexp, &ps, flags) ^ reverse)) { 308 309 if (g_flags & F_NEWEST) { 310 /* LINTED - opsinfo use ok */ 311 if (!ovalid || NEWER(ps, ops)) { 312 (void) memcpy(&ops, &ps, 313 sizeof (psinfo_t)); 314 ovalid = 1; 315 } 316 } else if (g_flags & F_OLDEST) { 317 if (!ovalid || NEWER(ops, ps)) { 318 (void) memcpy(&ops, &ps, 319 sizeof (psinfo_t)); 320 ovalid = 1; 321 } 322 } else { 323 (*funcp)(&ps); 324 nmatches++; 325 } 326 } 327 328 (void) close(procfd); 329 } 330 331 if ((g_flags & (F_NEWEST | F_OLDEST)) && ovalid) { 332 (*funcp)(&ops); 333 nmatches++; 334 } 335 336 return (nmatches); 337 } 338 339 static int 340 parse_ids(idtab_t *idt, char *arg, int base, int opt, idkey_t zero) 341 { 342 char *ptr, *next; 343 idkey_t id; 344 345 for (ptr = strtok(arg, LSEP); ptr != NULL; ptr = strtok(NULL, LSEP)) { 346 if ((id = (idkey_t)strtoul(ptr, &next, base)) != 0) 347 idtab_append(idt, id); 348 else 349 idtab_append(idt, zero); 350 351 if (next == ptr || *next != 0) { 352 uu_warn("invalid argument for option '%c' -- %s\n", 353 opt, ptr); 354 return (-1); 355 } 356 } 357 358 return (0); 359 } 360 361 static int 362 parse_uids(idtab_t *idt, char *arg) 363 { 364 char *ptr, *next; 365 struct passwd *pwent; 366 idkey_t id; 367 368 for (ptr = strtok(arg, LSEP); ptr != NULL; ptr = strtok(NULL, LSEP)) { 369 if (isdigit(ptr[0])) { 370 id = strtol(ptr, &next, 10); 371 372 if (next != ptr && *next == '\0') { 373 idtab_append(idt, id); 374 continue; 375 } 376 } 377 378 if ((pwent = getpwnam(ptr)) != NULL) 379 idtab_append(idt, pwent->pw_uid); 380 else 381 goto err; 382 } 383 384 return (0); 385 386 err: 387 uu_warn(gettext("invalid user name -- %s\n"), ptr); 388 return (-1); 389 } 390 391 static int 392 parse_gids(idtab_t *idt, char *arg) 393 { 394 char *ptr, *next; 395 struct group *grent; 396 idkey_t id; 397 398 for (ptr = strtok(arg, LSEP); ptr != NULL; ptr = strtok(NULL, LSEP)) { 399 if (isdigit(ptr[0])) { 400 id = strtol(ptr, &next, 10); 401 402 if (next != ptr && *next == '\0') { 403 idtab_append(idt, id); 404 continue; 405 } 406 } 407 408 if ((grent = getgrnam(ptr)) != NULL) 409 idtab_append(idt, grent->gr_gid); 410 else 411 goto err; 412 } 413 414 return (0); 415 416 err: 417 uu_warn(gettext("invalid group name -- %s\n"), ptr); 418 return (-1); 419 } 420 421 static int 422 parse_ttys(idtab_t *idt, char *arg) 423 { 424 char devpath[MAXPATHLEN]; 425 struct stat buf; 426 char *ptr; 427 428 int seen_console = 0; /* Flag so we only stat syscon and systty once */ 429 430 for (ptr = strtok(arg, LSEP); ptr != NULL; ptr = strtok(NULL, LSEP)) { 431 if (strcmp(ptr, "none") == 0) { 432 idtab_append(idt, (idkey_t)PRNODEV); 433 continue; 434 } 435 436 if (strcmp(ptr, "console") == 0) { 437 if (seen_console) 438 continue; 439 440 if (stat("/dev/syscon", &buf) == 0) 441 idtab_append(idt, (idkey_t)buf.st_rdev); 442 443 if (stat("/dev/systty", &buf) == 0) 444 idtab_append(idt, (idkey_t)buf.st_rdev); 445 446 seen_console++; 447 } 448 449 (void) snprintf(devpath, MAXPATHLEN - 1, "/dev/%s", ptr); 450 451 if (stat(devpath, &buf) == -1) 452 goto err; 453 454 idtab_append(idt, (idkey_t)buf.st_rdev); 455 } 456 457 return (0); 458 459 err: 460 uu_warn(gettext("unknown terminal name -- %s\n"), ptr); 461 return (-1); 462 } 463 464 static int 465 parse_projects(idtab_t *idt, char *arg) 466 { 467 char *ptr, *next; 468 projid_t projid; 469 idkey_t id; 470 471 for (ptr = strtok(arg, LSEP); ptr != NULL; ptr = strtok(NULL, LSEP)) { 472 if (isdigit(ptr[0])) { 473 id = strtol(ptr, &next, 10); 474 475 if (next != ptr && *next == '\0') { 476 idtab_append(idt, id); 477 continue; 478 } 479 } 480 481 if ((projid = getprojidbyname(ptr)) != -1) 482 idtab_append(idt, projid); 483 else 484 goto err; 485 } 486 487 return (0); 488 489 err: 490 uu_warn(gettext("invalid project name -- %s\n"), ptr); 491 return (-1); 492 } 493 494 static int 495 parse_zones(idtab_t *idt, char *arg) 496 { 497 char *ptr; 498 zoneid_t id; 499 500 for (ptr = strtok(arg, LSEP); ptr != NULL; ptr = strtok(NULL, LSEP)) { 501 if (zone_get_id(ptr, &id) != 0) { 502 uu_warn(gettext("invalid zone name -- %s\n"), ptr); 503 return (-1); 504 } 505 idtab_append(idt, id); 506 } 507 508 return (0); 509 } 510 511 /*ARGSUSED*/ 512 static int 513 opt_euid(char c, char *arg) 514 { 515 return (parse_uids(&g_psexp.ps_euids, arg)); 516 } 517 518 /*ARGSUSED*/ 519 static int 520 opt_uid(char c, char *arg) 521 { 522 return (parse_uids(&g_psexp.ps_ruids, arg)); 523 } 524 525 /*ARGSUSED*/ 526 static int 527 opt_gid(char c, char *arg) 528 { 529 return (parse_gids(&g_psexp.ps_rgids, arg)); 530 } 531 532 static int 533 opt_ppid(char c, char *arg) 534 { 535 return (parse_ids(&g_psexp.ps_ppids, arg, 10, c, 0)); 536 } 537 538 static int 539 opt_pgrp(char c, char *arg) 540 { 541 return (parse_ids(&g_psexp.ps_pgids, arg, 10, c, getpgrp())); 542 } 543 544 static int 545 opt_sid(char c, char *arg) 546 { 547 return (parse_ids(&g_psexp.ps_sids, arg, 10, c, getsid(0))); 548 } 549 550 /*ARGSUSED*/ 551 static int 552 opt_term(char c, char *arg) 553 { 554 return (parse_ttys(&g_psexp.ps_ttys, arg)); 555 } 556 557 /*ARGSUSED*/ 558 static int 559 opt_projid(char c, char *arg) 560 { 561 return (parse_projects(&g_psexp.ps_projids, arg)); 562 } 563 564 static int 565 opt_taskid(char c, char *arg) 566 { 567 return (parse_ids(&g_psexp.ps_taskids, arg, 10, c, gettaskid())); 568 } 569 570 /*ARGSUSED*/ 571 static int 572 opt_zoneid(char c, char *arg) 573 { 574 return (parse_zones(&g_psexp.ps_zoneids, arg)); 575 } 576 577 static int 578 opt_ctid(char c, char *arg) 579 { 580 return (parse_ids(&g_psexp.ps_ctids, arg, 10, c, getctid())); 581 } 582 583 static void 584 print_usage(FILE *stream) 585 { 586 if (g_flags & F_KILL) 587 (void) fprintf(stream, gettext(PKILL_USAGE), g_pname); 588 else 589 (void) fprintf(stream, gettext(PGREP_USAGE), g_pname); 590 } 591 592 int 593 main(int argc, char *argv[]) 594 { 595 void (*funcp)(psinfo_t *); 596 597 const char *optstr; 598 optdesc_t *optd; 599 int nmatches, c; 600 601 DIR *dirp; 602 603 (void) setlocale(LC_ALL, ""); 604 (void) textdomain(TEXT_DOMAIN); 605 606 UU_EXIT_FATAL = E_ERROR; 607 608 g_pname = uu_setpname(argv[0]); 609 g_pid = getpid(); 610 611 psexp_create(&g_psexp); 612 613 if (strcmp(g_pname, "pkill") == 0) { 614 615 if (argc > 1 && argv[1][0] == '-' && 616 str2sig(&argv[1][1], &g_signal) == 0) { 617 argv[1] = argv[0]; 618 argv++; 619 argc--; 620 } 621 622 optstr = PKILL_OPTS; 623 g_flags |= F_KILL; 624 } else 625 optstr = PGREP_OPTS; 626 627 opterr = 0; 628 629 while (optind < argc) { 630 while ((c = getopt(argc, argv, optstr)) != (int)EOF) { 631 632 if (c == ':' || c == '?' || 633 g_optdtab[c - 'A'].o_opts == 0) { 634 if (c == ':') { 635 uu_warn( 636 gettext("missing argument -- %c\n"), 637 optopt); 638 } else if (optopt != '?') { 639 uu_warn( 640 gettext("illegal option -- %c\n"), 641 optopt); 642 } 643 644 print_usage(stderr); 645 return (E_USAGE); 646 } 647 648 optd = &g_optdtab[c - 'A']; 649 650 if (optd->o_opts & OPT_SETB) 651 *((ushort_t *)optd->o_ptr) |= optd->o_bits; 652 653 if (optd->o_opts & OPT_CLRB) 654 *((ushort_t *)optd->o_ptr) &= ~optd->o_bits; 655 656 if (optd->o_opts & OPT_STR) 657 *((char **)optd->o_ptr) = optarg; 658 659 if (optd->o_opts & OPT_CRIT) 660 g_flags |= F_HAVE_CRIT; 661 662 if (optd->o_opts & OPT_FUNC) { 663 if (optd->o_func(c, optarg) == -1) 664 return (E_USAGE); 665 } 666 } 667 668 if (optind < argc) { 669 if (g_psexp.ps_pat != NULL) { 670 uu_warn(gettext("illegal argument -- %s\n"), 671 argv[optind]); 672 print_usage(stderr); 673 return (E_USAGE); 674 } 675 676 g_psexp.ps_pat = argv[optind++]; 677 g_flags |= F_HAVE_CRIT; 678 } 679 } 680 681 if ((g_flags & F_NEWEST) && (g_flags & F_OLDEST)) { 682 uu_warn(gettext("-n and -o are mutually exclusive\n")); 683 print_usage(stderr); 684 return (E_USAGE); 685 } 686 687 if ((g_flags & F_HAVE_CRIT) == 0) { 688 uu_warn(gettext("No matching criteria specified\n")); 689 print_usage(stderr); 690 return (E_USAGE); 691 } 692 693 if (psexp_compile(&g_psexp) == -1) { 694 psexp_destroy(&g_psexp); 695 return (E_USAGE); 696 } 697 698 if ((dirp = open_proc_dir(g_procdir)) == NULL) 699 return (E_ERROR); 700 701 if (g_flags & F_KILL) 702 funcp = kill_proc; 703 else if (g_flags & F_LONG_OUT) 704 funcp = print_proc_long; 705 else 706 funcp = print_proc; 707 708 nmatches = scan_proc_dir(g_procdir, dirp, &g_psexp, funcp); 709 710 if (g_flags & F_OUTPUT) 711 (void) fputc('\n', stdout); 712 713 psexp_destroy(&g_psexp); 714 return (nmatches ? E_MATCH : E_NOMATCH); 715 } 716