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