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