xref: /illumos-gate/usr/src/cmd/ptools/pargs/pargs.c (revision 7b34a9a5df26271af0da06974fc361c468cd48d3)
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 2007 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 /*
26  * Copyright (c) 2018, Joyent, Inc.
27  */
28 
29 /*
30  * pargs examines and prints the arguments (argv), environment (environ),
31  * and auxiliary vector of another process.
32  *
33  * This utility is made more complex because it must run in internationalized
34  * environments.  The two key cases for pargs to manage are:
35  *
36  * 1. pargs and target run in the same locale: pargs must respect the
37  * locale, but this case is straightforward.  Care is taken to correctly
38  * use wide characters in order to print results properly.
39  *
40  * 2. pargs and target run in different locales: in this case, pargs examines
41  * the string having assumed the victim's locale.  Unprintable (but valid)
42  * characters are escaped.  Next, iconv(3c) is used to convert between the
43  * target and pargs codeset.  Finally, a second pass to escape unprintable
44  * (but valid) characters is made.
45  *
46  * In any case in which characters are encountered which are not valid in
47  * their purported locale, the string "fails" and is treated as a traditional
48  * 7-bit ASCII encoded string, and escaped accordingly.
49  */
50 
51 #include <stdio.h>
52 #include <stdlib.h>
53 #include <locale.h>
54 #include <wchar.h>
55 #include <iconv.h>
56 #include <langinfo.h>
57 #include <unistd.h>
58 #include <ctype.h>
59 #include <fcntl.h>
60 #include <string.h>
61 #include <strings.h>
62 #include <limits.h>
63 #include <pwd.h>
64 #include <grp.h>
65 #include <errno.h>
66 #include <setjmp.h>
67 #include <sys/types.h>
68 #include <sys/auxv.h>
69 #include <sys/archsystm.h>
70 #include <sys/proc.h>
71 #include <sys/elf.h>
72 #include <libproc.h>
73 #include <wctype.h>
74 #include <widec.h>
75 #include <elfcap.h>
76 #include <libgen.h>
77 
78 typedef enum pargs_cmd {
79 	PARGS_ARGV,
80 	PARGS_ENV,
81 	PARGS_AUXV
82 } pargs_cmd_t;
83 
84 typedef struct pargs_data {
85 	struct ps_prochandle *pd_proc;	/* target proc handle */
86 	psinfo_t *pd_psinfo;		/* target psinfo */
87 	char *pd_locale;		/* target process locale */
88 	int pd_conv_flags;		/* flags governing string conversion */
89 	iconv_t pd_iconv;		/* iconv conversion descriptor */
90 	size_t pd_argc;
91 	uintptr_t *pd_argv;
92 	char **pd_argv_strs;
93 	size_t pd_envc;
94 	size_t pd_env_space;
95 	uintptr_t *pd_envp;
96 	char **pd_envp_strs;
97 	size_t pd_auxc;
98 	auxv_t *pd_auxv;
99 	char **pd_auxv_strs;
100 	char *pd_execname;
101 } pargs_data_t;
102 
103 #define	CONV_USE_ICONV		0x01
104 #define	CONV_STRICT_ASCII	0x02
105 
106 static char *command;
107 static int dmodel;
108 
109 #define	EXTRACT_BUFSZ 128		/* extract_string() initial size */
110 #define	ENV_CHUNK 16			/* #env ptrs to read at a time */
111 
112 static jmp_buf env;			/* malloc failure handling */
113 
114 static void *
115 safe_zalloc(size_t size)
116 {
117 	void *p;
118 
119 	/*
120 	 * If the malloc fails we longjmp out to allow the code to Prelease()
121 	 * a stopped victim if needed.
122 	 */
123 	if ((p = malloc(size)) == NULL) {
124 		longjmp(env, errno);
125 	}
126 
127 	bzero(p, size);
128 	return (p);
129 }
130 
131 static char *
132 safe_strdup(const char *s1)
133 {
134 	char	*s2;
135 
136 	s2 = safe_zalloc(strlen(s1) + 1);
137 	(void) strcpy(s2, s1);
138 	return (s2);
139 }
140 
141 /*
142  * Given a wchar_t which might represent an 'escapable' sequence (see
143  * formats(5)), return the base ascii character needed to print that
144  * sequence.
145  *
146  * The comparisons performed may look suspect at first, but all are valid;
147  * the characters below all appear in the "Portable Character Set."  The
148  * Single Unix Spec says: "The wide-character value for each member of the
149  * Portable Character Set will equal its value when used as the lone
150  * character in an integer character constant."
151  */
152 static uchar_t
153 get_interp_char(wchar_t wc)
154 {
155 	switch (wc) {
156 	case L'\a':
157 		return ('a');
158 	case L'\b':
159 		return ('b');
160 	case L'\f':
161 		return ('f');
162 	case L'\n':
163 		return ('n');
164 	case L'\r':
165 		return ('r');
166 	case L'\t':
167 		return ('t');
168 	case L'\v':
169 		return ('v');
170 	case L'\\':
171 		return ('\\');
172 	}
173 	return ('\0');
174 }
175 
176 static char *
177 unctrl_str_strict_ascii(const char *src, int escape_slash, int *unprintable)
178 {
179 	uchar_t *uc, *ucp, c, ic;
180 	uc = ucp = safe_zalloc((strlen(src) * 4) + 1);
181 	while ((c = *src++) != '\0') {
182 		/*
183 		 * Call get_interp_char *first*, since \ will otherwise not
184 		 * be escaped as \\.
185 		 */
186 		if ((ic = get_interp_char((wchar_t)c)) != '\0') {
187 			if (escape_slash || ic != '\\')
188 				*ucp++ = '\\';
189 			*ucp++ = ic;
190 		} else if (isascii(c) && isprint(c)) {
191 			*ucp++ = c;
192 		} else {
193 			*ucp++ = '\\';
194 			*ucp++ = ((c >> 6) & 7) + '0';
195 			*ucp++ = ((c >> 3) & 7) + '0';
196 			*ucp++ = (c & 7) + '0';
197 			*unprintable = 1;
198 		}
199 	}
200 	*ucp = '\0';
201 	return ((char *)uc);
202 }
203 
204 /*
205  * Convert control characters as described in format(5) to their readable
206  * representation; special care is taken to handle multibyte character sets.
207  *
208  * If escape_slash is true, escaping of '\' occurs.  The first time a string
209  * is unctrl'd, this should be '1'.  Subsequent iterations over the same
210  * string should set escape_slash to 0.  Otherwise you'll wind up with
211  * \ --> \\ --> \\\\.
212  */
213 static char *
214 unctrl_str(const char *src, int escape_slash, int *unprintable)
215 {
216 	wchar_t wc;
217 	wchar_t *wide_src, *wide_srcp;
218 	wchar_t *wide_dest, *wide_destp;
219 	char *uc;
220 	size_t srcbufsz = strlen(src) + 1;
221 	size_t destbufsz = srcbufsz * 4;
222 	size_t srclen, destlen;
223 
224 	wide_srcp = wide_src = safe_zalloc(srcbufsz * sizeof (wchar_t));
225 	wide_destp = wide_dest = safe_zalloc(destbufsz * sizeof (wchar_t));
226 
227 	if ((srclen = mbstowcs(wide_src, src, srcbufsz - 1)) == (size_t)-1) {
228 		/*
229 		 * We can't trust the string, since in the locale in which
230 		 * this call is operating, the string contains an invalid
231 		 * multibyte sequence.  There isn't much to do here, so
232 		 * convert the string byte by byte to wide characters, as
233 		 * if it came from a C locale (char) string.  This isn't
234 		 * perfect, but at least the characters will make it to
235 		 * the screen.
236 		 */
237 		free(wide_src);
238 		free(wide_dest);
239 		return (unctrl_str_strict_ascii(src, escape_slash,
240 		    unprintable));
241 	}
242 	if (srclen == (srcbufsz - 1)) {
243 		wide_src[srclen] = L'\0';
244 	}
245 
246 	while ((wc = *wide_srcp++) != L'\0') {
247 		char cvt_buf[MB_LEN_MAX];
248 		int len, i;
249 		char c = get_interp_char(wc);
250 
251 		if ((c != '\0') && (escape_slash || c != '\\')) {
252 			/*
253 			 * Print "interpreted version" (\n, \a, etc).
254 			 */
255 			*wide_destp++ = L'\\';
256 			*wide_destp++ = (wchar_t)c;
257 			continue;
258 		}
259 
260 		if (iswprint(wc)) {
261 			*wide_destp++ = wc;
262 			continue;
263 		}
264 
265 		/*
266 		 * Convert the wide char back into (potentially several)
267 		 * multibyte characters, then escape out each of those bytes.
268 		 */
269 		bzero(cvt_buf, sizeof (cvt_buf));
270 		if ((len = wctomb(cvt_buf, wc)) == -1) {
271 			/*
272 			 * This is a totally invalid wide char; discard it.
273 			 */
274 			continue;
275 		}
276 		for (i = 0; i < len; i++) {
277 			uchar_t c = cvt_buf[i];
278 			*wide_destp++ = L'\\';
279 			*wide_destp++ = (wchar_t)('0' + ((c >> 6) & 7));
280 			*wide_destp++ = (wchar_t)('0' + ((c >> 3) & 7));
281 			*wide_destp++ = (wchar_t)('0' + (c & 7));
282 			*unprintable = 1;
283 		}
284 	}
285 
286 	*wide_destp = '\0';
287 	destlen = (wide_destp - wide_dest) * MB_CUR_MAX + 1;
288 	uc = safe_zalloc(destlen);
289 	if (wcstombs(uc, wide_dest, destlen) == (size_t)-1) {
290 		/* If we've gotten this far, wcstombs shouldn't fail... */
291 		(void) fprintf(stderr, "%s: wcstombs failed unexpectedly: %s\n",
292 		    command, strerror(errno));
293 		exit(1);
294 	} else {
295 		char *tmp;
296 		/*
297 		 * Try to save memory; don't waste 3 * strlen in the
298 		 * common case.
299 		 */
300 		tmp = safe_strdup(uc);
301 		free(uc);
302 		uc = tmp;
303 	}
304 	free(wide_dest);
305 	free(wide_src);
306 	return (uc);
307 }
308 
309 /*
310  * These functions determine which characters are safe to be left unquoted.
311  * Rather than starting with every printable character and subtracting out the
312  * shell metacharacters, we take the more conservative approach of starting with
313  * a set of safe characters and adding those few common punctuation characters
314  * which are known to be safe.  The rules are:
315  *
316  *	If this is a printable character (graph), and not punctuation, it is
317  *	safe to leave unquoted.
318  *
319  *	If it's one of the known hard-coded safe characters, it's also safe to
320  *	leave unquoted.
321  *
322  *	Otherwise, the entire argument must be quoted.
323  *
324  * This will cause some strings to be unnecessarily quoted, but it is safer than
325  * having a character unintentionally interpreted by the shell.
326  */
327 static int
328 issafe_ascii(char c)
329 {
330 	return (isalnum(c) || strchr("_.-/@:,", c) != NULL);
331 }
332 
333 static int
334 issafe(wchar_t wc)
335 {
336 	return ((iswgraph(wc) && !iswpunct(wc)) ||
337 	    wschr(L"_.-/@:,", wc) != NULL);
338 }
339 
340 /*ARGSUSED*/
341 static char *
342 quote_string_ascii(pargs_data_t *datap, char *src)
343 {
344 	char *dst;
345 	int quote_count = 0;
346 	int need_quote = 0;
347 	char *srcp, *dstp;
348 	size_t dstlen;
349 
350 	for (srcp = src; *srcp != '\0'; srcp++) {
351 		if (!issafe_ascii(*srcp)) {
352 			need_quote = 1;
353 			if (*srcp == '\'')
354 				quote_count++;
355 		}
356 	}
357 
358 	if (!need_quote)
359 		return (src);
360 
361 	/*
362 	 * The only character we care about here is a single quote.  All the
363 	 * other unprintable characters (and backslashes) will have been dealt
364 	 * with by unctrl_str().  We make the following subtitution when we
365 	 * encounter a single quote:
366 	 *
367 	 *	' = '"'"'
368 	 *
369 	 * In addition, we put single quotes around the entire argument.  For
370 	 * example:
371 	 *
372 	 *	foo'bar = 'foo'"'"'bar'
373 	 */
374 	dstlen = strlen(src) + 3 + 4 * quote_count;
375 	dst = safe_zalloc(dstlen);
376 
377 	dstp = dst;
378 	*dstp++ = '\'';
379 	for (srcp = src; *srcp != '\0'; srcp++, dstp++) {
380 		*dstp = *srcp;
381 
382 		if (*srcp == '\'') {
383 			dstp[1] = '"';
384 			dstp[2] = '\'';
385 			dstp[3] = '"';
386 			dstp[4] = '\'';
387 			dstp += 4;
388 		}
389 	}
390 	*dstp++ = '\'';
391 	*dstp = '\0';
392 
393 	free(src);
394 
395 	return (dst);
396 }
397 
398 static char *
399 quote_string(pargs_data_t *datap, char *src)
400 {
401 	wchar_t *wide_src, *wide_srcp;
402 	wchar_t *wide_dest, *wide_destp;
403 	char *uc;
404 	size_t srcbufsz = strlen(src) + 1;
405 	size_t srclen;
406 	size_t destbufsz;
407 	size_t destlen;
408 	int quote_count = 0;
409 	int need_quote = 0;
410 
411 	if (datap->pd_conv_flags & CONV_STRICT_ASCII)
412 		return (quote_string_ascii(datap, src));
413 
414 	wide_srcp = wide_src = safe_zalloc(srcbufsz * sizeof (wchar_t));
415 
416 	if ((srclen = mbstowcs(wide_src, src, srcbufsz - 1)) == (size_t)-1) {
417 		free(wide_src);
418 		return (quote_string_ascii(datap, src));
419 	}
420 
421 	if (srclen == srcbufsz - 1)
422 		wide_src[srclen] = L'\0';
423 
424 	for (wide_srcp = wide_src; *wide_srcp != '\0'; wide_srcp++) {
425 		if (!issafe(*wide_srcp)) {
426 			need_quote = 1;
427 			if (*wide_srcp == L'\'')
428 				quote_count++;
429 		}
430 	}
431 
432 	if (!need_quote) {
433 		free(wide_src);
434 		return (src);
435 	}
436 
437 	/*
438 	 * See comment for quote_string_ascii(), above.
439 	 */
440 	destbufsz = srcbufsz + 3 + 4 * quote_count;
441 	wide_destp = wide_dest = safe_zalloc(destbufsz * sizeof (wchar_t));
442 
443 	*wide_destp++ = L'\'';
444 	for (wide_srcp = wide_src; *wide_srcp != L'\0';
445 	    wide_srcp++, wide_destp++) {
446 		*wide_destp = *wide_srcp;
447 
448 		if (*wide_srcp == L'\'') {
449 			wide_destp[1] = L'"';
450 			wide_destp[2] = L'\'';
451 			wide_destp[3] = L'"';
452 			wide_destp[4] = L'\'';
453 			wide_destp += 4;
454 		}
455 	}
456 	*wide_destp++ = L'\'';
457 	*wide_destp = L'\0';
458 
459 	destlen = destbufsz * MB_CUR_MAX + 1;
460 	uc = safe_zalloc(destlen);
461 	if (wcstombs(uc, wide_dest, destlen) == (size_t)-1) {
462 		/* If we've gotten this far, wcstombs shouldn't fail... */
463 		(void) fprintf(stderr, "%s: wcstombs failed unexpectedly: %s\n",
464 		    command, strerror(errno));
465 		exit(1);
466 	}
467 
468 	free(wide_dest);
469 	free(wide_src);
470 
471 	return (uc);
472 }
473 
474 
475 /*
476  * Determine the locale of the target process by traversing its environment,
477  * making only one pass for efficiency's sake; stash the result in
478  * datap->pd_locale.
479  *
480  * It's possible that the process has called setlocale() to change its
481  * locale to something different, but we mostly care about making a good
482  * guess as to the locale at exec(2) time.
483  */
484 static void
485 lookup_locale(pargs_data_t *datap)
486 {
487 	int i, j, composite = 0;
488 	size_t	len = 0;
489 	char	*pd_locale;
490 	char	*lc_all = NULL, *lang = NULL;
491 	char	*lcs[] = { NULL, NULL, NULL, NULL, NULL, NULL };
492 	static const char *cat_names[] = {
493 		"LC_CTYPE=",	"LC_NUMERIC=",	"LC_TIME=",
494 		"LC_COLLATE=",	"LC_MONETARY=",	"LC_MESSAGES="
495 	};
496 
497 	for (i = 0; i < datap->pd_envc; i++) {
498 		char *s = datap->pd_envp_strs[i];
499 
500 		if (s == NULL)
501 			continue;
502 
503 		if (strncmp("LC_ALL=", s, strlen("LC_ALL=")) == 0) {
504 			/*
505 			 * Minor optimization-- if we find LC_ALL we're done.
506 			 */
507 			lc_all = s + strlen("LC_ALL=");
508 			break;
509 		}
510 		for (j = 0; j <= _LastCategory; j++) {
511 			if (strncmp(cat_names[j], s,
512 			    strlen(cat_names[j])) == 0) {
513 				lcs[j] = s + strlen(cat_names[j]);
514 			}
515 		}
516 		if (strncmp("LANG=", s, strlen("LANG=")) == 0) {
517 			lang = s + strlen("LANG=");
518 		}
519 	}
520 
521 	if (lc_all && (*lc_all == '\0'))
522 		lc_all = NULL;
523 	if (lang && (*lang == '\0'))
524 		lang = NULL;
525 
526 	for (i = 0; i <= _LastCategory; i++) {
527 		if (lc_all != NULL) {
528 			lcs[i] = lc_all;
529 		} else if (lcs[i] != NULL) {
530 			lcs[i] = lcs[i];
531 		} else if (lang != NULL) {
532 			lcs[i] = lang;
533 		} else {
534 			lcs[i] = "C";
535 		}
536 		if ((i > 0) && (lcs[i] != lcs[i-1]))
537 			composite++;
538 
539 		len += 1 + strlen(lcs[i]);	/* 1 extra byte for '/' */
540 	}
541 
542 	if (composite == 0) {
543 		/* simple locale */
544 		pd_locale = safe_strdup(lcs[0]);
545 	} else {
546 		/* composite locale */
547 		pd_locale = safe_zalloc(len + 1);
548 		(void) snprintf(pd_locale, len + 1, "/%s/%s/%s/%s/%s/%s",
549 		    lcs[0], lcs[1], lcs[2], lcs[3], lcs[4], lcs[5]);
550 	}
551 	datap->pd_locale = pd_locale;
552 }
553 
554 /*
555  * Pull a string from the victim, regardless of size; this routine allocates
556  * memory for the string which must be freed by the caller.
557  */
558 static char *
559 extract_string(pargs_data_t *datap, uintptr_t addr)
560 {
561 	int size = EXTRACT_BUFSZ;
562 	char *result;
563 
564 	result = safe_zalloc(size);
565 
566 	for (;;) {
567 		if (Pread_string(datap->pd_proc, result, size, addr) < 0) {
568 			free(result);
569 			return (NULL);
570 		} else if (strlen(result) == (size - 1)) {
571 			free(result);
572 			size *= 2;
573 			result = safe_zalloc(size);
574 		} else {
575 			break;
576 		}
577 	}
578 	return (result);
579 }
580 
581 /*
582  * Utility function to read an array of pointers from the victim, adjusting
583  * for victim data model; returns the number of bytes successfully read.
584  */
585 static ssize_t
586 read_ptr_array(pargs_data_t *datap, uintptr_t offset, uintptr_t *buf,
587     size_t nelems)
588 {
589 	ssize_t res;
590 
591 	if (dmodel == PR_MODEL_NATIVE) {
592 		res = Pread(datap->pd_proc, buf, nelems * sizeof (uintptr_t),
593 		    offset);
594 	} else {
595 		int i;
596 		uint32_t *arr32 = safe_zalloc(nelems * sizeof (uint32_t));
597 
598 		res = Pread(datap->pd_proc, arr32, nelems * sizeof (uint32_t),
599 		    offset);
600 		if (res > 0) {
601 			for (i = 0; i < nelems; i++)
602 				buf[i] = arr32[i];
603 		}
604 		free(arr32);
605 	}
606 	return (res);
607 }
608 
609 /*
610  * Extract the argv array from the victim; store the pointer values in
611  * datap->pd_argv and the extracted strings in datap->pd_argv_strs.
612  */
613 static void
614 get_args(pargs_data_t *datap)
615 {
616 	size_t argc = datap->pd_psinfo->pr_argc;
617 	uintptr_t argvoff = datap->pd_psinfo->pr_argv;
618 	int i;
619 
620 	datap->pd_argc = argc;
621 	datap->pd_argv = safe_zalloc(argc * sizeof (uintptr_t));
622 
623 	if (read_ptr_array(datap, argvoff, datap->pd_argv, argc) <= 0) {
624 		free(datap->pd_argv);
625 		datap->pd_argv = NULL;
626 		datap->pd_argc = 0;
627 		return;
628 	}
629 
630 	datap->pd_argv_strs = safe_zalloc(argc * sizeof (char *));
631 	for (i = 0; i < argc; i++) {
632 		if (datap->pd_argv[i] == 0)
633 			continue;
634 		datap->pd_argv_strs[i] = extract_string(datap,
635 		    datap->pd_argv[i]);
636 	}
637 }
638 
639 /*ARGSUSED*/
640 static int
641 build_env(void *data, struct ps_prochandle *pr, uintptr_t addr, const char *str)
642 {
643 	pargs_data_t *datap = data;
644 
645 	if (datap->pd_envp != NULL) {
646 		if (datap->pd_envc == datap->pd_env_space) {
647 			/*
648 			 * Not enough space for storing the env (it has more
649 			 * items than before).  Try to grow both arrays.
650 			 */
651 			void *new = realloc(datap->pd_envp,
652 			    sizeof (uintptr_t) * datap->pd_env_space * 2);
653 			if (new == NULL)
654 				return (1);
655 			datap->pd_envp = new;
656 
657 			new = realloc(datap->pd_envp_strs,
658 			    sizeof (char *) * datap->pd_env_space * 2);
659 			if (new == NULL)
660 				return (1);
661 			datap->pd_envp_strs = new;
662 
663 			datap->pd_env_space *= 2;
664 		}
665 
666 		datap->pd_envp[datap->pd_envc] = addr;
667 		if (str == NULL)
668 			datap->pd_envp_strs[datap->pd_envc] = NULL;
669 		else
670 			datap->pd_envp_strs[datap->pd_envc] = strdup(str);
671 	}
672 
673 	datap->pd_envc++;
674 
675 	return (0);
676 }
677 
678 static void
679 get_env(pargs_data_t *datap)
680 {
681 	struct ps_prochandle *pr = datap->pd_proc;
682 
683 	datap->pd_envc = 0;
684 	(void) Penv_iter(pr, build_env, datap);
685 
686 	/* We must allocate space for at least one entry */
687 	datap->pd_env_space = datap->pd_envc != 0 ? datap->pd_envc : 1;
688 	datap->pd_envp = safe_zalloc(sizeof (uintptr_t) * datap->pd_env_space);
689 	datap->pd_envp_strs =
690 	    safe_zalloc(sizeof (char *) * datap->pd_env_space);
691 
692 	datap->pd_envc = 0;
693 	(void) Penv_iter(pr, build_env, datap);
694 }
695 
696 /*
697  * The following at_* routines are used to decode data from the aux vector.
698  */
699 
700 /*ARGSUSED*/
701 static void
702 at_null(long val, char *instr, size_t n, char *str)
703 {
704 	str[0] = '\0';
705 }
706 
707 /*ARGSUSED*/
708 static void
709 at_str(long val, char *instr, size_t n, char *str)
710 {
711 	str[0] = '\0';
712 	if (instr != NULL) {
713 		(void) strlcpy(str, instr, n);
714 	}
715 }
716 
717 /*
718  * Note: Don't forget to add a corresponding case to isainfo(1).
719  */
720 
721 #define	FMT_AV(s, n, hwcap, mask, name)				\
722 	if ((hwcap) & (mask))					\
723 		(void) snprintf(s, n, "%s" name " | ", s)
724 
725 /*ARGSUSED*/
726 static void
727 at_hwcap(long val, char *instr, size_t n, char *str)
728 {
729 #if defined(__sparc) || defined(__sparcv9)
730 	(void) elfcap_hw1_to_str(ELFCAP_STYLE_UC, val, str, n,
731 	    ELFCAP_FMT_PIPSPACE, EM_SPARC);
732 
733 #elif defined(__i386) || defined(__amd64)
734 	(void) elfcap_hw1_to_str(ELFCAP_STYLE_UC, val, str, n,
735 	    ELFCAP_FMT_PIPSPACE, EM_386);
736 #else
737 #error	"port me"
738 #endif
739 }
740 
741 /*ARGSUSED*/
742 static void
743 at_hwcap2(long val, char *instr, size_t n, char *str)
744 {
745 #if defined(__sparc) || defined(__sparcv9)
746 	(void) elfcap_hw2_to_str(ELFCAP_STYLE_UC, val, str, n,
747 	    ELFCAP_FMT_PIPSPACE, EM_SPARC);
748 
749 #elif defined(__i386) || defined(__amd64)
750 	(void) elfcap_hw2_to_str(ELFCAP_STYLE_UC, val, str, n,
751 	    ELFCAP_FMT_PIPSPACE, EM_386);
752 #else
753 #error	"port me"
754 #endif
755 }
756 
757 
758 /*ARGSUSED*/
759 static void
760 at_uid(long val, char *instr, size_t n, char *str)
761 {
762 	struct passwd *pw = getpwuid((uid_t)val);
763 
764 	if ((pw == NULL) || (pw->pw_name == NULL))
765 		str[0] = '\0';
766 	else
767 		(void) snprintf(str, n, "%lu(%s)", val, pw->pw_name);
768 }
769 
770 
771 /*ARGSUSED*/
772 static void
773 at_gid(long val, char *instr, size_t n, char *str)
774 {
775 	struct group *gr = getgrgid((gid_t)val);
776 
777 	if ((gr == NULL) || (gr->gr_name == NULL))
778 		str[0] = '\0';
779 	else
780 		(void) snprintf(str, n, "%lu(%s)", val, gr->gr_name);
781 }
782 
783 static struct auxfl {
784 	int af_flag;
785 	const char *af_name;
786 } auxfl[] = {
787 	{ AF_SUN_SETUGID,	"setugid" },
788 };
789 
790 /*ARGSUSED*/
791 static void
792 at_flags(long val, char *instr, size_t n, char *str)
793 {
794 	int i;
795 
796 	*str = '\0';
797 
798 	for (i = 0; i < sizeof (auxfl)/sizeof (struct auxfl); i++) {
799 		if ((val & auxfl[i].af_flag) != 0) {
800 			if (*str != '\0')
801 				(void) strlcat(str, ",", n);
802 			(void) strlcat(str, auxfl[i].af_name, n);
803 		}
804 	}
805 }
806 
807 #define	MAX_AT_NAME_LEN	15
808 
809 struct aux_id {
810 	int aux_type;
811 	const char *aux_name;
812 	void (*aux_decode)(long, char *, size_t, char *);
813 };
814 
815 static struct aux_id aux_arr[] = {
816 	{ AT_NULL,		"AT_NULL",		at_null	},
817 	{ AT_IGNORE,		"AT_IGNORE",		at_null	},
818 	{ AT_EXECFD,		"AT_EXECFD",		at_null	},
819 	{ AT_PHDR,		"AT_PHDR",		at_null	},
820 	{ AT_PHENT,		"AT_PHENT",		at_null	},
821 	{ AT_PHNUM,		"AT_PHNUM",		at_null	},
822 	{ AT_PAGESZ,		"AT_PAGESZ",		at_null	},
823 	{ AT_BASE,		"AT_BASE",		at_null	},
824 	{ AT_FLAGS,		"AT_FLAGS",		at_null	},
825 	{ AT_ENTRY,		"AT_ENTRY",		at_null	},
826 	{ AT_SUN_UID,		"AT_SUN_UID",		at_uid	},
827 	{ AT_SUN_RUID,		"AT_SUN_RUID",		at_uid	},
828 	{ AT_SUN_GID,		"AT_SUN_GID",		at_gid	},
829 	{ AT_SUN_RGID,		"AT_SUN_RGID",		at_gid	},
830 	{ AT_SUN_LDELF,		"AT_SUN_LDELF",		at_null	},
831 	{ AT_SUN_LDSHDR,	"AT_SUN_LDSHDR",	at_null	},
832 	{ AT_SUN_LDNAME,	"AT_SUN_LDNAME",	at_null	},
833 	{ AT_SUN_LPAGESZ,	"AT_SUN_LPAGESZ",	at_null	},
834 	{ AT_SUN_PLATFORM,	"AT_SUN_PLATFORM",	at_str	},
835 	{ AT_SUN_EXECNAME,	"AT_SUN_EXECNAME",	at_str	},
836 	{ AT_SUN_HWCAP,		"AT_SUN_HWCAP",		at_hwcap },
837 	{ AT_SUN_HWCAP2,	"AT_SUN_HWCAP2",	at_hwcap2 },
838 	{ AT_SUN_IFLUSH,	"AT_SUN_IFLUSH",	at_null	},
839 	{ AT_SUN_CPU,		"AT_SUN_CPU",		at_null	},
840 	{ AT_SUN_MMU,		"AT_SUN_MMU",		at_null	},
841 	{ AT_SUN_LDDATA,	"AT_SUN_LDDATA",	at_null	},
842 	{ AT_SUN_AUXFLAGS,	"AT_SUN_AUXFLAGS",	at_flags },
843 	{ AT_SUN_EMULATOR,	"AT_SUN_EMULATOR",	at_str	},
844 	{ AT_SUN_BRANDNAME,	"AT_SUN_BRANDNAME",	at_str	},
845 	{ AT_SUN_BRAND_AUX1,	"AT_SUN_BRAND_AUX1",	at_null	},
846 	{ AT_SUN_BRAND_AUX2,	"AT_SUN_BRAND_AUX2",	at_null	},
847 	{ AT_SUN_BRAND_AUX3,	"AT_SUN_BRAND_AUX3",	at_null	},
848 	{ AT_SUN_COMMPAGE,	"AT_SUN_COMMPAGE",	at_null	},
849 	{ AT_SUN_FPTYPE,	"AT_SUN_FPTYPE",	at_null },
850 	{ AT_SUN_FPSIZE,	"AT_SUN_FPSIZE",	at_null }
851 };
852 
853 #define	N_AT_ENTS (sizeof (aux_arr) / sizeof (struct aux_id))
854 
855 /*
856  * Return the aux_id entry for the given aux type; returns NULL if not found.
857  */
858 static struct aux_id *
859 aux_find(int type)
860 {
861 	int i;
862 
863 	for (i = 0; i < N_AT_ENTS; i++) {
864 		if (type == aux_arr[i].aux_type)
865 			return (&aux_arr[i]);
866 	}
867 
868 	return (NULL);
869 }
870 
871 static void
872 get_auxv(pargs_data_t *datap)
873 {
874 	int i;
875 	const auxv_t *auxvp;
876 
877 	/*
878 	 * Fetch the aux vector from the target process.
879 	 */
880 	if (ps_pauxv(datap->pd_proc, &auxvp) != PS_OK)
881 		return;
882 
883 	for (i = 0; auxvp[i].a_type != AT_NULL; i++)
884 		continue;
885 
886 	datap->pd_auxc = i;
887 	datap->pd_auxv = safe_zalloc(i * sizeof (auxv_t));
888 	bcopy(auxvp, datap->pd_auxv, i * sizeof (auxv_t));
889 
890 	datap->pd_auxv_strs = safe_zalloc(datap->pd_auxc * sizeof (char *));
891 	for (i = 0; i < datap->pd_auxc; i++) {
892 		struct aux_id *aux = aux_find(datap->pd_auxv[i].a_type);
893 
894 		/*
895 		 * Grab strings for those entries which have a string-decoder.
896 		 */
897 		if ((aux != NULL) && (aux->aux_decode == at_str)) {
898 			datap->pd_auxv_strs[i] =
899 			    extract_string(datap, datap->pd_auxv[i].a_un.a_val);
900 		}
901 	}
902 }
903 
904 /*
905  * Prepare to convert characters in the victim's character set into user's
906  * character set.
907  */
908 static void
909 setup_conversions(pargs_data_t *datap, int *diflocale)
910 {
911 	char *mylocale = NULL, *mycharset = NULL;
912 	char *targetlocale = NULL, *targetcharset = NULL;
913 
914 	mycharset = safe_strdup(nl_langinfo(CODESET));
915 
916 	mylocale = setlocale(LC_CTYPE, NULL);
917 	if ((mylocale == NULL) || (strcmp(mylocale, "") == 0))
918 		mylocale = "C";
919 	mylocale = safe_strdup(mylocale);
920 
921 	if (datap->pd_conv_flags & CONV_STRICT_ASCII)
922 		goto done;
923 
924 	/*
925 	 * If the target's locale is "C" or "POSIX", go fast.
926 	 */
927 	if ((strcmp(datap->pd_locale, "C") == 0) ||
928 	    (strcmp(datap->pd_locale, "POSIX") == 0)) {
929 		datap->pd_conv_flags |= CONV_STRICT_ASCII;
930 		goto done;
931 	}
932 
933 	/*
934 	 * Switch to the victim's locale, and discover its character set.
935 	 */
936 	if (setlocale(LC_ALL, datap->pd_locale) == NULL) {
937 		(void) fprintf(stderr,
938 		    "%s: Couldn't determine locale of target process.\n",
939 		    command);
940 		(void) fprintf(stderr,
941 		    "%s: Some strings may not be displayed properly.\n",
942 		    command);
943 		goto done;
944 	}
945 
946 	/*
947 	 * Get LC_CTYPE part of target's locale, and its codeset.
948 	 */
949 	targetlocale = safe_strdup(setlocale(LC_CTYPE, NULL));
950 	targetcharset = safe_strdup(nl_langinfo(CODESET));
951 
952 	/*
953 	 * Now go fully back to the pargs user's locale.
954 	 */
955 	(void) setlocale(LC_ALL, "");
956 
957 	/*
958 	 * It's safe to bail here if the lc_ctype of the locales are the
959 	 * same-- we know that their encodings and characters sets are the same.
960 	 */
961 	if (strcmp(targetlocale, mylocale) == 0)
962 		goto done;
963 
964 	*diflocale = 1;
965 
966 	/*
967 	 * If the codeset of the victim matches our codeset then iconv need
968 	 * not be involved.
969 	 */
970 	if (strcmp(mycharset, targetcharset) == 0)
971 		goto done;
972 
973 	if ((datap->pd_iconv = iconv_open(mycharset, targetcharset))
974 	    == (iconv_t)-1) {
975 		/*
976 		 * EINVAL indicates there was no conversion available
977 		 * from victim charset to mycharset
978 		 */
979 		if (errno != EINVAL) {
980 			(void) fprintf(stderr,
981 			    "%s: failed to initialize iconv: %s\n",
982 			    command, strerror(errno));
983 			exit(1);
984 		}
985 		datap->pd_conv_flags |= CONV_STRICT_ASCII;
986 	} else {
987 		datap->pd_conv_flags |= CONV_USE_ICONV;
988 	}
989 done:
990 	free(mycharset);
991 	free(mylocale);
992 	free(targetcharset);
993 	free(targetlocale);
994 }
995 
996 static void
997 cleanup_conversions(pargs_data_t *datap)
998 {
999 	if (datap->pd_conv_flags & CONV_USE_ICONV) {
1000 		(void) iconv_close(datap->pd_iconv);
1001 	}
1002 }
1003 
1004 static char *
1005 convert_run_iconv(pargs_data_t *datap, const char *str)
1006 {
1007 	size_t inleft, outleft, bufsz = 64;
1008 	char *outstr, *outstrptr;
1009 	const char *instrptr;
1010 
1011 	for (;;) {
1012 		outstrptr = outstr = safe_zalloc(bufsz + 1);
1013 		outleft = bufsz;
1014 
1015 		/*
1016 		 * Generate the "initial shift state" sequence, placing that
1017 		 * at the head of the string.
1018 		 */
1019 		inleft = 0;
1020 		(void) iconv(datap->pd_iconv, NULL, &inleft,
1021 		    &outstrptr, &outleft);
1022 
1023 		inleft = strlen(str);
1024 		instrptr = str;
1025 		if (iconv(datap->pd_iconv, &instrptr, &inleft, &outstrptr,
1026 		    &outleft) != (size_t)-1) {
1027 			/*
1028 			 * Outstr must be null terminated upon exit from
1029 			 * iconv().
1030 			 */
1031 			*(outstr + (bufsz - outleft)) = '\0';
1032 			break;
1033 		} else if (errno == E2BIG) {
1034 			bufsz *= 2;
1035 			free(outstr);
1036 		} else if ((errno == EILSEQ) || (errno == EINVAL)) {
1037 			free(outstr);
1038 			return (NULL);
1039 		} else {
1040 			/*
1041 			 * iconv() could in theory return EBADF, but that
1042 			 * shouldn't happen.
1043 			 */
1044 			(void) fprintf(stderr,
1045 			    "%s: iconv(3C) failed unexpectedly: %s\n",
1046 			    command, strerror(errno));
1047 
1048 			exit(1);
1049 		}
1050 	}
1051 	return (outstr);
1052 }
1053 
1054 /*
1055  * Returns a freshly allocated string converted to the local character set,
1056  * removed of unprintable characters.
1057  */
1058 static char *
1059 convert_str(pargs_data_t *datap, const char *str, int *unprintable)
1060 {
1061 	char *retstr, *tmp;
1062 
1063 	if (datap->pd_conv_flags & CONV_STRICT_ASCII) {
1064 		retstr = unctrl_str_strict_ascii(str, 1, unprintable);
1065 		return (retstr);
1066 	}
1067 
1068 	if ((datap->pd_conv_flags & CONV_USE_ICONV) == 0) {
1069 		/*
1070 		 * If we aren't using iconv(), convert control chars in
1071 		 * the string in pargs' locale, since that is the display
1072 		 * locale.
1073 		 */
1074 		retstr = unctrl_str(str, 1, unprintable);
1075 		return (retstr);
1076 	}
1077 
1078 	/*
1079 	 * The logic here is a bit (ahem) tricky.  Start by converting
1080 	 * unprintable characters *in the target's locale*.  This should
1081 	 * eliminate a variety of unprintable or illegal characters-- in
1082 	 * short, it should leave us with something which iconv() won't
1083 	 * have trouble with.
1084 	 *
1085 	 * After allowing iconv to convert characters as needed, run unctrl
1086 	 * again in pargs' locale-- This time to make sure that any
1087 	 * characters which aren't printable according to the *current*
1088 	 * locale (independent of the current codeset) get taken care of.
1089 	 * Without this second stage, we might (for example) fail to
1090 	 * properly handle characters converted into the 646 character set
1091 	 * (which are 8-bits wide), but which must be displayed in the C
1092 	 * locale (which uses 646, but whose printable characters are a
1093 	 * subset of the 7-bit characters).
1094 	 *
1095 	 * Note that assuming the victim's locale using LC_ALL will be
1096 	 * problematic when pargs' messages are internationalized in the
1097 	 * future (and it calls textdomain(3C)).  In this case, any
1098 	 * error message fprintf'd in unctrl_str() will be in the wrong
1099 	 * LC_MESSAGES class.  We'll cross that bridge when we come to it.
1100 	 */
1101 	(void) setlocale(LC_ALL, datap->pd_locale);
1102 	retstr = unctrl_str(str, 1, unprintable);
1103 	(void) setlocale(LC_ALL, "");
1104 
1105 	tmp = retstr;
1106 	if ((retstr = convert_run_iconv(datap, retstr)) == NULL) {
1107 		/*
1108 		 * In this (rare but real) case, the iconv() failed even
1109 		 * though we unctrl'd the string.  Treat the original string
1110 		 * (str) as a C locale string and strip it that way.
1111 		 */
1112 		free(tmp);
1113 		return (unctrl_str_strict_ascii(str, 0, unprintable));
1114 	}
1115 
1116 	free(tmp);
1117 	tmp = retstr;
1118 	/*
1119 	 * Run unctrl_str, but make sure not to escape \ characters, which
1120 	 * may have resulted from the first round of unctrl.
1121 	 */
1122 	retstr = unctrl_str(retstr, 0, unprintable);
1123 	free(tmp);
1124 	return (retstr);
1125 }
1126 
1127 
1128 static void
1129 convert_array(pargs_data_t *datap, char **arr, size_t count, int *unprintable)
1130 {
1131 	int i;
1132 	char *tmp;
1133 
1134 	if (arr == NULL)
1135 		return;
1136 
1137 	for (i = 0; i < count; i++) {
1138 		if ((tmp = arr[i]) == NULL)
1139 			continue;
1140 		arr[i] = convert_str(datap, arr[i], unprintable);
1141 		free(tmp);
1142 	}
1143 }
1144 
1145 /*
1146  * Free data allocated during the gathering phase.
1147  */
1148 static void
1149 free_data(pargs_data_t *datap)
1150 {
1151 	int i;
1152 
1153 	for (i = 0; i < datap->pd_argc; i++)
1154 		free(datap->pd_argv_strs[i]);
1155 	free(datap->pd_argv);
1156 	free(datap->pd_argv_strs);
1157 
1158 	for (i = 0; i < datap->pd_envc; i++)
1159 		free(datap->pd_envp_strs[i]);
1160 	free(datap->pd_envp);
1161 	free(datap->pd_envp_strs);
1162 
1163 	for (i = 0; i < datap->pd_auxc; i++)
1164 		free(datap->pd_auxv_strs[i]);
1165 	free(datap->pd_auxv);
1166 	free(datap->pd_auxv_strs);
1167 }
1168 
1169 static void
1170 print_args(pargs_data_t *datap)
1171 {
1172 	int i;
1173 
1174 	if (datap->pd_argv == NULL) {
1175 		(void) fprintf(stderr, "%s: failed to read argv[]\n", command);
1176 		return;
1177 	}
1178 
1179 	for (i = 0; i < datap->pd_argc; i++) {
1180 		(void) printf("argv[%d]: ", i);
1181 		if (datap->pd_argv[i] == (uintptr_t)NULL) {
1182 			(void) printf("<NULL>\n");
1183 		} else if (datap->pd_argv_strs[i] == NULL) {
1184 			(void) printf("<0x%0*lx>\n",
1185 			    (dmodel == PR_MODEL_LP64)? 16 : 8,
1186 			    (long)datap->pd_argv[i]);
1187 		} else {
1188 			(void) printf("%s\n", datap->pd_argv_strs[i]);
1189 		}
1190 	}
1191 }
1192 
1193 static void
1194 print_env(pargs_data_t *datap)
1195 {
1196 	int i;
1197 
1198 	if (datap->pd_envp == NULL) {
1199 		(void) fprintf(stderr, "%s: failed to read envp[]\n", command);
1200 		return;
1201 	}
1202 
1203 	for (i = 0; i < datap->pd_envc; i++) {
1204 		(void) printf("envp[%d]: ", i);
1205 		if (datap->pd_envp[i] == 0) {
1206 			break;
1207 		} else if (datap->pd_envp_strs[i] == NULL) {
1208 			(void) printf("<0x%0*lx>\n",
1209 			    (dmodel == PR_MODEL_LP64)? 16 : 8,
1210 			    (long)datap->pd_envp[i]);
1211 		} else {
1212 			(void) printf("%s\n", datap->pd_envp_strs[i]);
1213 		}
1214 	}
1215 }
1216 
1217 static int
1218 print_cmdline(pargs_data_t *datap)
1219 {
1220 	int i;
1221 
1222 	/*
1223 	 * Go through and check to see if we have valid data.  If not, print
1224 	 * an error message and bail.
1225 	 */
1226 	for (i = 0; i < datap->pd_argc; i++) {
1227 		if (datap->pd_argv == NULL ||
1228 		    datap->pd_argv[i] == (uintptr_t)NULL ||
1229 		    datap->pd_argv_strs[i] == NULL) {
1230 			(void) fprintf(stderr, "%s: target has corrupted "
1231 			    "argument list\n", command);
1232 			return (1);
1233 		}
1234 
1235 		datap->pd_argv_strs[i] =
1236 		    quote_string(datap, datap->pd_argv_strs[i]);
1237 	}
1238 
1239 	if (datap->pd_execname == NULL) {
1240 		(void) fprintf(stderr, "%s: cannot determine name of "
1241 		    "executable\n", command);
1242 		return (1);
1243 	}
1244 
1245 	(void) printf("%s ", datap->pd_execname);
1246 
1247 	for (i = 1; i < datap->pd_argc; i++)
1248 		(void) printf("%s ", datap->pd_argv_strs[i]);
1249 
1250 	(void) printf("\n");
1251 
1252 	return (0);
1253 }
1254 
1255 static void
1256 print_auxv(pargs_data_t *datap)
1257 {
1258 	int i;
1259 	const auxv_t *pa;
1260 
1261 	/*
1262 	 * Print the names and values of all the aux vector entries.
1263 	 */
1264 	for (i = 0; i < datap->pd_auxc; i++) {
1265 		char type[32];
1266 		char decode[PATH_MAX];
1267 		struct aux_id *aux;
1268 		long v;
1269 		pa = &datap->pd_auxv[i];
1270 
1271 		aux = aux_find(pa->a_type);
1272 		v = (long)pa->a_un.a_val;
1273 
1274 		if (aux != NULL) {
1275 			/*
1276 			 * Fetch aux vector type string and decoded
1277 			 * representation of the value.
1278 			 */
1279 			(void) strlcpy(type, aux->aux_name, sizeof (type));
1280 			aux->aux_decode(v, datap->pd_auxv_strs[i],
1281 			    sizeof (decode), decode);
1282 		} else {
1283 			(void) snprintf(type, sizeof (type), "%d", pa->a_type);
1284 			decode[0] = '\0';
1285 		}
1286 
1287 		(void) printf("%-*s 0x%0*lx %s\n", MAX_AT_NAME_LEN, type,
1288 		    (dmodel == PR_MODEL_LP64)? 16 : 8, v, decode);
1289 	}
1290 }
1291 
1292 int
1293 main(int argc, char *argv[])
1294 {
1295 	int aflag = 0, cflag = 0, eflag = 0, xflag = 0, lflag = 0;
1296 	int errflg = 0, retc = 0;
1297 	int opt;
1298 	int error = 1;
1299 	core_content_t content = 0;
1300 	pargs_cmd_t cmd = PARGS_ARGV;
1301 
1302 	(void) setlocale(LC_ALL, "");
1303 
1304 	command = basename(argv[0]);
1305 
1306 	if (strcmp(command, "penv") == 0)
1307 		cmd = PARGS_ENV;
1308 	else if (strcmp(command, "pauxv") == 0)
1309 		cmd = PARGS_AUXV;
1310 
1311 	while ((opt = getopt(argc, argv, "acelxF")) != EOF) {
1312 		switch (opt) {
1313 		case 'a':		/* show process arguments */
1314 			content |= CC_CONTENT_STACK;
1315 			aflag++;
1316 			if (cmd != PARGS_ARGV)
1317 				errflg++;
1318 			break;
1319 		case 'c':		/* force 7-bit ascii */
1320 			cflag++;
1321 			break;
1322 		case 'e':		/* show environment variables */
1323 			content |= CC_CONTENT_STACK;
1324 			eflag++;
1325 			if (cmd != PARGS_ARGV)
1326 				errflg++;
1327 			break;
1328 		case 'l':
1329 			lflag++;
1330 			aflag++;	/* -l implies -a */
1331 			if (cmd != PARGS_ARGV)
1332 				errflg++;
1333 			break;
1334 		case 'x':		/* show aux vector entries */
1335 			xflag++;
1336 			if (cmd != PARGS_ARGV)
1337 				errflg++;
1338 			break;
1339 		case 'F':
1340 			/*
1341 			 * Since we open the process read-only, there is no need
1342 			 * for the -F flag.  It's a documented flag, so we
1343 			 * consume it silently.
1344 			 */
1345 			break;
1346 		default:
1347 			errflg++;
1348 			break;
1349 		}
1350 	}
1351 
1352 	/* -a is the default if no options are specified */
1353 	if ((aflag + eflag + xflag + lflag) == 0) {
1354 		switch (cmd) {
1355 		case PARGS_ARGV:
1356 			aflag++;
1357 			content |= CC_CONTENT_STACK;
1358 			break;
1359 		case PARGS_ENV:
1360 			content |= CC_CONTENT_STACK;
1361 			eflag++;
1362 			break;
1363 		case PARGS_AUXV:
1364 			xflag++;
1365 			break;
1366 		}
1367 	}
1368 
1369 	/* -l cannot be used with the -x or -e flags */
1370 	if (lflag && (xflag || eflag)) {
1371 		(void) fprintf(stderr, "-l is incompatible with -x and -e\n");
1372 		errflg++;
1373 	}
1374 
1375 	argc -= optind;
1376 	argv += optind;
1377 
1378 	if (errflg || argc <= 0) {
1379 		(void) fprintf(stderr,
1380 		    "usage:  %s [-aceFlx] { pid | core } ...\n"
1381 		    "  (show process arguments and environment)\n"
1382 		    "  -a: show process arguments (default)\n"
1383 		    "  -c: interpret characters as 7-bit ascii regardless of "
1384 		    "locale\n"
1385 		    "  -e: show environment variables\n"
1386 		    "  -F: force grabbing of the target process\n"
1387 		    "  -l: display arguments as command line\n"
1388 		    "  -x: show aux vector entries\n", command);
1389 		return (2);
1390 	}
1391 
1392 	while (argc-- > 0) {
1393 		char *arg;
1394 		int gret, r;
1395 		psinfo_t psinfo;
1396 		char *psargs_conv;
1397 		struct ps_prochandle *Pr;
1398 		pargs_data_t datap;
1399 		char *info;
1400 		size_t info_sz;
1401 		int pstate;
1402 		char execname[PATH_MAX];
1403 		int unprintable;
1404 		int diflocale;
1405 
1406 		(void) fflush(stdout);
1407 		arg = *argv++;
1408 
1409 		/*
1410 		 * Suppress extra blanks lines if we've encountered processes
1411 		 * which can't be opened.
1412 		 */
1413 		if (error == 0) {
1414 			(void) printf("\n");
1415 		}
1416 		error = 0;
1417 
1418 		/*
1419 		 * First grab just the psinfo information, in case this
1420 		 * process is a zombie (in which case proc_arg_grab() will
1421 		 * fail).  If so, print a nice message and continue.
1422 		 */
1423 		if (proc_arg_psinfo(arg, PR_ARG_ANY, &psinfo,
1424 		    &gret) == -1) {
1425 			(void) fprintf(stderr, "%s: cannot examine %s: %s\n",
1426 			    command, arg, Pgrab_error(gret));
1427 			retc++;
1428 			error = 1;
1429 			continue;
1430 		}
1431 
1432 		if (psinfo.pr_nlwp == 0) {
1433 			(void) printf("%d: <defunct>\n", (int)psinfo.pr_pid);
1434 			continue;
1435 		}
1436 
1437 		/*
1438 		 * If process is a "system" process (like pageout), just
1439 		 * print its psargs and continue on.
1440 		 */
1441 		if (psinfo.pr_size == 0 && psinfo.pr_rssize == 0) {
1442 			proc_unctrl_psinfo(&psinfo);
1443 			if (!lflag)
1444 				(void) printf("%d: ", (int)psinfo.pr_pid);
1445 			(void) printf("%s\n", psinfo.pr_psargs);
1446 			continue;
1447 		}
1448 
1449 		/*
1450 		 * Open the process readonly, since we do not need to write to
1451 		 * the control file.
1452 		 */
1453 		if ((Pr = proc_arg_grab(arg, PR_ARG_ANY, PGRAB_RDONLY,
1454 		    &gret)) == NULL) {
1455 			(void) fprintf(stderr, "%s: cannot examine %s: %s\n",
1456 			    command, arg, Pgrab_error(gret));
1457 			retc++;
1458 			error = 1;
1459 			continue;
1460 		}
1461 
1462 		pstate = Pstate(Pr);
1463 
1464 		if (pstate == PS_DEAD &&
1465 		    (Pcontent(Pr) & content) != content) {
1466 			(void) fprintf(stderr, "%s: core '%s' has "
1467 			    "insufficient content\n", command, arg);
1468 			retc++;
1469 			continue;
1470 		}
1471 
1472 		/*
1473 		 * If malloc() fails, we return here so that we can let go
1474 		 * of the victim, restore our locale, print a message,
1475 		 * then exit.
1476 		 */
1477 		if ((r = setjmp(env)) != 0) {
1478 			Prelease(Pr, 0);
1479 			(void) setlocale(LC_ALL, "");
1480 			(void) fprintf(stderr, "%s: out of memory: %s\n",
1481 			    command, strerror(r));
1482 			return (1);
1483 		}
1484 
1485 		dmodel = Pstatus(Pr)->pr_dmodel;
1486 		bzero(&datap, sizeof (datap));
1487 		bcopy(Ppsinfo(Pr), &psinfo, sizeof (psinfo_t));
1488 		datap.pd_proc = Pr;
1489 		datap.pd_psinfo = &psinfo;
1490 
1491 		if (cflag)
1492 			datap.pd_conv_flags |= CONV_STRICT_ASCII;
1493 
1494 		/*
1495 		 * Strip control characters, then record process summary in
1496 		 * a buffer, since we don't want to print anything out until
1497 		 * after we release the process.
1498 		 */
1499 
1500 		/*
1501 		 * The process is neither a system process nor defunct.
1502 		 *
1503 		 * Do printing and post-processing (like name lookups) after
1504 		 * gathering the raw data from the process and releasing it.
1505 		 * This way, we don't deadlock on (for example) name lookup
1506 		 * if we grabbed the nscd and do 'pargs -x'.
1507 		 *
1508 		 * We always fetch the environment of the target, so that we
1509 		 * can make an educated guess about its locale.
1510 		 */
1511 		get_env(&datap);
1512 		if (aflag != 0)
1513 			get_args(&datap);
1514 		if (xflag != 0)
1515 			get_auxv(&datap);
1516 
1517 		/*
1518 		 * If malloc() fails after this poiint, we return here to
1519 		 * restore our locale and print a message.  If we don't
1520 		 * reset this, we might erroneously try to Prelease a process
1521 		 * twice.
1522 		 */
1523 		if ((r = setjmp(env)) != 0) {
1524 			(void) setlocale(LC_ALL, "");
1525 			(void) fprintf(stderr, "%s: out of memory: %s\n",
1526 			    command, strerror(r));
1527 			return (1);
1528 		}
1529 
1530 		/*
1531 		 * For the -l option, we need a proper name for this executable
1532 		 * before we release it.
1533 		 */
1534 		if (lflag)
1535 			datap.pd_execname = Pexecname(Pr, execname,
1536 			    sizeof (execname));
1537 
1538 		Prelease(Pr, 0);
1539 
1540 		/*
1541 		 * Crawl through the environment to determine the locale of
1542 		 * the target.
1543 		 */
1544 		lookup_locale(&datap);
1545 		diflocale = 0;
1546 		setup_conversions(&datap, &diflocale);
1547 
1548 		if (lflag != 0) {
1549 			unprintable = 0;
1550 			convert_array(&datap, datap.pd_argv_strs,
1551 			    datap.pd_argc, &unprintable);
1552 			if (diflocale)
1553 				(void) fprintf(stderr, "%s: Warning, target "
1554 				    "locale differs from current locale\n",
1555 				    command);
1556 			else if (unprintable)
1557 				(void) fprintf(stderr, "%s: Warning, command "
1558 				    "line contains unprintable characters\n",
1559 				    command);
1560 
1561 			retc += print_cmdline(&datap);
1562 		} else {
1563 			psargs_conv = convert_str(&datap, psinfo.pr_psargs,
1564 			    &unprintable);
1565 			info_sz = strlen(psargs_conv) + MAXPATHLEN + 32 + 1;
1566 			info = malloc(info_sz);
1567 			if (pstate == PS_DEAD) {
1568 				(void) snprintf(info, info_sz,
1569 				    "core '%s' of %d:\t%s\n",
1570 				    arg, (int)psinfo.pr_pid, psargs_conv);
1571 			} else {
1572 				(void) snprintf(info, info_sz, "%d:\t%s\n",
1573 				    (int)psinfo.pr_pid, psargs_conv);
1574 			}
1575 			(void) printf("%s", info);
1576 			free(info);
1577 			free(psargs_conv);
1578 
1579 			if (aflag != 0) {
1580 				convert_array(&datap, datap.pd_argv_strs,
1581 				    datap.pd_argc, &unprintable);
1582 				print_args(&datap);
1583 				if (eflag || xflag)
1584 					(void) printf("\n");
1585 			}
1586 
1587 			if (eflag != 0) {
1588 				convert_array(&datap, datap.pd_envp_strs,
1589 				    datap.pd_envc, &unprintable);
1590 				print_env(&datap);
1591 				if (xflag)
1592 					(void) printf("\n");
1593 			}
1594 
1595 			if (xflag != 0) {
1596 				convert_array(&datap, datap.pd_auxv_strs,
1597 				    datap.pd_auxc, &unprintable);
1598 				print_auxv(&datap);
1599 			}
1600 		}
1601 
1602 		cleanup_conversions(&datap);
1603 		free_data(&datap);
1604 	}
1605 
1606 	return (retc != 0 ? 1 : 0);
1607 }
1608