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