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