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