xref: /onnv-gate/usr/src/cmd/ptools/pargs/pargs.c (revision 5565:538e7adac11a)
10Sstevel@tonic-gate /*
20Sstevel@tonic-gate  * CDDL HEADER START
30Sstevel@tonic-gate  *
40Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
52712Snn35248  * Common Development and Distribution License (the "License").
62712Snn35248  * You may not use this file except in compliance with the License.
70Sstevel@tonic-gate  *
80Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
90Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
100Sstevel@tonic-gate  * See the License for the specific language governing permissions
110Sstevel@tonic-gate  * and limitations under the License.
120Sstevel@tonic-gate  *
130Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
140Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
150Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
160Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
170Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
180Sstevel@tonic-gate  *
190Sstevel@tonic-gate  * CDDL HEADER END
200Sstevel@tonic-gate  */
210Sstevel@tonic-gate /*
224642Ssl108498  * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
230Sstevel@tonic-gate  * Use is subject to license terms.
240Sstevel@tonic-gate  */
250Sstevel@tonic-gate 
260Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
270Sstevel@tonic-gate 
280Sstevel@tonic-gate /*
290Sstevel@tonic-gate  * pargs examines and prints the arguments (argv), environment (environ),
300Sstevel@tonic-gate  * and auxiliary vector of another process.
310Sstevel@tonic-gate  *
320Sstevel@tonic-gate  * This utility is made more complex because it must run in internationalized
330Sstevel@tonic-gate  * environments.  The two key cases for pargs to manage are:
340Sstevel@tonic-gate  *
350Sstevel@tonic-gate  * 1. pargs and target run in the same locale: pargs must respect the
360Sstevel@tonic-gate  * locale, but this case is straightforward.  Care is taken to correctly
370Sstevel@tonic-gate  * use wide characters in order to print results properly.
380Sstevel@tonic-gate  *
390Sstevel@tonic-gate  * 2. pargs and target run in different locales: in this case, pargs examines
400Sstevel@tonic-gate  * the string having assumed the victim's locale.  Unprintable (but valid)
410Sstevel@tonic-gate  * characters are escaped.  Next, iconv(3c) is used to convert between the
420Sstevel@tonic-gate  * target and pargs codeset.  Finally, a second pass to escape unprintable
430Sstevel@tonic-gate  * (but valid) characters is made.
440Sstevel@tonic-gate  *
450Sstevel@tonic-gate  * In any case in which characters are encountered which are not valid in
460Sstevel@tonic-gate  * their purported locale, the string "fails" and is treated as a traditional
470Sstevel@tonic-gate  * 7-bit ASCII encoded string, and escaped accordingly.
480Sstevel@tonic-gate  */
490Sstevel@tonic-gate 
500Sstevel@tonic-gate #include <stdio.h>
510Sstevel@tonic-gate #include <stdlib.h>
520Sstevel@tonic-gate #include <locale.h>
530Sstevel@tonic-gate #include <wchar.h>
540Sstevel@tonic-gate #include <iconv.h>
550Sstevel@tonic-gate #include <langinfo.h>
560Sstevel@tonic-gate #include <unistd.h>
570Sstevel@tonic-gate #include <ctype.h>
580Sstevel@tonic-gate #include <fcntl.h>
590Sstevel@tonic-gate #include <string.h>
600Sstevel@tonic-gate #include <strings.h>
610Sstevel@tonic-gate #include <limits.h>
620Sstevel@tonic-gate #include <pwd.h>
630Sstevel@tonic-gate #include <grp.h>
640Sstevel@tonic-gate #include <errno.h>
650Sstevel@tonic-gate #include <setjmp.h>
660Sstevel@tonic-gate #include <sys/types.h>
670Sstevel@tonic-gate #include <sys/auxv.h>
680Sstevel@tonic-gate #include <sys/archsystm.h>
690Sstevel@tonic-gate #include <sys/proc.h>
700Sstevel@tonic-gate #include <sys/elf.h>
710Sstevel@tonic-gate #include <libproc.h>
720Sstevel@tonic-gate #include <wctype.h>
730Sstevel@tonic-gate #include <widec.h>
740Sstevel@tonic-gate #include <elfcap.h>
750Sstevel@tonic-gate 
760Sstevel@tonic-gate typedef struct pargs_data {
770Sstevel@tonic-gate 	struct ps_prochandle *pd_proc;	/* target proc handle */
780Sstevel@tonic-gate 	psinfo_t *pd_psinfo;		/* target psinfo */
790Sstevel@tonic-gate 	char *pd_locale;		/* target process locale */
800Sstevel@tonic-gate 	int pd_conv_flags;		/* flags governing string conversion */
810Sstevel@tonic-gate 	iconv_t pd_iconv;		/* iconv conversion descriptor */
820Sstevel@tonic-gate 	size_t pd_argc;
830Sstevel@tonic-gate 	uintptr_t *pd_argv;
840Sstevel@tonic-gate 	char **pd_argv_strs;
850Sstevel@tonic-gate 	size_t pd_envc;
860Sstevel@tonic-gate 	uintptr_t *pd_envp;
870Sstevel@tonic-gate 	char **pd_envp_strs;
880Sstevel@tonic-gate 	size_t pd_auxc;
890Sstevel@tonic-gate 	auxv_t *pd_auxv;
900Sstevel@tonic-gate 	char **pd_auxv_strs;
910Sstevel@tonic-gate 	char *pd_execname;
920Sstevel@tonic-gate } pargs_data_t;
930Sstevel@tonic-gate 
940Sstevel@tonic-gate #define	CONV_USE_ICONV		0x01
950Sstevel@tonic-gate #define	CONV_STRICT_ASCII	0x02
960Sstevel@tonic-gate 
970Sstevel@tonic-gate static char *command;
980Sstevel@tonic-gate static int dmodel;
990Sstevel@tonic-gate 
1000Sstevel@tonic-gate #define	EXTRACT_BUFSZ 128		/* extract_string() initial size */
1010Sstevel@tonic-gate #define	ENV_CHUNK 16			/* #env ptrs to read at a time */
1020Sstevel@tonic-gate 
1030Sstevel@tonic-gate static jmp_buf env;			/* malloc failure handling */
1040Sstevel@tonic-gate 
1050Sstevel@tonic-gate static void *
safe_zalloc(size_t size)1060Sstevel@tonic-gate safe_zalloc(size_t size)
1070Sstevel@tonic-gate {
1080Sstevel@tonic-gate 	void *p;
1090Sstevel@tonic-gate 
1100Sstevel@tonic-gate 	/*
1110Sstevel@tonic-gate 	 * If the malloc fails we longjmp out to allow the code to Prelease()
1120Sstevel@tonic-gate 	 * a stopped victim if needed.
1130Sstevel@tonic-gate 	 */
1140Sstevel@tonic-gate 	if ((p = malloc(size)) == NULL) {
1150Sstevel@tonic-gate 		longjmp(env, errno);
1160Sstevel@tonic-gate 	}
1170Sstevel@tonic-gate 
1180Sstevel@tonic-gate 	bzero(p, size);
1190Sstevel@tonic-gate 	return (p);
1200Sstevel@tonic-gate }
1210Sstevel@tonic-gate 
1220Sstevel@tonic-gate static char *
safe_strdup(const char * s1)1230Sstevel@tonic-gate safe_strdup(const char *s1)
1240Sstevel@tonic-gate {
1250Sstevel@tonic-gate 	char	*s2;
1260Sstevel@tonic-gate 
1270Sstevel@tonic-gate 	s2 = safe_zalloc(strlen(s1) + 1);
1280Sstevel@tonic-gate 	(void) strcpy(s2, s1);
1290Sstevel@tonic-gate 	return (s2);
1300Sstevel@tonic-gate }
1310Sstevel@tonic-gate 
1320Sstevel@tonic-gate /*
1330Sstevel@tonic-gate  * Given a wchar_t which might represent an 'escapable' sequence (see
1340Sstevel@tonic-gate  * formats(5)), return the base ascii character needed to print that
1350Sstevel@tonic-gate  * sequence.
1360Sstevel@tonic-gate  *
1370Sstevel@tonic-gate  * The comparisons performed may look suspect at first, but all are valid;
1380Sstevel@tonic-gate  * the characters below all appear in the "Portable Character Set."  The
1390Sstevel@tonic-gate  * Single Unix Spec says: "The wide-character value for each member of the
1400Sstevel@tonic-gate  * Portable Character Set will equal its value when used as the lone
1410Sstevel@tonic-gate  * character in an integer character constant."
1420Sstevel@tonic-gate  */
1430Sstevel@tonic-gate static uchar_t
get_interp_char(wchar_t wc)1440Sstevel@tonic-gate get_interp_char(wchar_t wc)
1450Sstevel@tonic-gate {
1460Sstevel@tonic-gate 	switch (wc) {
1470Sstevel@tonic-gate 	case L'\a':
1480Sstevel@tonic-gate 		return ('a');
1490Sstevel@tonic-gate 	case L'\b':
1500Sstevel@tonic-gate 		return ('b');
1510Sstevel@tonic-gate 	case L'\f':
1520Sstevel@tonic-gate 		return ('f');
1530Sstevel@tonic-gate 	case L'\n':
1540Sstevel@tonic-gate 		return ('n');
1550Sstevel@tonic-gate 	case L'\r':
1560Sstevel@tonic-gate 		return ('r');
1570Sstevel@tonic-gate 	case L'\t':
1580Sstevel@tonic-gate 		return ('t');
1590Sstevel@tonic-gate 	case L'\v':
1600Sstevel@tonic-gate 		return ('v');
1610Sstevel@tonic-gate 	case L'\\':
1620Sstevel@tonic-gate 		return ('\\');
1630Sstevel@tonic-gate 	}
1640Sstevel@tonic-gate 	return ('\0');
1650Sstevel@tonic-gate }
1660Sstevel@tonic-gate 
1670Sstevel@tonic-gate static char *
unctrl_str_strict_ascii(const char * src,int escape_slash,int * unprintable)1680Sstevel@tonic-gate unctrl_str_strict_ascii(const char *src, int escape_slash, int *unprintable)
1690Sstevel@tonic-gate {
1700Sstevel@tonic-gate 	uchar_t *uc, *ucp, c, ic;
1710Sstevel@tonic-gate 	uc = ucp = safe_zalloc((strlen(src) * 4) + 1);
1720Sstevel@tonic-gate 	while ((c = *src++) != '\0') {
1730Sstevel@tonic-gate 		/*
1740Sstevel@tonic-gate 		 * Call get_interp_char *first*, since \ will otherwise not
1750Sstevel@tonic-gate 		 * be escaped as \\.
1760Sstevel@tonic-gate 		 */
1770Sstevel@tonic-gate 		if ((ic = get_interp_char((wchar_t)c)) != '\0') {
1780Sstevel@tonic-gate 			if (escape_slash || ic != '\\')
1790Sstevel@tonic-gate 				*ucp++ = '\\';
1800Sstevel@tonic-gate 			*ucp++ = ic;
1810Sstevel@tonic-gate 		} else if (isascii(c) && isprint(c)) {
1820Sstevel@tonic-gate 			*ucp++ = c;
1830Sstevel@tonic-gate 		} else {
1840Sstevel@tonic-gate 			*ucp++ = '\\';
1850Sstevel@tonic-gate 			*ucp++ = ((c >> 6) & 7) + '0';
1860Sstevel@tonic-gate 			*ucp++ = ((c >> 3) & 7) + '0';
1870Sstevel@tonic-gate 			*ucp++ = (c & 7) + '0';
1880Sstevel@tonic-gate 			*unprintable = 1;
1890Sstevel@tonic-gate 		}
1900Sstevel@tonic-gate 	}
1910Sstevel@tonic-gate 	*ucp = '\0';
1920Sstevel@tonic-gate 	return ((char *)uc);
1930Sstevel@tonic-gate }
1940Sstevel@tonic-gate 
1950Sstevel@tonic-gate /*
1960Sstevel@tonic-gate  * Convert control characters as described in format(5) to their readable
1970Sstevel@tonic-gate  * representation; special care is taken to handle multibyte character sets.
1980Sstevel@tonic-gate  *
1990Sstevel@tonic-gate  * If escape_slash is true, escaping of '\' occurs.  The first time a string
2000Sstevel@tonic-gate  * is unctrl'd, this should be '1'.  Subsequent iterations over the same
2010Sstevel@tonic-gate  * string should set escape_slash to 0.  Otherwise you'll wind up with
2020Sstevel@tonic-gate  * \ --> \\ --> \\\\.
2030Sstevel@tonic-gate  */
2040Sstevel@tonic-gate static char *
unctrl_str(const char * src,int escape_slash,int * unprintable)2050Sstevel@tonic-gate unctrl_str(const char *src, int escape_slash, int *unprintable)
2060Sstevel@tonic-gate {
2070Sstevel@tonic-gate 	wchar_t wc;
2080Sstevel@tonic-gate 	wchar_t *wide_src, *wide_srcp;
2090Sstevel@tonic-gate 	wchar_t *wide_dest, *wide_destp;
2100Sstevel@tonic-gate 	char *uc;
2110Sstevel@tonic-gate 	size_t srcbufsz = strlen(src) + 1;
2120Sstevel@tonic-gate 	size_t destbufsz = srcbufsz * 4;
2130Sstevel@tonic-gate 	size_t srclen, destlen;
2140Sstevel@tonic-gate 
2150Sstevel@tonic-gate 	wide_srcp = wide_src = safe_zalloc(srcbufsz * sizeof (wchar_t));
2160Sstevel@tonic-gate 	wide_destp = wide_dest = safe_zalloc(destbufsz * sizeof (wchar_t));
2170Sstevel@tonic-gate 
2180Sstevel@tonic-gate 	if ((srclen = mbstowcs(wide_src, src, srcbufsz - 1)) == (size_t)-1) {
2190Sstevel@tonic-gate 		/*
2200Sstevel@tonic-gate 		 * We can't trust the string, since in the locale in which
2210Sstevel@tonic-gate 		 * this call is operating, the string contains an invalid
2220Sstevel@tonic-gate 		 * multibyte sequence.  There isn't much to do here, so
2230Sstevel@tonic-gate 		 * convert the string byte by byte to wide characters, as
2240Sstevel@tonic-gate 		 * if it came from a C locale (char) string.  This isn't
2250Sstevel@tonic-gate 		 * perfect, but at least the characters will make it to
2260Sstevel@tonic-gate 		 * the screen.
2270Sstevel@tonic-gate 		 */
2280Sstevel@tonic-gate 		free(wide_src);
2290Sstevel@tonic-gate 		free(wide_dest);
2300Sstevel@tonic-gate 		return (unctrl_str_strict_ascii(src, escape_slash,
2310Sstevel@tonic-gate 		    unprintable));
2320Sstevel@tonic-gate 	}
2330Sstevel@tonic-gate 	if (srclen == (srcbufsz - 1)) {
2340Sstevel@tonic-gate 		wide_src[srclen] = L'\0';
2350Sstevel@tonic-gate 	}
2360Sstevel@tonic-gate 
2370Sstevel@tonic-gate 	while ((wc = *wide_srcp++) != L'\0') {
2380Sstevel@tonic-gate 		char cvt_buf[MB_LEN_MAX];
2390Sstevel@tonic-gate 		int len, i;
2400Sstevel@tonic-gate 		char c = get_interp_char(wc);
2410Sstevel@tonic-gate 
2420Sstevel@tonic-gate 		if ((c != '\0') && (escape_slash || c != '\\')) {
2430Sstevel@tonic-gate 			/*
2440Sstevel@tonic-gate 			 * Print "interpreted version" (\n, \a, etc).
2450Sstevel@tonic-gate 			 */
2460Sstevel@tonic-gate 			*wide_destp++ = L'\\';
2470Sstevel@tonic-gate 			*wide_destp++ = (wchar_t)c;
2480Sstevel@tonic-gate 			continue;
2490Sstevel@tonic-gate 		}
2500Sstevel@tonic-gate 
2510Sstevel@tonic-gate 		if (iswprint(wc)) {
2520Sstevel@tonic-gate 			*wide_destp++ = wc;
2530Sstevel@tonic-gate 			continue;
2540Sstevel@tonic-gate 		}
2550Sstevel@tonic-gate 
2560Sstevel@tonic-gate 		/*
2570Sstevel@tonic-gate 		 * Convert the wide char back into (potentially several)
2580Sstevel@tonic-gate 		 * multibyte characters, then escape out each of those bytes.
2590Sstevel@tonic-gate 		 */
2600Sstevel@tonic-gate 		bzero(cvt_buf, sizeof (cvt_buf));
2610Sstevel@tonic-gate 		if ((len = wctomb(cvt_buf, wc)) == -1) {
2620Sstevel@tonic-gate 			/*
2630Sstevel@tonic-gate 			 * This is a totally invalid wide char; discard it.
2640Sstevel@tonic-gate 			 */
2650Sstevel@tonic-gate 			continue;
2660Sstevel@tonic-gate 		}
2670Sstevel@tonic-gate 		for (i = 0; i < len; i++) {
2680Sstevel@tonic-gate 			uchar_t c = cvt_buf[i];
2690Sstevel@tonic-gate 			*wide_destp++ = L'\\';
2700Sstevel@tonic-gate 			*wide_destp++ = (wchar_t)('0' + ((c >> 6) & 7));
2710Sstevel@tonic-gate 			*wide_destp++ = (wchar_t)('0' + ((c >> 3) & 7));
2720Sstevel@tonic-gate 			*wide_destp++ = (wchar_t)('0' + (c & 7));
2730Sstevel@tonic-gate 			*unprintable = 1;
2740Sstevel@tonic-gate 		}
2750Sstevel@tonic-gate 	}
2760Sstevel@tonic-gate 
2770Sstevel@tonic-gate 	*wide_destp = '\0';
2780Sstevel@tonic-gate 	destlen = (wide_destp - wide_dest) * MB_CUR_MAX + 1;
2790Sstevel@tonic-gate 	uc = safe_zalloc(destlen);
2800Sstevel@tonic-gate 	if (wcstombs(uc, wide_dest, destlen) == (size_t)-1) {
2810Sstevel@tonic-gate 		/* If we've gotten this far, wcstombs shouldn't fail... */
2820Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: wcstombs failed unexpectedly: %s\n",
2830Sstevel@tonic-gate 		    command, strerror(errno));
2840Sstevel@tonic-gate 		exit(1);
2850Sstevel@tonic-gate 	} else {
2860Sstevel@tonic-gate 		char *tmp;
2870Sstevel@tonic-gate 		/*
2880Sstevel@tonic-gate 		 * Try to save memory; don't waste 3 * strlen in the
2890Sstevel@tonic-gate 		 * common case.
2900Sstevel@tonic-gate 		 */
2910Sstevel@tonic-gate 		tmp = safe_strdup(uc);
2920Sstevel@tonic-gate 		free(uc);
2930Sstevel@tonic-gate 		uc = tmp;
2940Sstevel@tonic-gate 	}
2950Sstevel@tonic-gate 	free(wide_dest);
2960Sstevel@tonic-gate 	free(wide_src);
2970Sstevel@tonic-gate 	return (uc);
2980Sstevel@tonic-gate }
2990Sstevel@tonic-gate 
3000Sstevel@tonic-gate /*
3010Sstevel@tonic-gate  * These functions determine which characters are safe to be left unquoted.
3020Sstevel@tonic-gate  * Rather than starting with every printable character and subtracting out the
3030Sstevel@tonic-gate  * shell metacharacters, we take the more conservative approach of starting with
3040Sstevel@tonic-gate  * a set of safe characters and adding those few common punctuation characters
3050Sstevel@tonic-gate  * which are known to be safe.  The rules are:
3060Sstevel@tonic-gate  *
3070Sstevel@tonic-gate  * 	If this is a printable character (graph), and not punctuation, it is
3080Sstevel@tonic-gate  * 	safe to leave unquoted.
3090Sstevel@tonic-gate  *
3100Sstevel@tonic-gate  * 	If it's one of known hard-coded safe characters, it's also safe to leave
3110Sstevel@tonic-gate  * 	unquoted.
3120Sstevel@tonic-gate  *
3130Sstevel@tonic-gate  * 	Otherwise, the entire argument must be quoted.
3140Sstevel@tonic-gate  *
3150Sstevel@tonic-gate  * This will cause some strings to be unecessarily quoted, but it is safer than
3160Sstevel@tonic-gate  * having a character unintentionally interpreted by the shell.
3170Sstevel@tonic-gate  */
3180Sstevel@tonic-gate static int
issafe_ascii(char c)3190Sstevel@tonic-gate issafe_ascii(char c)
3200Sstevel@tonic-gate {
3210Sstevel@tonic-gate 	return (isalnum(c) || strchr("_.-/@:,", c) != NULL);
3220Sstevel@tonic-gate }
3230Sstevel@tonic-gate 
3240Sstevel@tonic-gate static int
issafe(wchar_t wc)3250Sstevel@tonic-gate issafe(wchar_t wc)
3260Sstevel@tonic-gate {
3270Sstevel@tonic-gate 	return ((iswgraph(wc) && !iswpunct(wc)) ||
3280Sstevel@tonic-gate 	    wschr(L"_.-/@:,", wc) != NULL);
3290Sstevel@tonic-gate }
3300Sstevel@tonic-gate 
3310Sstevel@tonic-gate /*ARGSUSED*/
3320Sstevel@tonic-gate static char *
quote_string_ascii(pargs_data_t * datap,char * src)3330Sstevel@tonic-gate quote_string_ascii(pargs_data_t *datap, char *src)
3340Sstevel@tonic-gate {
3350Sstevel@tonic-gate 	char *dst;
3360Sstevel@tonic-gate 	int quote_count = 0;
3370Sstevel@tonic-gate 	int need_quote = 0;
3380Sstevel@tonic-gate 	char *srcp, *dstp;
3390Sstevel@tonic-gate 	size_t dstlen;
3400Sstevel@tonic-gate 
3410Sstevel@tonic-gate 	for (srcp = src; *srcp != '\0'; srcp++) {
3420Sstevel@tonic-gate 		if (!issafe_ascii(*srcp)) {
3430Sstevel@tonic-gate 			need_quote = 1;
3440Sstevel@tonic-gate 			if (*srcp == '\'')
3450Sstevel@tonic-gate 				quote_count++;
3460Sstevel@tonic-gate 		}
3470Sstevel@tonic-gate 	}
3480Sstevel@tonic-gate 
3490Sstevel@tonic-gate 	if (!need_quote)
3500Sstevel@tonic-gate 		return (src);
3510Sstevel@tonic-gate 
3520Sstevel@tonic-gate 	/*
3530Sstevel@tonic-gate 	 * The only character we care about here is a single quote.  All the
3540Sstevel@tonic-gate 	 * other unprintable characters (and backslashes) will have been dealt
3550Sstevel@tonic-gate 	 * with by unctrl_str().  We make the following subtitution when we
3560Sstevel@tonic-gate 	 * encounter a single quote:
3570Sstevel@tonic-gate 	 *
3580Sstevel@tonic-gate 	 * 	' = '"'"'
3590Sstevel@tonic-gate 	 *
3600Sstevel@tonic-gate 	 * In addition, we put single quotes around the entire argument.  For
3610Sstevel@tonic-gate 	 * example:
3620Sstevel@tonic-gate 	 *
3630Sstevel@tonic-gate 	 * 	foo'bar = 'foo'"'"'bar'
3640Sstevel@tonic-gate 	 */
3650Sstevel@tonic-gate 	dstlen = strlen(src) + 3 + 4 * quote_count;
3660Sstevel@tonic-gate 	dst = safe_zalloc(dstlen);
3670Sstevel@tonic-gate 
3680Sstevel@tonic-gate 	dstp = dst;
3690Sstevel@tonic-gate 	*dstp++ = '\'';
3700Sstevel@tonic-gate 	for (srcp = src; *srcp != '\0'; srcp++, dstp++) {
3710Sstevel@tonic-gate 		*dstp = *srcp;
3720Sstevel@tonic-gate 
3730Sstevel@tonic-gate 		if (*srcp == '\'') {
3740Sstevel@tonic-gate 			dstp[1] = '"';
3750Sstevel@tonic-gate 			dstp[2] = '\'';
3760Sstevel@tonic-gate 			dstp[3] = '"';
3770Sstevel@tonic-gate 			dstp[4] = '\'';
3780Sstevel@tonic-gate 			dstp += 4;
3790Sstevel@tonic-gate 		}
3800Sstevel@tonic-gate 	}
3810Sstevel@tonic-gate 	*dstp++ = '\'';
3820Sstevel@tonic-gate 	*dstp = '\0';
3830Sstevel@tonic-gate 
3840Sstevel@tonic-gate 	free(src);
3850Sstevel@tonic-gate 
3860Sstevel@tonic-gate 	return (dst);
3870Sstevel@tonic-gate }
3880Sstevel@tonic-gate 
3890Sstevel@tonic-gate static char *
quote_string(pargs_data_t * datap,char * src)3900Sstevel@tonic-gate quote_string(pargs_data_t *datap, char *src)
3910Sstevel@tonic-gate {
3920Sstevel@tonic-gate 	wchar_t *wide_src, *wide_srcp;
3930Sstevel@tonic-gate 	wchar_t *wide_dest, *wide_destp;
3940Sstevel@tonic-gate 	char *uc;
3950Sstevel@tonic-gate 	size_t srcbufsz = strlen(src) + 1;
3960Sstevel@tonic-gate 	size_t srclen;
3970Sstevel@tonic-gate 	size_t destbufsz;
3980Sstevel@tonic-gate 	size_t destlen;
3990Sstevel@tonic-gate 	int quote_count = 0;
4000Sstevel@tonic-gate 	int need_quote = 0;
4010Sstevel@tonic-gate 
4020Sstevel@tonic-gate 	if (datap->pd_conv_flags & CONV_STRICT_ASCII)
4030Sstevel@tonic-gate 		return (quote_string_ascii(datap, src));
4040Sstevel@tonic-gate 
4050Sstevel@tonic-gate 	wide_srcp = wide_src = safe_zalloc(srcbufsz * sizeof (wchar_t));
4060Sstevel@tonic-gate 
4070Sstevel@tonic-gate 	if ((srclen = mbstowcs(wide_src, src, srcbufsz - 1)) == (size_t)-1) {
4080Sstevel@tonic-gate 		free(wide_src);
4090Sstevel@tonic-gate 		return (quote_string_ascii(datap, src));
4100Sstevel@tonic-gate 	}
4110Sstevel@tonic-gate 
4120Sstevel@tonic-gate 	if (srclen == srcbufsz - 1)
4130Sstevel@tonic-gate 		wide_src[srclen] = L'\0';
4140Sstevel@tonic-gate 
4150Sstevel@tonic-gate 	for (wide_srcp = wide_src; *wide_srcp != '\0'; wide_srcp++) {
4160Sstevel@tonic-gate 		if (!issafe(*wide_srcp)) {
4170Sstevel@tonic-gate 			need_quote = 1;
4180Sstevel@tonic-gate 			if (*wide_srcp == L'\'')
4190Sstevel@tonic-gate 				quote_count++;
4200Sstevel@tonic-gate 		}
4210Sstevel@tonic-gate 	}
4220Sstevel@tonic-gate 
4230Sstevel@tonic-gate 	if (!need_quote) {
4240Sstevel@tonic-gate 		free(wide_src);
4250Sstevel@tonic-gate 		return (src);
4260Sstevel@tonic-gate 	}
4270Sstevel@tonic-gate 
4280Sstevel@tonic-gate 	/*
4290Sstevel@tonic-gate 	 * See comment for quote_string_ascii(), above.
4300Sstevel@tonic-gate 	 */
4310Sstevel@tonic-gate 	destbufsz = srcbufsz + 3 + 4 * quote_count;
4320Sstevel@tonic-gate 	wide_destp = wide_dest = safe_zalloc(destbufsz * sizeof (wchar_t));
4330Sstevel@tonic-gate 
4340Sstevel@tonic-gate 	*wide_destp++ = L'\'';
4350Sstevel@tonic-gate 	for (wide_srcp = wide_src; *wide_srcp != L'\0';
4360Sstevel@tonic-gate 	    wide_srcp++, wide_destp++) {
4370Sstevel@tonic-gate 		*wide_destp = *wide_srcp;
4380Sstevel@tonic-gate 
4390Sstevel@tonic-gate 		if (*wide_srcp == L'\'') {
4400Sstevel@tonic-gate 			wide_destp[1] = L'"';
4410Sstevel@tonic-gate 			wide_destp[2] = L'\'';
4420Sstevel@tonic-gate 			wide_destp[3] = L'"';
4430Sstevel@tonic-gate 			wide_destp[4] = L'\'';
4440Sstevel@tonic-gate 			wide_destp += 4;
4450Sstevel@tonic-gate 		}
4460Sstevel@tonic-gate 	}
4470Sstevel@tonic-gate 	*wide_destp++ = L'\'';
4480Sstevel@tonic-gate 	*wide_destp = L'\0';
4490Sstevel@tonic-gate 
4500Sstevel@tonic-gate 	destlen = destbufsz * MB_CUR_MAX + 1;
4510Sstevel@tonic-gate 	uc = safe_zalloc(destlen);
4520Sstevel@tonic-gate 	if (wcstombs(uc, wide_dest, destlen) == (size_t)-1) {
4530Sstevel@tonic-gate 		/* If we've gotten this far, wcstombs shouldn't fail... */
4540Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: wcstombs failed unexpectedly: %s\n",
4550Sstevel@tonic-gate 		    command, strerror(errno));
4560Sstevel@tonic-gate 		exit(1);
4570Sstevel@tonic-gate 	}
4580Sstevel@tonic-gate 
4590Sstevel@tonic-gate 	free(wide_dest);
4600Sstevel@tonic-gate 	free(wide_src);
4610Sstevel@tonic-gate 
4620Sstevel@tonic-gate 	return (uc);
4630Sstevel@tonic-gate }
4640Sstevel@tonic-gate 
4650Sstevel@tonic-gate 
4660Sstevel@tonic-gate /*
4670Sstevel@tonic-gate  * Determine the locale of the target process by traversing its environment,
4680Sstevel@tonic-gate  * making only one pass for efficiency's sake; stash the result in
4690Sstevel@tonic-gate  * datap->pd_locale.
4700Sstevel@tonic-gate  *
4710Sstevel@tonic-gate  * It's possible that the process has called setlocale() to change its
4720Sstevel@tonic-gate  * locale to something different, but we mostly care about making a good
4730Sstevel@tonic-gate  * guess as to the locale at exec(2) time.
4740Sstevel@tonic-gate  */
4750Sstevel@tonic-gate static void
lookup_locale(pargs_data_t * datap)4760Sstevel@tonic-gate lookup_locale(pargs_data_t *datap)
4770Sstevel@tonic-gate {
4780Sstevel@tonic-gate 	int i, j, composite = 0;
4790Sstevel@tonic-gate 	size_t	len = 0;
4800Sstevel@tonic-gate 	char	*pd_locale;
4810Sstevel@tonic-gate 	char	*lc_all = NULL, *lang = NULL;
4820Sstevel@tonic-gate 	char	*lcs[] = { NULL, NULL, NULL, NULL, NULL, NULL };
4830Sstevel@tonic-gate 	static const char *cat_names[] = {
4840Sstevel@tonic-gate 		"LC_CTYPE=",	"LC_NUMERIC=",	"LC_TIME=",
4850Sstevel@tonic-gate 		"LC_COLLATE=",	"LC_MONETARY=",	"LC_MESSAGES="
4860Sstevel@tonic-gate 	};
4870Sstevel@tonic-gate 
4880Sstevel@tonic-gate 	for (i = 0; i < datap->pd_envc; i++) {
4890Sstevel@tonic-gate 		char *s = datap->pd_envp_strs[i];
4900Sstevel@tonic-gate 
4910Sstevel@tonic-gate 		if (s == NULL)
4920Sstevel@tonic-gate 			continue;
4930Sstevel@tonic-gate 
4940Sstevel@tonic-gate 		if (strncmp("LC_ALL=", s, strlen("LC_ALL=")) == 0) {
4950Sstevel@tonic-gate 			/*
4960Sstevel@tonic-gate 			 * Minor optimization-- if we find LC_ALL we're done.
4970Sstevel@tonic-gate 			 */
4980Sstevel@tonic-gate 			lc_all = s + strlen("LC_ALL=");
4990Sstevel@tonic-gate 			break;
5000Sstevel@tonic-gate 		}
5010Sstevel@tonic-gate 		for (j = 0; j <= _LastCategory; j++) {
5020Sstevel@tonic-gate 			if (strncmp(cat_names[j], s,
5030Sstevel@tonic-gate 			    strlen(cat_names[j])) == 0) {
5040Sstevel@tonic-gate 				lcs[j] = s + strlen(cat_names[j]);
5050Sstevel@tonic-gate 			}
5060Sstevel@tonic-gate 		}
5070Sstevel@tonic-gate 		if (strncmp("LANG=", s, strlen("LANG=")) == 0) {
5080Sstevel@tonic-gate 			lang = s + strlen("LANG=");
5090Sstevel@tonic-gate 		}
5100Sstevel@tonic-gate 	}
5110Sstevel@tonic-gate 
5120Sstevel@tonic-gate 	if (lc_all && (*lc_all == '\0'))
5130Sstevel@tonic-gate 		lc_all = NULL;
5140Sstevel@tonic-gate 	if (lang && (*lang == '\0'))
5150Sstevel@tonic-gate 		lang = NULL;
5160Sstevel@tonic-gate 
5170Sstevel@tonic-gate 	for (i = 0; i <= _LastCategory; i++) {
5180Sstevel@tonic-gate 		if (lc_all != NULL) {
5190Sstevel@tonic-gate 			lcs[i] = lc_all;
5200Sstevel@tonic-gate 		} else if (lcs[i] != NULL) {
5210Sstevel@tonic-gate 			lcs[i] = lcs[i];
5220Sstevel@tonic-gate 		} else if (lang != NULL) {
5230Sstevel@tonic-gate 			lcs[i] = lang;
5240Sstevel@tonic-gate 		} else {
5250Sstevel@tonic-gate 			lcs[i] = "C";
5260Sstevel@tonic-gate 		}
5270Sstevel@tonic-gate 		if ((i > 0) && (lcs[i] != lcs[i-1]))
5280Sstevel@tonic-gate 			composite++;
5290Sstevel@tonic-gate 
5300Sstevel@tonic-gate 		len += 1 + strlen(lcs[i]);	/* 1 extra byte for '/' */
5310Sstevel@tonic-gate 	}
5320Sstevel@tonic-gate 
5330Sstevel@tonic-gate 	if (composite == 0) {
5340Sstevel@tonic-gate 		/* simple locale */
5350Sstevel@tonic-gate 		pd_locale = safe_strdup(lcs[0]);
5360Sstevel@tonic-gate 	} else {
5370Sstevel@tonic-gate 		/* composite locale */
5380Sstevel@tonic-gate 		pd_locale = safe_zalloc(len + 1);
5390Sstevel@tonic-gate 		(void) snprintf(pd_locale, len + 1, "/%s/%s/%s/%s/%s/%s",
5400Sstevel@tonic-gate 		    lcs[0], lcs[1], lcs[2], lcs[3], lcs[4], lcs[5]);
5410Sstevel@tonic-gate 	}
5420Sstevel@tonic-gate 	datap->pd_locale = pd_locale;
5430Sstevel@tonic-gate }
5440Sstevel@tonic-gate 
5450Sstevel@tonic-gate /*
5460Sstevel@tonic-gate  * Pull a string from the victim, regardless of size; this routine allocates
5470Sstevel@tonic-gate  * memory for the string which must be freed by the caller.
5480Sstevel@tonic-gate  */
5490Sstevel@tonic-gate static char *
extract_string(pargs_data_t * datap,uintptr_t addr)5500Sstevel@tonic-gate extract_string(pargs_data_t *datap, uintptr_t addr)
5510Sstevel@tonic-gate {
5520Sstevel@tonic-gate 	int size = EXTRACT_BUFSZ;
5530Sstevel@tonic-gate 	char *result;
5540Sstevel@tonic-gate 
5550Sstevel@tonic-gate 	result = safe_zalloc(size);
5560Sstevel@tonic-gate 
5570Sstevel@tonic-gate 	for (;;) {
5580Sstevel@tonic-gate 		if (Pread_string(datap->pd_proc, result, size, addr) < 0) {
5590Sstevel@tonic-gate 			free(result);
5600Sstevel@tonic-gate 			return (NULL);
5610Sstevel@tonic-gate 		} else if (strlen(result) == (size - 1)) {
5620Sstevel@tonic-gate 			free(result);
5630Sstevel@tonic-gate 			size *= 2;
5640Sstevel@tonic-gate 			result = safe_zalloc(size);
5650Sstevel@tonic-gate 		} else {
5660Sstevel@tonic-gate 			break;
5670Sstevel@tonic-gate 		}
5680Sstevel@tonic-gate 	}
5690Sstevel@tonic-gate 	return (result);
5700Sstevel@tonic-gate }
5710Sstevel@tonic-gate 
5720Sstevel@tonic-gate /*
5730Sstevel@tonic-gate  * Utility function to read an array of pointers from the victim, adjusting
5740Sstevel@tonic-gate  * for victim data model; returns the number of bytes successfully read.
5750Sstevel@tonic-gate  */
5760Sstevel@tonic-gate static ssize_t
read_ptr_array(pargs_data_t * datap,uintptr_t offset,uintptr_t * buf,size_t nelems)5770Sstevel@tonic-gate read_ptr_array(pargs_data_t *datap, uintptr_t offset, uintptr_t *buf,
5780Sstevel@tonic-gate     size_t nelems)
5790Sstevel@tonic-gate {
5800Sstevel@tonic-gate 	ssize_t res;
5810Sstevel@tonic-gate 
5820Sstevel@tonic-gate 	if (dmodel == PR_MODEL_NATIVE) {
5830Sstevel@tonic-gate 		res = Pread(datap->pd_proc, buf, nelems * sizeof (uintptr_t),
5840Sstevel@tonic-gate 		    offset);
5850Sstevel@tonic-gate 	} else {
5860Sstevel@tonic-gate 		int i;
5870Sstevel@tonic-gate 		uint32_t *arr32 = safe_zalloc(nelems * sizeof (uint32_t));
5880Sstevel@tonic-gate 
5890Sstevel@tonic-gate 		res = Pread(datap->pd_proc, arr32, nelems * sizeof (uint32_t),
5900Sstevel@tonic-gate 		    offset);
5910Sstevel@tonic-gate 		if (res > 0) {
5920Sstevel@tonic-gate 			for (i = 0; i < nelems; i++)
5930Sstevel@tonic-gate 				buf[i] = arr32[i];
5940Sstevel@tonic-gate 		}
5950Sstevel@tonic-gate 		free(arr32);
5960Sstevel@tonic-gate 	}
5970Sstevel@tonic-gate 	return (res);
5980Sstevel@tonic-gate }
5990Sstevel@tonic-gate 
6000Sstevel@tonic-gate /*
6010Sstevel@tonic-gate  * Extract the argv array from the victim; store the pointer values in
6020Sstevel@tonic-gate  * datap->pd_argv and the extracted strings in datap->pd_argv_strs.
6030Sstevel@tonic-gate  */
6040Sstevel@tonic-gate static void
get_args(pargs_data_t * datap)6050Sstevel@tonic-gate get_args(pargs_data_t *datap)
6060Sstevel@tonic-gate {
6070Sstevel@tonic-gate 	size_t argc = datap->pd_psinfo->pr_argc;
6080Sstevel@tonic-gate 	uintptr_t argvoff = datap->pd_psinfo->pr_argv;
6090Sstevel@tonic-gate 	int i;
6100Sstevel@tonic-gate 
6110Sstevel@tonic-gate 	datap->pd_argc = argc;
6120Sstevel@tonic-gate 	datap->pd_argv = safe_zalloc(argc * sizeof (uintptr_t));
6130Sstevel@tonic-gate 
6140Sstevel@tonic-gate 	if (read_ptr_array(datap, argvoff, datap->pd_argv, argc) <= 0) {
6150Sstevel@tonic-gate 		free(datap->pd_argv);
6160Sstevel@tonic-gate 		datap->pd_argv = NULL;
6170Sstevel@tonic-gate 		return;
6180Sstevel@tonic-gate 	}
6190Sstevel@tonic-gate 
6200Sstevel@tonic-gate 	datap->pd_argv_strs = safe_zalloc(argc * sizeof (char *));
6210Sstevel@tonic-gate 	for (i = 0; i < argc; i++) {
6220Sstevel@tonic-gate 		if (datap->pd_argv[i] == 0)
6230Sstevel@tonic-gate 			continue;
6240Sstevel@tonic-gate 		datap->pd_argv_strs[i] = extract_string(datap,
6250Sstevel@tonic-gate 		    datap->pd_argv[i]);
6260Sstevel@tonic-gate 	}
6270Sstevel@tonic-gate }
6280Sstevel@tonic-gate 
6290Sstevel@tonic-gate /*ARGSUSED*/
6300Sstevel@tonic-gate static int
build_env(void * data,struct ps_prochandle * pr,uintptr_t addr,const char * str)6310Sstevel@tonic-gate build_env(void *data, struct ps_prochandle *pr, uintptr_t addr, const char *str)
6320Sstevel@tonic-gate {
6330Sstevel@tonic-gate 	pargs_data_t *datap = data;
6340Sstevel@tonic-gate 
6350Sstevel@tonic-gate 	if (datap->pd_envp != NULL) {
6360Sstevel@tonic-gate 		datap->pd_envp[datap->pd_envc] = addr;
6370Sstevel@tonic-gate 		if (str == NULL)
6380Sstevel@tonic-gate 			datap->pd_envp_strs[datap->pd_envc] = NULL;
6390Sstevel@tonic-gate 		else
6400Sstevel@tonic-gate 			datap->pd_envp_strs[datap->pd_envc] = strdup(str);
6410Sstevel@tonic-gate 	}
6420Sstevel@tonic-gate 
6430Sstevel@tonic-gate 	datap->pd_envc++;
6440Sstevel@tonic-gate 
6450Sstevel@tonic-gate 	return (0);
6460Sstevel@tonic-gate }
6470Sstevel@tonic-gate 
6480Sstevel@tonic-gate static void
get_env(pargs_data_t * datap)6490Sstevel@tonic-gate get_env(pargs_data_t *datap)
6500Sstevel@tonic-gate {
6510Sstevel@tonic-gate 	struct ps_prochandle *pr = datap->pd_proc;
6520Sstevel@tonic-gate 
6530Sstevel@tonic-gate 	datap->pd_envc = 0;
6540Sstevel@tonic-gate 	(void) Penv_iter(pr, build_env, datap);
6550Sstevel@tonic-gate 
6560Sstevel@tonic-gate 	datap->pd_envp = safe_zalloc(sizeof (uintptr_t) * datap->pd_envc);
6570Sstevel@tonic-gate 	datap->pd_envp_strs = safe_zalloc(sizeof (char *) * datap->pd_envc);
6580Sstevel@tonic-gate 
6590Sstevel@tonic-gate 	datap->pd_envc = 0;
6600Sstevel@tonic-gate 	(void) Penv_iter(pr, build_env, datap);
6610Sstevel@tonic-gate }
6620Sstevel@tonic-gate 
6630Sstevel@tonic-gate /*
6640Sstevel@tonic-gate  * The following at_* routines are used to decode data from the aux vector.
6650Sstevel@tonic-gate  */
6660Sstevel@tonic-gate 
6670Sstevel@tonic-gate /*ARGSUSED*/
6680Sstevel@tonic-gate static void
at_null(long val,char * instr,size_t n,char * str)6690Sstevel@tonic-gate at_null(long val, char *instr, size_t n, char *str)
6700Sstevel@tonic-gate {
6710Sstevel@tonic-gate 	str[0] = '\0';
6720Sstevel@tonic-gate }
6730Sstevel@tonic-gate 
6740Sstevel@tonic-gate /*ARGSUSED*/
6750Sstevel@tonic-gate static void
at_str(long val,char * instr,size_t n,char * str)6760Sstevel@tonic-gate at_str(long val, char *instr, size_t n, char *str)
6770Sstevel@tonic-gate {
6780Sstevel@tonic-gate 	str[0] = '\0';
6790Sstevel@tonic-gate 	if (instr != NULL) {
6800Sstevel@tonic-gate 		(void) strlcpy(str, instr, n);
6810Sstevel@tonic-gate 	}
6820Sstevel@tonic-gate }
6830Sstevel@tonic-gate 
6840Sstevel@tonic-gate /*
6850Sstevel@tonic-gate  * Note: Don't forget to add a corresponding case to isainfo(1).
6860Sstevel@tonic-gate  */
6870Sstevel@tonic-gate 
6880Sstevel@tonic-gate #define	FMT_AV(s, n, hwcap, mask, name)				\
6890Sstevel@tonic-gate 	if ((hwcap) & (mask)) 					\
6900Sstevel@tonic-gate 		(void) snprintf(s, n, "%s" name " | ", s)
6910Sstevel@tonic-gate 
6920Sstevel@tonic-gate /*ARGSUSED*/
6930Sstevel@tonic-gate static void
at_hwcap(long val,char * instr,size_t n,char * str)6940Sstevel@tonic-gate at_hwcap(long val, char *instr, size_t n, char *str)
6950Sstevel@tonic-gate {
6960Sstevel@tonic-gate #if defined(__sparc) || defined(__sparcv9)
697*5565Sab196087 	(void) elfcap_hw1_to_str(ELFCAP_STYLE_UC, val, str, n,
698*5565Sab196087 	    ELFCAP_FMT_PIPSPACE, EM_SPARC);
6990Sstevel@tonic-gate 
7000Sstevel@tonic-gate #elif defined(__i386) || defined(__amd64)
701*5565Sab196087 	(void) elfcap_hw1_to_str(ELFCAP_STYLE_UC, val, str, n,
702*5565Sab196087 	    ELFCAP_FMT_PIPSPACE, EM_386);
7030Sstevel@tonic-gate #else
7040Sstevel@tonic-gate #error	"port me"
7050Sstevel@tonic-gate #endif
7060Sstevel@tonic-gate }
7070Sstevel@tonic-gate 
7080Sstevel@tonic-gate /*ARGSUSED*/
7090Sstevel@tonic-gate static void
at_uid(long val,char * instr,size_t n,char * str)7100Sstevel@tonic-gate at_uid(long val, char *instr, size_t n, char *str)
7110Sstevel@tonic-gate {
7120Sstevel@tonic-gate 	struct passwd *pw = getpwuid((uid_t)val);
7130Sstevel@tonic-gate 
7140Sstevel@tonic-gate 	if ((pw == NULL) || (pw->pw_name == NULL))
7150Sstevel@tonic-gate 		str[0] = '\0';
7160Sstevel@tonic-gate 	else
7170Sstevel@tonic-gate 		(void) snprintf(str, n, "%lu(%s)", val, pw->pw_name);
7180Sstevel@tonic-gate }
7190Sstevel@tonic-gate 
7200Sstevel@tonic-gate 
7210Sstevel@tonic-gate /*ARGSUSED*/
7220Sstevel@tonic-gate static void
at_gid(long val,char * instr,size_t n,char * str)7230Sstevel@tonic-gate at_gid(long val, char *instr, size_t n, char *str)
7240Sstevel@tonic-gate {
7250Sstevel@tonic-gate 	struct group *gr = getgrgid((gid_t)val);
7260Sstevel@tonic-gate 
7270Sstevel@tonic-gate 	if ((gr == NULL) || (gr->gr_name == NULL))
7280Sstevel@tonic-gate 		str[0] = '\0';
7290Sstevel@tonic-gate 	else
7300Sstevel@tonic-gate 		(void) snprintf(str, n, "%lu(%s)", val, gr->gr_name);
7310Sstevel@tonic-gate }
7320Sstevel@tonic-gate 
7330Sstevel@tonic-gate static struct auxfl {
7340Sstevel@tonic-gate 	int af_flag;
7350Sstevel@tonic-gate 	const char *af_name;
7360Sstevel@tonic-gate } auxfl[] = {
7370Sstevel@tonic-gate 	{ AF_SUN_SETUGID,	"setugid" },
7380Sstevel@tonic-gate };
7390Sstevel@tonic-gate 
7400Sstevel@tonic-gate /*ARGSUSED*/
7410Sstevel@tonic-gate static void
at_flags(long val,char * instr,size_t n,char * str)7420Sstevel@tonic-gate at_flags(long val, char *instr, size_t n, char *str)
7430Sstevel@tonic-gate {
7440Sstevel@tonic-gate 	int i;
7450Sstevel@tonic-gate 
7460Sstevel@tonic-gate 	*str = '\0';
7470Sstevel@tonic-gate 
7480Sstevel@tonic-gate 	for (i = 0; i < sizeof (auxfl)/sizeof (struct auxfl); i++) {
7490Sstevel@tonic-gate 		if ((val & auxfl[i].af_flag) != 0) {
7500Sstevel@tonic-gate 			if (*str != '\0')
7510Sstevel@tonic-gate 				(void) strlcat(str, ",", n);
7520Sstevel@tonic-gate 			(void) strlcat(str, auxfl[i].af_name, n);
7530Sstevel@tonic-gate 		}
7540Sstevel@tonic-gate 	}
7550Sstevel@tonic-gate }
7560Sstevel@tonic-gate 
7570Sstevel@tonic-gate #define	MAX_AT_NAME_LEN	15
7580Sstevel@tonic-gate 
7590Sstevel@tonic-gate struct aux_id {
7600Sstevel@tonic-gate 	int aux_type;
7610Sstevel@tonic-gate 	const char *aux_name;
7620Sstevel@tonic-gate 	void (*aux_decode)(long, char *, size_t, char *);
7630Sstevel@tonic-gate };
7640Sstevel@tonic-gate 
7650Sstevel@tonic-gate static struct aux_id aux_arr[] = {
7660Sstevel@tonic-gate 	{ AT_NULL,		"AT_NULL",		at_null	},
7670Sstevel@tonic-gate 	{ AT_IGNORE,		"AT_IGNORE",		at_null	},
7680Sstevel@tonic-gate 	{ AT_EXECFD,		"AT_EXECFD",		at_null	},
7690Sstevel@tonic-gate 	{ AT_PHDR,		"AT_PHDR",		at_null	},
7700Sstevel@tonic-gate 	{ AT_PHENT,		"AT_PHENT",		at_null	},
7710Sstevel@tonic-gate 	{ AT_PHNUM,		"AT_PHNUM",		at_null	},
7720Sstevel@tonic-gate 	{ AT_PAGESZ,		"AT_PAGESZ",		at_null	},
7730Sstevel@tonic-gate 	{ AT_BASE,		"AT_BASE",		at_null	},
7740Sstevel@tonic-gate 	{ AT_FLAGS,		"AT_FLAGS",		at_null	},
7750Sstevel@tonic-gate 	{ AT_ENTRY,		"AT_ENTRY",		at_null	},
7760Sstevel@tonic-gate 	{ AT_SUN_UID,		"AT_SUN_UID",		at_uid	},
7770Sstevel@tonic-gate 	{ AT_SUN_RUID,		"AT_SUN_RUID",		at_uid	},
7780Sstevel@tonic-gate 	{ AT_SUN_GID,		"AT_SUN_GID",		at_gid	},
7790Sstevel@tonic-gate 	{ AT_SUN_RGID,		"AT_SUN_RGID",		at_gid	},
7800Sstevel@tonic-gate 	{ AT_SUN_LDELF,		"AT_SUN_LDELF",		at_null	},
7810Sstevel@tonic-gate 	{ AT_SUN_LDSHDR,	"AT_SUN_LDSHDR",	at_null	},
7820Sstevel@tonic-gate 	{ AT_SUN_LDNAME,	"AT_SUN_LDNAME",	at_null	},
7830Sstevel@tonic-gate 	{ AT_SUN_LPAGESZ,	"AT_SUN_LPAGESZ",	at_null	},
7840Sstevel@tonic-gate 	{ AT_SUN_PLATFORM,	"AT_SUN_PLATFORM",	at_str	},
7850Sstevel@tonic-gate 	{ AT_SUN_EXECNAME,	"AT_SUN_EXECNAME",	at_str	},
7860Sstevel@tonic-gate 	{ AT_SUN_HWCAP,		"AT_SUN_HWCAP",		at_hwcap },
7870Sstevel@tonic-gate 	{ AT_SUN_IFLUSH,	"AT_SUN_IFLUSH",	at_null	},
7880Sstevel@tonic-gate 	{ AT_SUN_CPU,		"AT_SUN_CPU",		at_null	},
7890Sstevel@tonic-gate 	{ AT_SUN_MMU,		"AT_SUN_MMU",		at_null	},
7900Sstevel@tonic-gate 	{ AT_SUN_LDDATA,	"AT_SUN_LDDATA",	at_null	},
7910Sstevel@tonic-gate 	{ AT_SUN_AUXFLAGS,	"AT_SUN_AUXFLAGS",	at_flags },
7922712Snn35248 	{ AT_SUN_EMULATOR,	"AT_SUN_EMULATOR",	at_str	},
7932712Snn35248 	{ AT_SUN_BRANDNAME,	"AT_SUN_BRANDNAME",	at_str	},
7944642Ssl108498 	{ AT_SUN_BRAND_AUX1,	"AT_SUN_BRAND_AUX1",	at_null	},
7954642Ssl108498 	{ AT_SUN_BRAND_AUX2,	"AT_SUN_BRAND_AUX2",	at_null	},
7964642Ssl108498 	{ AT_SUN_BRAND_AUX3,	"AT_SUN_BRAND_AUX3",	at_null	}
7970Sstevel@tonic-gate };
7980Sstevel@tonic-gate 
7990Sstevel@tonic-gate #define	N_AT_ENTS (sizeof (aux_arr) / sizeof (struct aux_id))
8000Sstevel@tonic-gate 
8010Sstevel@tonic-gate /*
8020Sstevel@tonic-gate  * Return the aux_id entry for the given aux type; returns NULL if not found.
8030Sstevel@tonic-gate  */
8040Sstevel@tonic-gate static struct aux_id *
aux_find(int type)8050Sstevel@tonic-gate aux_find(int type)
8060Sstevel@tonic-gate {
8070Sstevel@tonic-gate 	int i;
8080Sstevel@tonic-gate 
8090Sstevel@tonic-gate 	for (i = 0; i < N_AT_ENTS; i++) {
8100Sstevel@tonic-gate 		if (type == aux_arr[i].aux_type)
8110Sstevel@tonic-gate 			return (&aux_arr[i]);
8120Sstevel@tonic-gate 	}
8130Sstevel@tonic-gate 
8140Sstevel@tonic-gate 	return (NULL);
8150Sstevel@tonic-gate }
8160Sstevel@tonic-gate 
8170Sstevel@tonic-gate static void
get_auxv(pargs_data_t * datap)8180Sstevel@tonic-gate get_auxv(pargs_data_t *datap)
8190Sstevel@tonic-gate {
8200Sstevel@tonic-gate 	int i;
8210Sstevel@tonic-gate 	const auxv_t *auxvp;
8220Sstevel@tonic-gate 
8230Sstevel@tonic-gate 	/*
8240Sstevel@tonic-gate 	 * Fetch the aux vector from the target process.
8250Sstevel@tonic-gate 	 */
8260Sstevel@tonic-gate 	if (ps_pauxv(datap->pd_proc, &auxvp) != PS_OK)
8270Sstevel@tonic-gate 		return;
8280Sstevel@tonic-gate 
8290Sstevel@tonic-gate 	for (i = 0; auxvp[i].a_type != AT_NULL; i++)
8300Sstevel@tonic-gate 		continue;
8310Sstevel@tonic-gate 
8320Sstevel@tonic-gate 	datap->pd_auxc = i;
8330Sstevel@tonic-gate 	datap->pd_auxv = safe_zalloc(i * sizeof (auxv_t));
8340Sstevel@tonic-gate 	bcopy(auxvp, datap->pd_auxv, i * sizeof (auxv_t));
8350Sstevel@tonic-gate 
8360Sstevel@tonic-gate 	datap->pd_auxv_strs = safe_zalloc(datap->pd_auxc * sizeof (char *));
8370Sstevel@tonic-gate 	for (i = 0; i < datap->pd_auxc; i++) {
8380Sstevel@tonic-gate 		struct aux_id *aux = aux_find(datap->pd_auxv[i].a_type);
8390Sstevel@tonic-gate 
8400Sstevel@tonic-gate 		/*
8410Sstevel@tonic-gate 		 * Grab strings for those entries which have a string-decoder.
8420Sstevel@tonic-gate 		 */
8430Sstevel@tonic-gate 		if ((aux != NULL) && (aux->aux_decode == at_str)) {
8440Sstevel@tonic-gate 			datap->pd_auxv_strs[i] =
8450Sstevel@tonic-gate 			    extract_string(datap, datap->pd_auxv[i].a_un.a_val);
8460Sstevel@tonic-gate 		}
8470Sstevel@tonic-gate 	}
8480Sstevel@tonic-gate }
8490Sstevel@tonic-gate 
8500Sstevel@tonic-gate /*
8510Sstevel@tonic-gate  * Prepare to convert characters in the victim's character set into user's
8520Sstevel@tonic-gate  * character set.
8530Sstevel@tonic-gate  */
8540Sstevel@tonic-gate static void
setup_conversions(pargs_data_t * datap,int * diflocale)8550Sstevel@tonic-gate setup_conversions(pargs_data_t *datap, int *diflocale)
8560Sstevel@tonic-gate {
8570Sstevel@tonic-gate 	char *mylocale = NULL, *mycharset = NULL;
8580Sstevel@tonic-gate 	char *targetlocale = NULL, *targetcharset = NULL;
8590Sstevel@tonic-gate 
8600Sstevel@tonic-gate 	mycharset = safe_strdup(nl_langinfo(CODESET));
8610Sstevel@tonic-gate 
8620Sstevel@tonic-gate 	mylocale = setlocale(LC_CTYPE, NULL);
8630Sstevel@tonic-gate 	if ((mylocale == NULL) || (strcmp(mylocale, "") == 0))
8640Sstevel@tonic-gate 		mylocale = "C";
8650Sstevel@tonic-gate 	mylocale = safe_strdup(mylocale);
8660Sstevel@tonic-gate 
8670Sstevel@tonic-gate 	if (datap->pd_conv_flags & CONV_STRICT_ASCII)
8680Sstevel@tonic-gate 		goto done;
8690Sstevel@tonic-gate 
8700Sstevel@tonic-gate 	/*
8710Sstevel@tonic-gate 	 * If the target's locale is "C" or "POSIX", go fast.
8720Sstevel@tonic-gate 	 */
8730Sstevel@tonic-gate 	if ((strcmp(datap->pd_locale, "C") == 0) ||
8740Sstevel@tonic-gate 	    (strcmp(datap->pd_locale, "POSIX") == 0)) {
8750Sstevel@tonic-gate 		datap->pd_conv_flags |= CONV_STRICT_ASCII;
8760Sstevel@tonic-gate 		goto done;
8770Sstevel@tonic-gate 	}
8780Sstevel@tonic-gate 
8790Sstevel@tonic-gate 	/*
8800Sstevel@tonic-gate 	 * Switch to the victim's locale, and discover its character set.
8810Sstevel@tonic-gate 	 */
8820Sstevel@tonic-gate 	if (setlocale(LC_ALL, datap->pd_locale) == NULL) {
8830Sstevel@tonic-gate 		(void) fprintf(stderr,
8840Sstevel@tonic-gate 		    "%s: Couldn't determine locale of target process.\n",
8850Sstevel@tonic-gate 		    command);
8860Sstevel@tonic-gate 		(void) fprintf(stderr,
8870Sstevel@tonic-gate 		    "%s: Some strings may not be displayed properly.\n",
8880Sstevel@tonic-gate 		    command);
8890Sstevel@tonic-gate 		goto done;
8900Sstevel@tonic-gate 	}
8910Sstevel@tonic-gate 
8920Sstevel@tonic-gate 	/*
8930Sstevel@tonic-gate 	 * Get LC_CTYPE part of target's locale, and its codeset.
8940Sstevel@tonic-gate 	 */
8950Sstevel@tonic-gate 	targetlocale = safe_strdup(setlocale(LC_CTYPE, NULL));
8960Sstevel@tonic-gate 	targetcharset = safe_strdup(nl_langinfo(CODESET));
8970Sstevel@tonic-gate 
8980Sstevel@tonic-gate 	/*
8990Sstevel@tonic-gate 	 * Now go fully back to the pargs user's locale.
9000Sstevel@tonic-gate 	 */
9010Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
9020Sstevel@tonic-gate 
9030Sstevel@tonic-gate 	/*
9040Sstevel@tonic-gate 	 * It's safe to bail here if the lc_ctype of the locales are the
9050Sstevel@tonic-gate 	 * same-- we know that their encodings and characters sets are the same.
9060Sstevel@tonic-gate 	 */
9070Sstevel@tonic-gate 	if (strcmp(targetlocale, mylocale) == 0)
9080Sstevel@tonic-gate 		goto done;
9090Sstevel@tonic-gate 
9100Sstevel@tonic-gate 	*diflocale = 1;
9110Sstevel@tonic-gate 
9120Sstevel@tonic-gate 	/*
9130Sstevel@tonic-gate 	 * If the codeset of the victim matches our codeset then iconv need
9140Sstevel@tonic-gate 	 * not be involved.
9150Sstevel@tonic-gate 	 */
9160Sstevel@tonic-gate 	if (strcmp(mycharset, targetcharset) == 0)
9170Sstevel@tonic-gate 		goto done;
9180Sstevel@tonic-gate 
9190Sstevel@tonic-gate 	if ((datap->pd_iconv = iconv_open(mycharset, targetcharset))
9200Sstevel@tonic-gate 	    == (iconv_t)-1) {
9210Sstevel@tonic-gate 		/*
9220Sstevel@tonic-gate 		 * EINVAL indicates there was no conversion available
9230Sstevel@tonic-gate 		 * from victim charset to mycharset
9240Sstevel@tonic-gate 		 */
9250Sstevel@tonic-gate 		if (errno != EINVAL) {
9260Sstevel@tonic-gate 			(void) fprintf(stderr,
9270Sstevel@tonic-gate 			    "%s: failed to initialize iconv: %s\n",
9280Sstevel@tonic-gate 			    command, strerror(errno));
9290Sstevel@tonic-gate 			exit(1);
9300Sstevel@tonic-gate 		}
9310Sstevel@tonic-gate 		datap->pd_conv_flags |= CONV_STRICT_ASCII;
9320Sstevel@tonic-gate 	} else {
9330Sstevel@tonic-gate 		datap->pd_conv_flags |= CONV_USE_ICONV;
9340Sstevel@tonic-gate 	}
9350Sstevel@tonic-gate done:
9360Sstevel@tonic-gate 	free(mycharset);
9370Sstevel@tonic-gate 	free(mylocale);
9380Sstevel@tonic-gate 	free(targetcharset);
9390Sstevel@tonic-gate 	free(targetlocale);
9400Sstevel@tonic-gate }
9410Sstevel@tonic-gate 
9420Sstevel@tonic-gate static void
cleanup_conversions(pargs_data_t * datap)9430Sstevel@tonic-gate cleanup_conversions(pargs_data_t *datap)
9440Sstevel@tonic-gate {
9450Sstevel@tonic-gate 	if (datap->pd_conv_flags & CONV_USE_ICONV) {
9460Sstevel@tonic-gate 		(void) iconv_close(datap->pd_iconv);
9470Sstevel@tonic-gate 	}
9480Sstevel@tonic-gate }
9490Sstevel@tonic-gate 
9500Sstevel@tonic-gate static char *
convert_run_iconv(pargs_data_t * datap,const char * str)9510Sstevel@tonic-gate convert_run_iconv(pargs_data_t *datap, const char *str)
9520Sstevel@tonic-gate {
9530Sstevel@tonic-gate 	size_t inleft, outleft, bufsz = 64;
9540Sstevel@tonic-gate 	char *outstr, *outstrptr;
9550Sstevel@tonic-gate 	const char *instrptr;
9560Sstevel@tonic-gate 
9570Sstevel@tonic-gate 	for (;;) {
9580Sstevel@tonic-gate 		outstrptr = outstr = safe_zalloc(bufsz + 1);
9590Sstevel@tonic-gate 		outleft = bufsz;
9600Sstevel@tonic-gate 
9610Sstevel@tonic-gate 		/*
9620Sstevel@tonic-gate 		 * Generate the "initial shift state" sequence, placing that
9630Sstevel@tonic-gate 		 * at the head of the string.
9640Sstevel@tonic-gate 		 */
9650Sstevel@tonic-gate 		inleft = 0;
9660Sstevel@tonic-gate 		(void) iconv(datap->pd_iconv, NULL, &inleft,
9670Sstevel@tonic-gate 		    &outstrptr, &outleft);
9680Sstevel@tonic-gate 
9690Sstevel@tonic-gate 		inleft = strlen(str);
9700Sstevel@tonic-gate 		instrptr = str;
9710Sstevel@tonic-gate 		if (iconv(datap->pd_iconv, &instrptr, &inleft, &outstrptr,
9720Sstevel@tonic-gate 		    &outleft) != (size_t)-1) {
9730Sstevel@tonic-gate 			/*
9740Sstevel@tonic-gate 			 * Outstr must be null terminated upon exit from
9750Sstevel@tonic-gate 			 * iconv().
9760Sstevel@tonic-gate 			 */
9770Sstevel@tonic-gate 			*(outstr + (bufsz - outleft)) = '\0';
9780Sstevel@tonic-gate 			break;
9790Sstevel@tonic-gate 		} else if (errno == E2BIG) {
9800Sstevel@tonic-gate 			bufsz *= 2;
9810Sstevel@tonic-gate 			free(outstr);
9820Sstevel@tonic-gate 		} else if ((errno == EILSEQ) || (errno == EINVAL)) {
9830Sstevel@tonic-gate 			free(outstr);
9840Sstevel@tonic-gate 			return (NULL);
9850Sstevel@tonic-gate 		} else {
9860Sstevel@tonic-gate 			/*
9870Sstevel@tonic-gate 			 * iconv() could in theory return EBADF, but that
9880Sstevel@tonic-gate 			 * shouldn't happen.
9890Sstevel@tonic-gate 			 */
9900Sstevel@tonic-gate 			(void) fprintf(stderr,
9910Sstevel@tonic-gate 			    "%s: iconv(3C) failed unexpectedly: %s\n",
9920Sstevel@tonic-gate 			    command, strerror(errno));
9930Sstevel@tonic-gate 
9940Sstevel@tonic-gate 			exit(1);
9950Sstevel@tonic-gate 		}
9960Sstevel@tonic-gate 	}
9970Sstevel@tonic-gate 	return (outstr);
9980Sstevel@tonic-gate }
9990Sstevel@tonic-gate 
10000Sstevel@tonic-gate /*
10010Sstevel@tonic-gate  * Returns a freshly allocated string converted to the local character set,
10020Sstevel@tonic-gate  * removed of unprintable characters.
10030Sstevel@tonic-gate  */
10040Sstevel@tonic-gate static char *
convert_str(pargs_data_t * datap,const char * str,int * unprintable)10050Sstevel@tonic-gate convert_str(pargs_data_t *datap, const char *str, int *unprintable)
10060Sstevel@tonic-gate {
10070Sstevel@tonic-gate 	char *retstr, *tmp;
10080Sstevel@tonic-gate 
10090Sstevel@tonic-gate 	if (datap->pd_conv_flags & CONV_STRICT_ASCII) {
10100Sstevel@tonic-gate 		retstr = unctrl_str_strict_ascii(str, 1, unprintable);
10110Sstevel@tonic-gate 		return (retstr);
10120Sstevel@tonic-gate 	}
10130Sstevel@tonic-gate 
10140Sstevel@tonic-gate 	if ((datap->pd_conv_flags & CONV_USE_ICONV) == 0) {
10150Sstevel@tonic-gate 		/*
10160Sstevel@tonic-gate 		 * If we aren't using iconv(), convert control chars in
10170Sstevel@tonic-gate 		 * the string in pargs' locale, since that is the display
10180Sstevel@tonic-gate 		 * locale.
10190Sstevel@tonic-gate 		 */
10200Sstevel@tonic-gate 		retstr = unctrl_str(str, 1, unprintable);
10210Sstevel@tonic-gate 		return (retstr);
10220Sstevel@tonic-gate 	}
10230Sstevel@tonic-gate 
10240Sstevel@tonic-gate 	/*
10250Sstevel@tonic-gate 	 * The logic here is a bit (ahem) tricky.  Start by converting
10260Sstevel@tonic-gate 	 * unprintable characters *in the target's locale*.  This should
10270Sstevel@tonic-gate 	 * eliminate a variety of unprintable or illegal characters-- in
10280Sstevel@tonic-gate 	 * short, it should leave us with something which iconv() won't
10290Sstevel@tonic-gate 	 * have trouble with.
10300Sstevel@tonic-gate 	 *
10310Sstevel@tonic-gate 	 * After allowing iconv to convert characters as needed, run unctrl
10320Sstevel@tonic-gate 	 * again in pargs' locale-- This time to make sure that any
10330Sstevel@tonic-gate 	 * characters which aren't printable according to the *current*
10340Sstevel@tonic-gate 	 * locale (independent of the current codeset) get taken care of.
10350Sstevel@tonic-gate 	 * Without this second stage, we might (for example) fail to
10360Sstevel@tonic-gate 	 * properly handle characters converted into the 646 character set
10370Sstevel@tonic-gate 	 * (which are 8-bits wide), but which must be displayed in the C
10380Sstevel@tonic-gate 	 * locale (which uses 646, but whose printable characters are a
10390Sstevel@tonic-gate 	 * subset of the 7-bit characters).
10400Sstevel@tonic-gate 	 *
10410Sstevel@tonic-gate 	 * Note that assuming the victim's locale using LC_ALL will be
10420Sstevel@tonic-gate 	 * problematic when pargs' messages are internationalized in the
10430Sstevel@tonic-gate 	 * future (and it calls textdomain(3C)).  In this case, any
10440Sstevel@tonic-gate 	 * error message fprintf'd in unctrl_str() will be in the wrong
10450Sstevel@tonic-gate 	 * LC_MESSAGES class.  We'll cross that bridge when we come to it.
10460Sstevel@tonic-gate 	 */
10470Sstevel@tonic-gate 	(void) setlocale(LC_ALL, datap->pd_locale);
10480Sstevel@tonic-gate 	retstr = unctrl_str(str, 1, unprintable);
10490Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
10500Sstevel@tonic-gate 
10510Sstevel@tonic-gate 	tmp = retstr;
10520Sstevel@tonic-gate 	if ((retstr = convert_run_iconv(datap, retstr)) == NULL) {
10530Sstevel@tonic-gate 		/*
10540Sstevel@tonic-gate 		 * In this (rare but real) case, the iconv() failed even
10550Sstevel@tonic-gate 		 * though we unctrl'd the string.  Treat the original string
10560Sstevel@tonic-gate 		 * (str) as a C locale string and strip it that way.
10570Sstevel@tonic-gate 		 */
10580Sstevel@tonic-gate 		free(tmp);
10590Sstevel@tonic-gate 		return (unctrl_str_strict_ascii(str, 0, unprintable));
10600Sstevel@tonic-gate 	}
10610Sstevel@tonic-gate 
10620Sstevel@tonic-gate 	free(tmp);
10630Sstevel@tonic-gate 	tmp = retstr;
10640Sstevel@tonic-gate 	/*
10650Sstevel@tonic-gate 	 * Run unctrl_str, but make sure not to escape \ characters, which
10660Sstevel@tonic-gate 	 * may have resulted from the first round of unctrl.
10670Sstevel@tonic-gate 	 */
10680Sstevel@tonic-gate 	retstr = unctrl_str(retstr, 0, unprintable);
10690Sstevel@tonic-gate 	free(tmp);
10700Sstevel@tonic-gate 	return (retstr);
10710Sstevel@tonic-gate }
10720Sstevel@tonic-gate 
10730Sstevel@tonic-gate 
10740Sstevel@tonic-gate static void
convert_array(pargs_data_t * datap,char ** arr,size_t count,int * unprintable)10750Sstevel@tonic-gate convert_array(pargs_data_t *datap, char **arr, size_t count, int *unprintable)
10760Sstevel@tonic-gate {
10770Sstevel@tonic-gate 	int i;
10780Sstevel@tonic-gate 	char *tmp;
10790Sstevel@tonic-gate 
10800Sstevel@tonic-gate 	if (arr == NULL)
10810Sstevel@tonic-gate 		return;
10820Sstevel@tonic-gate 
10830Sstevel@tonic-gate 	for (i = 0; i < count; i++) {
10840Sstevel@tonic-gate 		if ((tmp = arr[i]) == NULL)
10850Sstevel@tonic-gate 			continue;
10860Sstevel@tonic-gate 		arr[i] = convert_str(datap, arr[i], unprintable);
10870Sstevel@tonic-gate 		free(tmp);
10880Sstevel@tonic-gate 	}
10890Sstevel@tonic-gate }
10900Sstevel@tonic-gate 
10910Sstevel@tonic-gate /*
10920Sstevel@tonic-gate  * Free data allocated during the gathering phase.
10930Sstevel@tonic-gate  */
10940Sstevel@tonic-gate static void
free_data(pargs_data_t * datap)10950Sstevel@tonic-gate free_data(pargs_data_t *datap)
10960Sstevel@tonic-gate {
10970Sstevel@tonic-gate 	int i;
10980Sstevel@tonic-gate 
10990Sstevel@tonic-gate 	if (datap->pd_argv) {
11000Sstevel@tonic-gate 		for (i = 0; i < datap->pd_argc; i++) {
11010Sstevel@tonic-gate 			if (datap->pd_argv_strs[i] != NULL)
11020Sstevel@tonic-gate 				free(datap->pd_argv_strs[i]);
11030Sstevel@tonic-gate 		}
11040Sstevel@tonic-gate 		free(datap->pd_argv);
11050Sstevel@tonic-gate 		free(datap->pd_argv_strs);
11060Sstevel@tonic-gate 	}
11070Sstevel@tonic-gate 
11080Sstevel@tonic-gate 	if (datap->pd_envp) {
11090Sstevel@tonic-gate 		for (i = 0; i < datap->pd_envc; i++) {
11100Sstevel@tonic-gate 			if (datap->pd_envp_strs[i] != NULL)
11110Sstevel@tonic-gate 				free(datap->pd_envp_strs[i]);
11120Sstevel@tonic-gate 		}
11130Sstevel@tonic-gate 		free(datap->pd_envp);
11140Sstevel@tonic-gate 		free(datap->pd_envp_strs);
11150Sstevel@tonic-gate 	}
11160Sstevel@tonic-gate 
11170Sstevel@tonic-gate 	if (datap->pd_auxv) {
11180Sstevel@tonic-gate 		for (i = 0; i < datap->pd_auxc; i++) {
11190Sstevel@tonic-gate 			if (datap->pd_auxv_strs[i] != NULL)
11200Sstevel@tonic-gate 				free(datap->pd_auxv_strs[i]);
11210Sstevel@tonic-gate 		}
11220Sstevel@tonic-gate 		free(datap->pd_auxv);
11230Sstevel@tonic-gate 		free(datap->pd_auxv_strs);
11240Sstevel@tonic-gate 	}
11250Sstevel@tonic-gate }
11260Sstevel@tonic-gate 
11270Sstevel@tonic-gate static void
print_args(pargs_data_t * datap)11280Sstevel@tonic-gate print_args(pargs_data_t *datap)
11290Sstevel@tonic-gate {
11300Sstevel@tonic-gate 	int i;
11310Sstevel@tonic-gate 
11320Sstevel@tonic-gate 	if (datap->pd_argv == NULL) {
11330Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: failed to read argv[]\n", command);
11340Sstevel@tonic-gate 		return;
11350Sstevel@tonic-gate 	}
11360Sstevel@tonic-gate 
11370Sstevel@tonic-gate 	for (i = 0; i < datap->pd_argc; i++) {
11380Sstevel@tonic-gate 		(void) printf("argv[%d]: ", i);
11390Sstevel@tonic-gate 		if (datap->pd_argv[i] == NULL) {
11400Sstevel@tonic-gate 			(void) printf("<NULL>\n");
11410Sstevel@tonic-gate 		} else if (datap->pd_argv_strs[i] == NULL) {
11420Sstevel@tonic-gate 			(void) printf("<0x%0*lx>\n",
11430Sstevel@tonic-gate 			    (dmodel == PR_MODEL_LP64)? 16 : 8,
11440Sstevel@tonic-gate 			    (long)datap->pd_argv[i]);
11450Sstevel@tonic-gate 		} else {
11460Sstevel@tonic-gate 			(void) printf("%s\n", datap->pd_argv_strs[i]);
11470Sstevel@tonic-gate 		}
11480Sstevel@tonic-gate 	}
11490Sstevel@tonic-gate }
11500Sstevel@tonic-gate 
11510Sstevel@tonic-gate static void
print_env(pargs_data_t * datap)11520Sstevel@tonic-gate print_env(pargs_data_t *datap)
11530Sstevel@tonic-gate {
11540Sstevel@tonic-gate 	int i;
11550Sstevel@tonic-gate 
11560Sstevel@tonic-gate 	if (datap->pd_envp == NULL) {
11570Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: failed to read envp[]\n", command);
11580Sstevel@tonic-gate 		return;
11590Sstevel@tonic-gate 	}
11600Sstevel@tonic-gate 
11610Sstevel@tonic-gate 	for (i = 0; i < datap->pd_envc; i++) {
11620Sstevel@tonic-gate 		(void) printf("envp[%d]: ", i);
11630Sstevel@tonic-gate 		if (datap->pd_envp[i] == 0) {
11640Sstevel@tonic-gate 			break;
11650Sstevel@tonic-gate 		} else if (datap->pd_envp_strs[i] == NULL) {
11660Sstevel@tonic-gate 			(void) printf("<0x%0*lx>\n",
11670Sstevel@tonic-gate 			    (dmodel == PR_MODEL_LP64)? 16 : 8,
11680Sstevel@tonic-gate 			    (long)datap->pd_envp[i]);
11690Sstevel@tonic-gate 		} else {
11700Sstevel@tonic-gate 			(void) printf("%s\n", datap->pd_envp_strs[i]);
11710Sstevel@tonic-gate 		}
11720Sstevel@tonic-gate 	}
11730Sstevel@tonic-gate }
11740Sstevel@tonic-gate 
11750Sstevel@tonic-gate static int
print_cmdline(pargs_data_t * datap)11760Sstevel@tonic-gate print_cmdline(pargs_data_t *datap)
11770Sstevel@tonic-gate {
11780Sstevel@tonic-gate 	int i;
11790Sstevel@tonic-gate 
11800Sstevel@tonic-gate 	/*
11810Sstevel@tonic-gate 	 * Go through and check to see if we have valid data.  If not, print
11820Sstevel@tonic-gate 	 * an error message and bail.
11830Sstevel@tonic-gate 	 */
11840Sstevel@tonic-gate 	for (i = 0; i < datap->pd_argc; i++) {
11850Sstevel@tonic-gate 		if (datap->pd_argv[i] == NULL ||
11860Sstevel@tonic-gate 		    datap->pd_argv_strs[i] == NULL) {
11870Sstevel@tonic-gate 			(void) fprintf(stderr, "%s: target has corrupted "
11880Sstevel@tonic-gate 			    "argument list\n", command);
11890Sstevel@tonic-gate 			return (1);
11900Sstevel@tonic-gate 		}
11910Sstevel@tonic-gate 
11920Sstevel@tonic-gate 		datap->pd_argv_strs[i] =
11930Sstevel@tonic-gate 		    quote_string(datap, datap->pd_argv_strs[i]);
11940Sstevel@tonic-gate 	}
11950Sstevel@tonic-gate 
11960Sstevel@tonic-gate 	if (datap->pd_execname == NULL) {
11970Sstevel@tonic-gate 		(void) fprintf(stderr, "%s: cannot determine name of "
11980Sstevel@tonic-gate 		    "executable\n", command);
11990Sstevel@tonic-gate 		return (1);
12000Sstevel@tonic-gate 	}
12010Sstevel@tonic-gate 
12020Sstevel@tonic-gate 	(void) printf("%s ", datap->pd_execname);
12030Sstevel@tonic-gate 
12040Sstevel@tonic-gate 	for (i = 1; i < datap->pd_argc; i++)
12050Sstevel@tonic-gate 		(void) printf("%s ", datap->pd_argv_strs[i]);
12060Sstevel@tonic-gate 
12070Sstevel@tonic-gate 	(void) printf("\n");
12080Sstevel@tonic-gate 
12090Sstevel@tonic-gate 	return (0);
12100Sstevel@tonic-gate }
12110Sstevel@tonic-gate 
12120Sstevel@tonic-gate static void
print_auxv(pargs_data_t * datap)12130Sstevel@tonic-gate print_auxv(pargs_data_t *datap)
12140Sstevel@tonic-gate {
12150Sstevel@tonic-gate 	int i;
12160Sstevel@tonic-gate 	const auxv_t *pa;
12170Sstevel@tonic-gate 
12180Sstevel@tonic-gate 	/*
12190Sstevel@tonic-gate 	 * Print the names and values of all the aux vector entries.
12200Sstevel@tonic-gate 	 */
12210Sstevel@tonic-gate 	for (i = 0; i < datap->pd_auxc; i++) {
12220Sstevel@tonic-gate 		char type[32];
12230Sstevel@tonic-gate 		char decode[PATH_MAX];
12240Sstevel@tonic-gate 		struct aux_id *aux;
12250Sstevel@tonic-gate 		long v;
12260Sstevel@tonic-gate 		pa = &datap->pd_auxv[i];
12270Sstevel@tonic-gate 
12280Sstevel@tonic-gate 		aux = aux_find(pa->a_type);
12290Sstevel@tonic-gate 		v = (long)pa->a_un.a_val;
12300Sstevel@tonic-gate 
12310Sstevel@tonic-gate 		if (aux != NULL) {
12320Sstevel@tonic-gate 			/*
12330Sstevel@tonic-gate 			 * Fetch aux vector type string and decoded
12340Sstevel@tonic-gate 			 * representation of the value.
12350Sstevel@tonic-gate 			 */
12360Sstevel@tonic-gate 			(void) strlcpy(type, aux->aux_name, sizeof (type));
12370Sstevel@tonic-gate 			aux->aux_decode(v, datap->pd_auxv_strs[i],
12380Sstevel@tonic-gate 			    sizeof (decode), decode);
12390Sstevel@tonic-gate 		} else {
12400Sstevel@tonic-gate 			(void) snprintf(type, sizeof (type), "%d", pa->a_type);
12410Sstevel@tonic-gate 			decode[0] = '\0';
12420Sstevel@tonic-gate 		}
12430Sstevel@tonic-gate 
12440Sstevel@tonic-gate 		(void) printf("%-*s 0x%0*lx %s\n", MAX_AT_NAME_LEN, type,
12450Sstevel@tonic-gate 		    (dmodel == PR_MODEL_LP64)? 16 : 8, v, decode);
12460Sstevel@tonic-gate 	}
12470Sstevel@tonic-gate }
12480Sstevel@tonic-gate 
12490Sstevel@tonic-gate int
main(int argc,char * argv[])12500Sstevel@tonic-gate main(int argc, char *argv[])
12510Sstevel@tonic-gate {
12520Sstevel@tonic-gate 	int aflag = 0, cflag = 0, eflag = 0, xflag = 0, lflag = 0;
12530Sstevel@tonic-gate 	int errflg = 0, retc = 0;
12540Sstevel@tonic-gate 	int opt;
12550Sstevel@tonic-gate 	int error = 1;
12560Sstevel@tonic-gate 	core_content_t content = 0;
12570Sstevel@tonic-gate 
12580Sstevel@tonic-gate 	(void) setlocale(LC_ALL, "");
12590Sstevel@tonic-gate 
12600Sstevel@tonic-gate 	if ((command = strrchr(argv[0], '/')) != NULL)
12610Sstevel@tonic-gate 		command++;
12620Sstevel@tonic-gate 	else
12630Sstevel@tonic-gate 		command = argv[0];
12640Sstevel@tonic-gate 
12650Sstevel@tonic-gate 	while ((opt = getopt(argc, argv, "acelxF")) != EOF) {
12660Sstevel@tonic-gate 		switch (opt) {
12670Sstevel@tonic-gate 		case 'a':		/* show process arguments */
12680Sstevel@tonic-gate 			content |= CC_CONTENT_STACK;
12690Sstevel@tonic-gate 			aflag++;
12700Sstevel@tonic-gate 			break;
12710Sstevel@tonic-gate 		case 'c':		/* force 7-bit ascii */
12720Sstevel@tonic-gate 			cflag++;
12730Sstevel@tonic-gate 			break;
12740Sstevel@tonic-gate 		case 'e':		/* show environment variables */
12750Sstevel@tonic-gate 			content |= CC_CONTENT_STACK;
12760Sstevel@tonic-gate 			eflag++;
12770Sstevel@tonic-gate 			break;
12780Sstevel@tonic-gate 		case 'l':
12790Sstevel@tonic-gate 			lflag++;
12800Sstevel@tonic-gate 			aflag++;	/* -l implies -a */
12810Sstevel@tonic-gate 			break;
12820Sstevel@tonic-gate 		case 'x':		/* show aux vector entries */
12830Sstevel@tonic-gate 			xflag++;
12840Sstevel@tonic-gate 			break;
12850Sstevel@tonic-gate 		case 'F':
12860Sstevel@tonic-gate 			/*
12870Sstevel@tonic-gate 			 * Since we open the process read-only, there is no need
12880Sstevel@tonic-gate 			 * for the -F flag.  It's a documented flag, so we
12890Sstevel@tonic-gate 			 * consume it silently.
12900Sstevel@tonic-gate 			 */
12910Sstevel@tonic-gate 			break;
12920Sstevel@tonic-gate 		default:
12930Sstevel@tonic-gate 			errflg++;
12940Sstevel@tonic-gate 			break;
12950Sstevel@tonic-gate 		}
12960Sstevel@tonic-gate 	}
12970Sstevel@tonic-gate 
12980Sstevel@tonic-gate 	/* -a is the default if no options are specified */
12990Sstevel@tonic-gate 	if ((aflag + eflag + xflag + lflag) == 0) {
13000Sstevel@tonic-gate 		aflag++;
13010Sstevel@tonic-gate 		content |= CC_CONTENT_STACK;
13020Sstevel@tonic-gate 	}
13030Sstevel@tonic-gate 
13040Sstevel@tonic-gate 	/* -l cannot be used with the -x or -e flags */
13050Sstevel@tonic-gate 	if (lflag && (xflag || eflag)) {
13060Sstevel@tonic-gate 		(void) fprintf(stderr, "-l is incompatible with -x and -e\n");
13070Sstevel@tonic-gate 		errflg++;
13080Sstevel@tonic-gate 	}
13090Sstevel@tonic-gate 
13100Sstevel@tonic-gate 	argc -= optind;
13110Sstevel@tonic-gate 	argv += optind;
13120Sstevel@tonic-gate 
13130Sstevel@tonic-gate 	if (errflg || argc <= 0) {
13140Sstevel@tonic-gate 		(void) fprintf(stderr,
13150Sstevel@tonic-gate 		    "usage:  %s [-acexF] { pid | core } ...\n"
13160Sstevel@tonic-gate 		    "  (show process arguments and environment)\n"
13170Sstevel@tonic-gate 		    "  -a: show process arguments (default)\n"
13180Sstevel@tonic-gate 		    "  -c: interpret characters as 7-bit ascii regardless of "
13190Sstevel@tonic-gate 		    "locale\n"
13200Sstevel@tonic-gate 		    "  -e: show environment variables\n"
13210Sstevel@tonic-gate 		    "  -l: display arguments as command line\n"
13220Sstevel@tonic-gate 		    "  -x: show aux vector entries\n"
13230Sstevel@tonic-gate 		    "  -F: force grabbing of the target process\n", command);
13240Sstevel@tonic-gate 		return (2);
13250Sstevel@tonic-gate 	}
13260Sstevel@tonic-gate 
13270Sstevel@tonic-gate 	while (argc-- > 0) {
13280Sstevel@tonic-gate 		char *arg;
13290Sstevel@tonic-gate 		int gret, r;
13300Sstevel@tonic-gate 		psinfo_t psinfo;
13310Sstevel@tonic-gate 		char *psargs_conv;
13320Sstevel@tonic-gate 		struct ps_prochandle *Pr;
13330Sstevel@tonic-gate 		pargs_data_t datap;
13340Sstevel@tonic-gate 		char *info;
13350Sstevel@tonic-gate 		size_t info_sz;
13360Sstevel@tonic-gate 		int pstate;
13370Sstevel@tonic-gate 		char execname[PATH_MAX];
13380Sstevel@tonic-gate 		int unprintable;
13390Sstevel@tonic-gate 		int diflocale;
13400Sstevel@tonic-gate 
13410Sstevel@tonic-gate 		(void) fflush(stdout);
13420Sstevel@tonic-gate 		arg = *argv++;
13430Sstevel@tonic-gate 
13440Sstevel@tonic-gate 		/*
13450Sstevel@tonic-gate 		 * Suppress extra blanks lines if we've encountered processes
13460Sstevel@tonic-gate 		 * which can't be opened.
13470Sstevel@tonic-gate 		 */
13480Sstevel@tonic-gate 		if (error == 0) {
13490Sstevel@tonic-gate 			(void) printf("\n");
13500Sstevel@tonic-gate 		}
13510Sstevel@tonic-gate 		error = 0;
13520Sstevel@tonic-gate 
13530Sstevel@tonic-gate 		/*
13540Sstevel@tonic-gate 		 * First grab just the psinfo information, in case this
13550Sstevel@tonic-gate 		 * process is a zombie (in which case proc_arg_grab() will
13560Sstevel@tonic-gate 		 * fail).  If so, print a nice message and continue.
13570Sstevel@tonic-gate 		 */
13580Sstevel@tonic-gate 		if (proc_arg_psinfo(arg, PR_ARG_ANY, &psinfo,
13590Sstevel@tonic-gate 		    &gret) == -1) {
13600Sstevel@tonic-gate 			(void) fprintf(stderr, "%s: cannot examine %s: %s\n",
13610Sstevel@tonic-gate 			    command, arg, Pgrab_error(gret));
13620Sstevel@tonic-gate 			retc++;
13630Sstevel@tonic-gate 			error = 1;
13640Sstevel@tonic-gate 			continue;
13650Sstevel@tonic-gate 		}
13660Sstevel@tonic-gate 
13670Sstevel@tonic-gate 		if (psinfo.pr_nlwp == 0) {
13680Sstevel@tonic-gate 			(void) printf("%d: <defunct>\n", (int)psinfo.pr_pid);
13690Sstevel@tonic-gate 			continue;
13700Sstevel@tonic-gate 		}
13710Sstevel@tonic-gate 
13720Sstevel@tonic-gate 		/*
13730Sstevel@tonic-gate 		 * If process is a "system" process (like pageout), just
13740Sstevel@tonic-gate 		 * print its psargs and continue on.
13750Sstevel@tonic-gate 		 */
13760Sstevel@tonic-gate 		if (psinfo.pr_size == 0 && psinfo.pr_rssize == 0) {
13770Sstevel@tonic-gate 			proc_unctrl_psinfo(&psinfo);
13780Sstevel@tonic-gate 			if (!lflag)
13790Sstevel@tonic-gate 				(void) printf("%d: ", (int)psinfo.pr_pid);
13800Sstevel@tonic-gate 			(void) printf("%s\n", psinfo.pr_psargs);
13810Sstevel@tonic-gate 			continue;
13820Sstevel@tonic-gate 		}
13830Sstevel@tonic-gate 
13840Sstevel@tonic-gate 		/*
13850Sstevel@tonic-gate 		 * Open the process readonly, since we do not need to write to
13860Sstevel@tonic-gate 		 * the control file.
13870Sstevel@tonic-gate 		 */
13880Sstevel@tonic-gate 		if ((Pr = proc_arg_grab(arg, PR_ARG_ANY, PGRAB_RDONLY,
13890Sstevel@tonic-gate 		    &gret)) == NULL) {
13900Sstevel@tonic-gate 			(void) fprintf(stderr, "%s: cannot examine %s: %s\n",
13910Sstevel@tonic-gate 			    command, arg, Pgrab_error(gret));
13920Sstevel@tonic-gate 			retc++;
13930Sstevel@tonic-gate 			error = 1;
13940Sstevel@tonic-gate 			continue;
13950Sstevel@tonic-gate 		}
13960Sstevel@tonic-gate 
13970Sstevel@tonic-gate 		pstate = Pstate(Pr);
13980Sstevel@tonic-gate 
13990Sstevel@tonic-gate 		if (pstate == PS_DEAD &&
14000Sstevel@tonic-gate 		    (Pcontent(Pr) & content) != content) {
14010Sstevel@tonic-gate 			(void) fprintf(stderr, "%s: core '%s' has "
14020Sstevel@tonic-gate 			    "insufficient content\n", command, arg);
14030Sstevel@tonic-gate 			retc++;
14040Sstevel@tonic-gate 			continue;
14050Sstevel@tonic-gate 		}
14060Sstevel@tonic-gate 
14070Sstevel@tonic-gate 		/*
14080Sstevel@tonic-gate 		 * If malloc() fails, we return here so that we can let go
14090Sstevel@tonic-gate 		 * of the victim, restore our locale, print a message,
14100Sstevel@tonic-gate 		 * then exit.
14110Sstevel@tonic-gate 		 */
14120Sstevel@tonic-gate 		if ((r = setjmp(env)) != 0) {
14130Sstevel@tonic-gate 			Prelease(Pr, 0);
14140Sstevel@tonic-gate 			(void) setlocale(LC_ALL, "");
14150Sstevel@tonic-gate 			(void) fprintf(stderr, "%s: out of memory: %s\n",
14160Sstevel@tonic-gate 			    command, strerror(r));
14170Sstevel@tonic-gate 			return (1);
14180Sstevel@tonic-gate 		}
14190Sstevel@tonic-gate 
14200Sstevel@tonic-gate 		dmodel = Pstatus(Pr)->pr_dmodel;
14210Sstevel@tonic-gate 		bzero(&datap, sizeof (datap));
14220Sstevel@tonic-gate 		bcopy(Ppsinfo(Pr), &psinfo, sizeof (psinfo_t));
14230Sstevel@tonic-gate 		datap.pd_proc = Pr;
14240Sstevel@tonic-gate 		datap.pd_psinfo = &psinfo;
14250Sstevel@tonic-gate 
14260Sstevel@tonic-gate 		if (cflag)
14270Sstevel@tonic-gate 			datap.pd_conv_flags |= CONV_STRICT_ASCII;
14280Sstevel@tonic-gate 
14290Sstevel@tonic-gate 		/*
14300Sstevel@tonic-gate 		 * Strip control characters, then record process summary in
14310Sstevel@tonic-gate 		 * a buffer, since we don't want to print anything out until
14320Sstevel@tonic-gate 		 * after we release the process.
14330Sstevel@tonic-gate 		 */
14340Sstevel@tonic-gate 
14350Sstevel@tonic-gate 		/*
14360Sstevel@tonic-gate 		 * The process is neither a system process nor defunct.
14370Sstevel@tonic-gate 		 *
14380Sstevel@tonic-gate 		 * Do printing and post-processing (like name lookups) after
14390Sstevel@tonic-gate 		 * gathering the raw data from the process and releasing it.
14400Sstevel@tonic-gate 		 * This way, we don't deadlock on (for example) name lookup
14410Sstevel@tonic-gate 		 * if we grabbed the nscd and do 'pargs -x'.
14420Sstevel@tonic-gate 		 *
14430Sstevel@tonic-gate 		 * We always fetch the environment of the target, so that we
14440Sstevel@tonic-gate 		 * can make an educated guess about its locale.
14450Sstevel@tonic-gate 		 */
14460Sstevel@tonic-gate 		get_env(&datap);
14470Sstevel@tonic-gate 		if (aflag != 0)
14480Sstevel@tonic-gate 			get_args(&datap);
14490Sstevel@tonic-gate 		if (xflag != 0)
14500Sstevel@tonic-gate 			get_auxv(&datap);
14510Sstevel@tonic-gate 
14520Sstevel@tonic-gate 		/*
14530Sstevel@tonic-gate 		 * If malloc() fails after this poiint, we return here to
14540Sstevel@tonic-gate 		 * restore our locale and print a message.  If we don't
14550Sstevel@tonic-gate 		 * reset this, we might erroneously try to Prelease a process
14560Sstevel@tonic-gate 		 * twice.
14570Sstevel@tonic-gate 		 */
14580Sstevel@tonic-gate 		if ((r = setjmp(env)) != 0) {
14590Sstevel@tonic-gate 			(void) setlocale(LC_ALL, "");
14600Sstevel@tonic-gate 			(void) fprintf(stderr, "%s: out of memory: %s\n",
14610Sstevel@tonic-gate 			    command, strerror(r));
14620Sstevel@tonic-gate 			return (1);
14630Sstevel@tonic-gate 		}
14640Sstevel@tonic-gate 
14650Sstevel@tonic-gate 		/*
14660Sstevel@tonic-gate 		 * For the -l option, we need a proper name for this executable
14670Sstevel@tonic-gate 		 * before we release it.
14680Sstevel@tonic-gate 		 */
14690Sstevel@tonic-gate 		if (lflag)
14700Sstevel@tonic-gate 			datap.pd_execname = Pexecname(Pr, execname,
14710Sstevel@tonic-gate 			    sizeof (execname));
14720Sstevel@tonic-gate 
14730Sstevel@tonic-gate 		Prelease(Pr, 0);
14740Sstevel@tonic-gate 
14750Sstevel@tonic-gate 		/*
14760Sstevel@tonic-gate 		 * Crawl through the environment to determine the locale of
14770Sstevel@tonic-gate 		 * the target.
14780Sstevel@tonic-gate 		 */
14790Sstevel@tonic-gate 		lookup_locale(&datap);
14800Sstevel@tonic-gate 		diflocale = 0;
14810Sstevel@tonic-gate 		setup_conversions(&datap, &diflocale);
14820Sstevel@tonic-gate 
14830Sstevel@tonic-gate 		if (lflag != 0) {
14840Sstevel@tonic-gate 			unprintable = 0;
14850Sstevel@tonic-gate 			convert_array(&datap, datap.pd_argv_strs,
14860Sstevel@tonic-gate 			    datap.pd_argc, &unprintable);
14870Sstevel@tonic-gate 			if (diflocale)
14880Sstevel@tonic-gate 				(void) fprintf(stderr, "%s: Warning, target "
14890Sstevel@tonic-gate 				    "locale differs from current locale\n",
14900Sstevel@tonic-gate 				    command);
14910Sstevel@tonic-gate 			else if (unprintable)
14920Sstevel@tonic-gate 				(void) fprintf(stderr, "%s: Warning, command "
14930Sstevel@tonic-gate 				    "line contains unprintable characters\n",
14940Sstevel@tonic-gate 				    command);
14950Sstevel@tonic-gate 
14960Sstevel@tonic-gate 			retc += print_cmdline(&datap);
14970Sstevel@tonic-gate 		} else {
14980Sstevel@tonic-gate 			psargs_conv = convert_str(&datap, psinfo.pr_psargs,
14990Sstevel@tonic-gate 			    &unprintable);
15000Sstevel@tonic-gate 			info_sz = strlen(psargs_conv) + MAXPATHLEN + 32 + 1;
15010Sstevel@tonic-gate 			info = malloc(info_sz);
15020Sstevel@tonic-gate 			if (pstate == PS_DEAD) {
15030Sstevel@tonic-gate 				(void) snprintf(info, info_sz,
15040Sstevel@tonic-gate 				    "core '%s' of %d:\t%s\n",
15050Sstevel@tonic-gate 				    arg, (int)psinfo.pr_pid, psargs_conv);
15060Sstevel@tonic-gate 			} else {
15070Sstevel@tonic-gate 				(void) snprintf(info, info_sz, "%d:\t%s\n",
15080Sstevel@tonic-gate 				    (int)psinfo.pr_pid, psargs_conv);
15090Sstevel@tonic-gate 			}
15100Sstevel@tonic-gate 			(void) printf("%s", info);
15110Sstevel@tonic-gate 			free(info);
15120Sstevel@tonic-gate 			free(psargs_conv);
15130Sstevel@tonic-gate 
15140Sstevel@tonic-gate 			if (aflag != 0) {
15150Sstevel@tonic-gate 				convert_array(&datap, datap.pd_argv_strs,
15160Sstevel@tonic-gate 				    datap.pd_argc, &unprintable);
15170Sstevel@tonic-gate 				print_args(&datap);
15180Sstevel@tonic-gate 				if (eflag || xflag)
15190Sstevel@tonic-gate 					(void) printf("\n");
15200Sstevel@tonic-gate 			}
15210Sstevel@tonic-gate 
15220Sstevel@tonic-gate 			if (eflag != 0) {
15230Sstevel@tonic-gate 				convert_array(&datap, datap.pd_envp_strs,
15240Sstevel@tonic-gate 				    datap.pd_envc, &unprintable);
15250Sstevel@tonic-gate 				print_env(&datap);
15260Sstevel@tonic-gate 				if (xflag)
15270Sstevel@tonic-gate 					(void) printf("\n");
15280Sstevel@tonic-gate 			}
15290Sstevel@tonic-gate 
15300Sstevel@tonic-gate 			if (xflag != 0) {
15310Sstevel@tonic-gate 				convert_array(&datap, datap.pd_auxv_strs,
15320Sstevel@tonic-gate 				    datap.pd_auxc, &unprintable);
15330Sstevel@tonic-gate 				print_auxv(&datap);
15340Sstevel@tonic-gate 			}
15350Sstevel@tonic-gate 		}
15360Sstevel@tonic-gate 
15370Sstevel@tonic-gate 		cleanup_conversions(&datap);
15380Sstevel@tonic-gate 		free_data(&datap);
15390Sstevel@tonic-gate 	}
15400Sstevel@tonic-gate 
15410Sstevel@tonic-gate 	return (retc != 0 ? 1 : 0);
15420Sstevel@tonic-gate }
1543