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