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