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