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