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