xref: /illumos-gate/usr/src/cmd/pgrep/pgrep.c (revision 69a119caa6570c7077699161b7c28b6ee9f8b0f4)
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