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 }