pargs.c revision 4642:d7554fc0577a
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
76typedef 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
97static char *command;
98static 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
103static jmp_buf env;			/* malloc failure handling */
104
105static void *
106safe_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
122static char *
123safe_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 */
143static uchar_t
144get_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
167static char *
168unctrl_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 */
204static char *
205unctrl_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 */
318static int
319issafe_ascii(char c)
320{
321	return (isalnum(c) || strchr("_.-/@:,", c) != NULL);
322}
323
324static int
325issafe(wchar_t wc)
326{
327	return ((iswgraph(wc) && !iswpunct(wc)) ||
328	    wschr(L"_.-/@:,", wc) != NULL);
329}
330
331/*ARGSUSED*/
332static char *
333quote_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
389static char *
390quote_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 */
475static void
476lookup_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 */
549static char *
550extract_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 */
576static ssize_t
577read_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 */
604static void
605get_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*/
630static int
631build_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
648static void
649get_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*/
668static void
669at_null(long val, char *instr, size_t n, char *str)
670{
671	str[0] = '\0';
672}
673
674/*ARGSUSED*/
675static void
676at_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*/
693static void
694at_hwcap(long val, char *instr, size_t n, char *str)
695{
696#if defined(__sparc) || defined(__sparcv9)
697	(void) hwcap_1_val2str(val, str, n, CAP_FMT_PIPSPACE, EM_SPARC);
698
699#elif defined(__i386) || defined(__amd64)
700	(void) hwcap_1_val2str(val, str, n, CAP_FMT_PIPSPACE, EM_386);
701#else
702#error	"port me"
703#endif
704}
705
706/*ARGSUSED*/
707static void
708at_uid(long val, char *instr, size_t n, char *str)
709{
710	struct passwd *pw = getpwuid((uid_t)val);
711
712	if ((pw == NULL) || (pw->pw_name == NULL))
713		str[0] = '\0';
714	else
715		(void) snprintf(str, n, "%lu(%s)", val, pw->pw_name);
716}
717
718
719/*ARGSUSED*/
720static void
721at_gid(long val, char *instr, size_t n, char *str)
722{
723	struct group *gr = getgrgid((gid_t)val);
724
725	if ((gr == NULL) || (gr->gr_name == NULL))
726		str[0] = '\0';
727	else
728		(void) snprintf(str, n, "%lu(%s)", val, gr->gr_name);
729}
730
731static struct auxfl {
732	int af_flag;
733	const char *af_name;
734} auxfl[] = {
735	{ AF_SUN_SETUGID,	"setugid" },
736};
737
738/*ARGSUSED*/
739static void
740at_flags(long val, char *instr, size_t n, char *str)
741{
742	int i;
743
744	*str = '\0';
745
746	for (i = 0; i < sizeof (auxfl)/sizeof (struct auxfl); i++) {
747		if ((val & auxfl[i].af_flag) != 0) {
748			if (*str != '\0')
749				(void) strlcat(str, ",", n);
750			(void) strlcat(str, auxfl[i].af_name, n);
751		}
752	}
753}
754
755#define	MAX_AT_NAME_LEN	15
756
757struct aux_id {
758	int aux_type;
759	const char *aux_name;
760	void (*aux_decode)(long, char *, size_t, char *);
761};
762
763static struct aux_id aux_arr[] = {
764	{ AT_NULL,		"AT_NULL",		at_null	},
765	{ AT_IGNORE,		"AT_IGNORE",		at_null	},
766	{ AT_EXECFD,		"AT_EXECFD",		at_null	},
767	{ AT_PHDR,		"AT_PHDR",		at_null	},
768	{ AT_PHENT,		"AT_PHENT",		at_null	},
769	{ AT_PHNUM,		"AT_PHNUM",		at_null	},
770	{ AT_PAGESZ,		"AT_PAGESZ",		at_null	},
771	{ AT_BASE,		"AT_BASE",		at_null	},
772	{ AT_FLAGS,		"AT_FLAGS",		at_null	},
773	{ AT_ENTRY,		"AT_ENTRY",		at_null	},
774	{ AT_SUN_UID,		"AT_SUN_UID",		at_uid	},
775	{ AT_SUN_RUID,		"AT_SUN_RUID",		at_uid	},
776	{ AT_SUN_GID,		"AT_SUN_GID",		at_gid	},
777	{ AT_SUN_RGID,		"AT_SUN_RGID",		at_gid	},
778	{ AT_SUN_LDELF,		"AT_SUN_LDELF",		at_null	},
779	{ AT_SUN_LDSHDR,	"AT_SUN_LDSHDR",	at_null	},
780	{ AT_SUN_LDNAME,	"AT_SUN_LDNAME",	at_null	},
781	{ AT_SUN_LPAGESZ,	"AT_SUN_LPAGESZ",	at_null	},
782	{ AT_SUN_PLATFORM,	"AT_SUN_PLATFORM",	at_str	},
783	{ AT_SUN_EXECNAME,	"AT_SUN_EXECNAME",	at_str	},
784	{ AT_SUN_HWCAP,		"AT_SUN_HWCAP",		at_hwcap },
785	{ AT_SUN_IFLUSH,	"AT_SUN_IFLUSH",	at_null	},
786	{ AT_SUN_CPU,		"AT_SUN_CPU",		at_null	},
787	{ AT_SUN_MMU,		"AT_SUN_MMU",		at_null	},
788	{ AT_SUN_LDDATA,	"AT_SUN_LDDATA",	at_null	},
789	{ AT_SUN_AUXFLAGS,	"AT_SUN_AUXFLAGS",	at_flags },
790	{ AT_SUN_EMULATOR,	"AT_SUN_EMULATOR",	at_str	},
791	{ AT_SUN_BRANDNAME,	"AT_SUN_BRANDNAME",	at_str	},
792	{ AT_SUN_BRAND_AUX1,	"AT_SUN_BRAND_AUX1",	at_null	},
793	{ AT_SUN_BRAND_AUX2,	"AT_SUN_BRAND_AUX2",	at_null	},
794	{ AT_SUN_BRAND_AUX3,	"AT_SUN_BRAND_AUX3",	at_null	}
795};
796
797#define	N_AT_ENTS (sizeof (aux_arr) / sizeof (struct aux_id))
798
799/*
800 * Return the aux_id entry for the given aux type; returns NULL if not found.
801 */
802static struct aux_id *
803aux_find(int type)
804{
805	int i;
806
807	for (i = 0; i < N_AT_ENTS; i++) {
808		if (type == aux_arr[i].aux_type)
809			return (&aux_arr[i]);
810	}
811
812	return (NULL);
813}
814
815static void
816get_auxv(pargs_data_t *datap)
817{
818	int i;
819	const auxv_t *auxvp;
820
821	/*
822	 * Fetch the aux vector from the target process.
823	 */
824	if (ps_pauxv(datap->pd_proc, &auxvp) != PS_OK)
825		return;
826
827	for (i = 0; auxvp[i].a_type != AT_NULL; i++)
828		continue;
829
830	datap->pd_auxc = i;
831	datap->pd_auxv = safe_zalloc(i * sizeof (auxv_t));
832	bcopy(auxvp, datap->pd_auxv, i * sizeof (auxv_t));
833
834	datap->pd_auxv_strs = safe_zalloc(datap->pd_auxc * sizeof (char *));
835	for (i = 0; i < datap->pd_auxc; i++) {
836		struct aux_id *aux = aux_find(datap->pd_auxv[i].a_type);
837
838		/*
839		 * Grab strings for those entries which have a string-decoder.
840		 */
841		if ((aux != NULL) && (aux->aux_decode == at_str)) {
842			datap->pd_auxv_strs[i] =
843			    extract_string(datap, datap->pd_auxv[i].a_un.a_val);
844		}
845	}
846}
847
848/*
849 * Prepare to convert characters in the victim's character set into user's
850 * character set.
851 */
852static void
853setup_conversions(pargs_data_t *datap, int *diflocale)
854{
855	char *mylocale = NULL, *mycharset = NULL;
856	char *targetlocale = NULL, *targetcharset = NULL;
857
858	mycharset = safe_strdup(nl_langinfo(CODESET));
859
860	mylocale = setlocale(LC_CTYPE, NULL);
861	if ((mylocale == NULL) || (strcmp(mylocale, "") == 0))
862		mylocale = "C";
863	mylocale = safe_strdup(mylocale);
864
865	if (datap->pd_conv_flags & CONV_STRICT_ASCII)
866		goto done;
867
868	/*
869	 * If the target's locale is "C" or "POSIX", go fast.
870	 */
871	if ((strcmp(datap->pd_locale, "C") == 0) ||
872	    (strcmp(datap->pd_locale, "POSIX") == 0)) {
873		datap->pd_conv_flags |= CONV_STRICT_ASCII;
874		goto done;
875	}
876
877	/*
878	 * Switch to the victim's locale, and discover its character set.
879	 */
880	if (setlocale(LC_ALL, datap->pd_locale) == NULL) {
881		(void) fprintf(stderr,
882		    "%s: Couldn't determine locale of target process.\n",
883		    command);
884		(void) fprintf(stderr,
885		    "%s: Some strings may not be displayed properly.\n",
886		    command);
887		goto done;
888	}
889
890	/*
891	 * Get LC_CTYPE part of target's locale, and its codeset.
892	 */
893	targetlocale = safe_strdup(setlocale(LC_CTYPE, NULL));
894	targetcharset = safe_strdup(nl_langinfo(CODESET));
895
896	/*
897	 * Now go fully back to the pargs user's locale.
898	 */
899	(void) setlocale(LC_ALL, "");
900
901	/*
902	 * It's safe to bail here if the lc_ctype of the locales are the
903	 * same-- we know that their encodings and characters sets are the same.
904	 */
905	if (strcmp(targetlocale, mylocale) == 0)
906		goto done;
907
908	*diflocale = 1;
909
910	/*
911	 * If the codeset of the victim matches our codeset then iconv need
912	 * not be involved.
913	 */
914	if (strcmp(mycharset, targetcharset) == 0)
915		goto done;
916
917	if ((datap->pd_iconv = iconv_open(mycharset, targetcharset))
918	    == (iconv_t)-1) {
919		/*
920		 * EINVAL indicates there was no conversion available
921		 * from victim charset to mycharset
922		 */
923		if (errno != EINVAL) {
924			(void) fprintf(stderr,
925			    "%s: failed to initialize iconv: %s\n",
926			    command, strerror(errno));
927			exit(1);
928		}
929		datap->pd_conv_flags |= CONV_STRICT_ASCII;
930	} else {
931		datap->pd_conv_flags |= CONV_USE_ICONV;
932	}
933done:
934	free(mycharset);
935	free(mylocale);
936	free(targetcharset);
937	free(targetlocale);
938}
939
940static void
941cleanup_conversions(pargs_data_t *datap)
942{
943	if (datap->pd_conv_flags & CONV_USE_ICONV) {
944		(void) iconv_close(datap->pd_iconv);
945	}
946}
947
948static char *
949convert_run_iconv(pargs_data_t *datap, const char *str)
950{
951	size_t inleft, outleft, bufsz = 64;
952	char *outstr, *outstrptr;
953	const char *instrptr;
954
955	for (;;) {
956		outstrptr = outstr = safe_zalloc(bufsz + 1);
957		outleft = bufsz;
958
959		/*
960		 * Generate the "initial shift state" sequence, placing that
961		 * at the head of the string.
962		 */
963		inleft = 0;
964		(void) iconv(datap->pd_iconv, NULL, &inleft,
965		    &outstrptr, &outleft);
966
967		inleft = strlen(str);
968		instrptr = str;
969		if (iconv(datap->pd_iconv, &instrptr, &inleft, &outstrptr,
970		    &outleft) != (size_t)-1) {
971			/*
972			 * Outstr must be null terminated upon exit from
973			 * iconv().
974			 */
975			*(outstr + (bufsz - outleft)) = '\0';
976			break;
977		} else if (errno == E2BIG) {
978			bufsz *= 2;
979			free(outstr);
980		} else if ((errno == EILSEQ) || (errno == EINVAL)) {
981			free(outstr);
982			return (NULL);
983		} else {
984			/*
985			 * iconv() could in theory return EBADF, but that
986			 * shouldn't happen.
987			 */
988			(void) fprintf(stderr,
989			    "%s: iconv(3C) failed unexpectedly: %s\n",
990			    command, strerror(errno));
991
992			exit(1);
993		}
994	}
995	return (outstr);
996}
997
998/*
999 * Returns a freshly allocated string converted to the local character set,
1000 * removed of unprintable characters.
1001 */
1002static char *
1003convert_str(pargs_data_t *datap, const char *str, int *unprintable)
1004{
1005	char *retstr, *tmp;
1006
1007	if (datap->pd_conv_flags & CONV_STRICT_ASCII) {
1008		retstr = unctrl_str_strict_ascii(str, 1, unprintable);
1009		return (retstr);
1010	}
1011
1012	if ((datap->pd_conv_flags & CONV_USE_ICONV) == 0) {
1013		/*
1014		 * If we aren't using iconv(), convert control chars in
1015		 * the string in pargs' locale, since that is the display
1016		 * locale.
1017		 */
1018		retstr = unctrl_str(str, 1, unprintable);
1019		return (retstr);
1020	}
1021
1022	/*
1023	 * The logic here is a bit (ahem) tricky.  Start by converting
1024	 * unprintable characters *in the target's locale*.  This should
1025	 * eliminate a variety of unprintable or illegal characters-- in
1026	 * short, it should leave us with something which iconv() won't
1027	 * have trouble with.
1028	 *
1029	 * After allowing iconv to convert characters as needed, run unctrl
1030	 * again in pargs' locale-- This time to make sure that any
1031	 * characters which aren't printable according to the *current*
1032	 * locale (independent of the current codeset) get taken care of.
1033	 * Without this second stage, we might (for example) fail to
1034	 * properly handle characters converted into the 646 character set
1035	 * (which are 8-bits wide), but which must be displayed in the C
1036	 * locale (which uses 646, but whose printable characters are a
1037	 * subset of the 7-bit characters).
1038	 *
1039	 * Note that assuming the victim's locale using LC_ALL will be
1040	 * problematic when pargs' messages are internationalized in the
1041	 * future (and it calls textdomain(3C)).  In this case, any
1042	 * error message fprintf'd in unctrl_str() will be in the wrong
1043	 * LC_MESSAGES class.  We'll cross that bridge when we come to it.
1044	 */
1045	(void) setlocale(LC_ALL, datap->pd_locale);
1046	retstr = unctrl_str(str, 1, unprintable);
1047	(void) setlocale(LC_ALL, "");
1048
1049	tmp = retstr;
1050	if ((retstr = convert_run_iconv(datap, retstr)) == NULL) {
1051		/*
1052		 * In this (rare but real) case, the iconv() failed even
1053		 * though we unctrl'd the string.  Treat the original string
1054		 * (str) as a C locale string and strip it that way.
1055		 */
1056		free(tmp);
1057		return (unctrl_str_strict_ascii(str, 0, unprintable));
1058	}
1059
1060	free(tmp);
1061	tmp = retstr;
1062	/*
1063	 * Run unctrl_str, but make sure not to escape \ characters, which
1064	 * may have resulted from the first round of unctrl.
1065	 */
1066	retstr = unctrl_str(retstr, 0, unprintable);
1067	free(tmp);
1068	return (retstr);
1069}
1070
1071
1072static void
1073convert_array(pargs_data_t *datap, char **arr, size_t count, int *unprintable)
1074{
1075	int i;
1076	char *tmp;
1077
1078	if (arr == NULL)
1079		return;
1080
1081	for (i = 0; i < count; i++) {
1082		if ((tmp = arr[i]) == NULL)
1083			continue;
1084		arr[i] = convert_str(datap, arr[i], unprintable);
1085		free(tmp);
1086	}
1087}
1088
1089/*
1090 * Free data allocated during the gathering phase.
1091 */
1092static void
1093free_data(pargs_data_t *datap)
1094{
1095	int i;
1096
1097	if (datap->pd_argv) {
1098		for (i = 0; i < datap->pd_argc; i++) {
1099			if (datap->pd_argv_strs[i] != NULL)
1100				free(datap->pd_argv_strs[i]);
1101		}
1102		free(datap->pd_argv);
1103		free(datap->pd_argv_strs);
1104	}
1105
1106	if (datap->pd_envp) {
1107		for (i = 0; i < datap->pd_envc; i++) {
1108			if (datap->pd_envp_strs[i] != NULL)
1109				free(datap->pd_envp_strs[i]);
1110		}
1111		free(datap->pd_envp);
1112		free(datap->pd_envp_strs);
1113	}
1114
1115	if (datap->pd_auxv) {
1116		for (i = 0; i < datap->pd_auxc; i++) {
1117			if (datap->pd_auxv_strs[i] != NULL)
1118				free(datap->pd_auxv_strs[i]);
1119		}
1120		free(datap->pd_auxv);
1121		free(datap->pd_auxv_strs);
1122	}
1123}
1124
1125static void
1126print_args(pargs_data_t *datap)
1127{
1128	int i;
1129
1130	if (datap->pd_argv == NULL) {
1131		(void) fprintf(stderr, "%s: failed to read argv[]\n", command);
1132		return;
1133	}
1134
1135	for (i = 0; i < datap->pd_argc; i++) {
1136		(void) printf("argv[%d]: ", i);
1137		if (datap->pd_argv[i] == NULL) {
1138			(void) printf("<NULL>\n");
1139		} else if (datap->pd_argv_strs[i] == NULL) {
1140			(void) printf("<0x%0*lx>\n",
1141			    (dmodel == PR_MODEL_LP64)? 16 : 8,
1142			    (long)datap->pd_argv[i]);
1143		} else {
1144			(void) printf("%s\n", datap->pd_argv_strs[i]);
1145		}
1146	}
1147}
1148
1149static void
1150print_env(pargs_data_t *datap)
1151{
1152	int i;
1153
1154	if (datap->pd_envp == NULL) {
1155		(void) fprintf(stderr, "%s: failed to read envp[]\n", command);
1156		return;
1157	}
1158
1159	for (i = 0; i < datap->pd_envc; i++) {
1160		(void) printf("envp[%d]: ", i);
1161		if (datap->pd_envp[i] == 0) {
1162			break;
1163		} else if (datap->pd_envp_strs[i] == NULL) {
1164			(void) printf("<0x%0*lx>\n",
1165			    (dmodel == PR_MODEL_LP64)? 16 : 8,
1166			    (long)datap->pd_envp[i]);
1167		} else {
1168			(void) printf("%s\n", datap->pd_envp_strs[i]);
1169		}
1170	}
1171}
1172
1173static int
1174print_cmdline(pargs_data_t *datap)
1175{
1176	int i;
1177
1178	/*
1179	 * Go through and check to see if we have valid data.  If not, print
1180	 * an error message and bail.
1181	 */
1182	for (i = 0; i < datap->pd_argc; i++) {
1183		if (datap->pd_argv[i] == NULL ||
1184		    datap->pd_argv_strs[i] == NULL) {
1185			(void) fprintf(stderr, "%s: target has corrupted "
1186			    "argument list\n", command);
1187			return (1);
1188		}
1189
1190		datap->pd_argv_strs[i] =
1191		    quote_string(datap, datap->pd_argv_strs[i]);
1192	}
1193
1194	if (datap->pd_execname == NULL) {
1195		(void) fprintf(stderr, "%s: cannot determine name of "
1196		    "executable\n", command);
1197		return (1);
1198	}
1199
1200	(void) printf("%s ", datap->pd_execname);
1201
1202	for (i = 1; i < datap->pd_argc; i++)
1203		(void) printf("%s ", datap->pd_argv_strs[i]);
1204
1205	(void) printf("\n");
1206
1207	return (0);
1208}
1209
1210static void
1211print_auxv(pargs_data_t *datap)
1212{
1213	int i;
1214	const auxv_t *pa;
1215
1216	/*
1217	 * Print the names and values of all the aux vector entries.
1218	 */
1219	for (i = 0; i < datap->pd_auxc; i++) {
1220		char type[32];
1221		char decode[PATH_MAX];
1222		struct aux_id *aux;
1223		long v;
1224		pa = &datap->pd_auxv[i];
1225
1226		aux = aux_find(pa->a_type);
1227		v = (long)pa->a_un.a_val;
1228
1229		if (aux != NULL) {
1230			/*
1231			 * Fetch aux vector type string and decoded
1232			 * representation of the value.
1233			 */
1234			(void) strlcpy(type, aux->aux_name, sizeof (type));
1235			aux->aux_decode(v, datap->pd_auxv_strs[i],
1236			    sizeof (decode), decode);
1237		} else {
1238			(void) snprintf(type, sizeof (type), "%d", pa->a_type);
1239			decode[0] = '\0';
1240		}
1241
1242		(void) printf("%-*s 0x%0*lx %s\n", MAX_AT_NAME_LEN, type,
1243		    (dmodel == PR_MODEL_LP64)? 16 : 8, v, decode);
1244	}
1245}
1246
1247int
1248main(int argc, char *argv[])
1249{
1250	int aflag = 0, cflag = 0, eflag = 0, xflag = 0, lflag = 0;
1251	int errflg = 0, retc = 0;
1252	int opt;
1253	int error = 1;
1254	core_content_t content = 0;
1255
1256	(void) setlocale(LC_ALL, "");
1257
1258	if ((command = strrchr(argv[0], '/')) != NULL)
1259		command++;
1260	else
1261		command = argv[0];
1262
1263	while ((opt = getopt(argc, argv, "acelxF")) != EOF) {
1264		switch (opt) {
1265		case 'a':		/* show process arguments */
1266			content |= CC_CONTENT_STACK;
1267			aflag++;
1268			break;
1269		case 'c':		/* force 7-bit ascii */
1270			cflag++;
1271			break;
1272		case 'e':		/* show environment variables */
1273			content |= CC_CONTENT_STACK;
1274			eflag++;
1275			break;
1276		case 'l':
1277			lflag++;
1278			aflag++;	/* -l implies -a */
1279			break;
1280		case 'x':		/* show aux vector entries */
1281			xflag++;
1282			break;
1283		case 'F':
1284			/*
1285			 * Since we open the process read-only, there is no need
1286			 * for the -F flag.  It's a documented flag, so we
1287			 * consume it silently.
1288			 */
1289			break;
1290		default:
1291			errflg++;
1292			break;
1293		}
1294	}
1295
1296	/* -a is the default if no options are specified */
1297	if ((aflag + eflag + xflag + lflag) == 0) {
1298		aflag++;
1299		content |= CC_CONTENT_STACK;
1300	}
1301
1302	/* -l cannot be used with the -x or -e flags */
1303	if (lflag && (xflag || eflag)) {
1304		(void) fprintf(stderr, "-l is incompatible with -x and -e\n");
1305		errflg++;
1306	}
1307
1308	argc -= optind;
1309	argv += optind;
1310
1311	if (errflg || argc <= 0) {
1312		(void) fprintf(stderr,
1313		    "usage:  %s [-acexF] { pid | core } ...\n"
1314		    "  (show process arguments and environment)\n"
1315		    "  -a: show process arguments (default)\n"
1316		    "  -c: interpret characters as 7-bit ascii regardless of "
1317		    "locale\n"
1318		    "  -e: show environment variables\n"
1319		    "  -l: display arguments as command line\n"
1320		    "  -x: show aux vector entries\n"
1321		    "  -F: force grabbing of the target process\n", command);
1322		return (2);
1323	}
1324
1325	while (argc-- > 0) {
1326		char *arg;
1327		int gret, r;
1328		psinfo_t psinfo;
1329		char *psargs_conv;
1330		struct ps_prochandle *Pr;
1331		pargs_data_t datap;
1332		char *info;
1333		size_t info_sz;
1334		int pstate;
1335		char execname[PATH_MAX];
1336		int unprintable;
1337		int diflocale;
1338
1339		(void) fflush(stdout);
1340		arg = *argv++;
1341
1342		/*
1343		 * Suppress extra blanks lines if we've encountered processes
1344		 * which can't be opened.
1345		 */
1346		if (error == 0) {
1347			(void) printf("\n");
1348		}
1349		error = 0;
1350
1351		/*
1352		 * First grab just the psinfo information, in case this
1353		 * process is a zombie (in which case proc_arg_grab() will
1354		 * fail).  If so, print a nice message and continue.
1355		 */
1356		if (proc_arg_psinfo(arg, PR_ARG_ANY, &psinfo,
1357		    &gret) == -1) {
1358			(void) fprintf(stderr, "%s: cannot examine %s: %s\n",
1359			    command, arg, Pgrab_error(gret));
1360			retc++;
1361			error = 1;
1362			continue;
1363		}
1364
1365		if (psinfo.pr_nlwp == 0) {
1366			(void) printf("%d: <defunct>\n", (int)psinfo.pr_pid);
1367			continue;
1368		}
1369
1370		/*
1371		 * If process is a "system" process (like pageout), just
1372		 * print its psargs and continue on.
1373		 */
1374		if (psinfo.pr_size == 0 && psinfo.pr_rssize == 0) {
1375			proc_unctrl_psinfo(&psinfo);
1376			if (!lflag)
1377				(void) printf("%d: ", (int)psinfo.pr_pid);
1378			(void) printf("%s\n", psinfo.pr_psargs);
1379			continue;
1380		}
1381
1382		/*
1383		 * Open the process readonly, since we do not need to write to
1384		 * the control file.
1385		 */
1386		if ((Pr = proc_arg_grab(arg, PR_ARG_ANY, PGRAB_RDONLY,
1387		    &gret)) == NULL) {
1388			(void) fprintf(stderr, "%s: cannot examine %s: %s\n",
1389			    command, arg, Pgrab_error(gret));
1390			retc++;
1391			error = 1;
1392			continue;
1393		}
1394
1395		pstate = Pstate(Pr);
1396
1397		if (pstate == PS_DEAD &&
1398		    (Pcontent(Pr) & content) != content) {
1399			(void) fprintf(stderr, "%s: core '%s' has "
1400			    "insufficient content\n", command, arg);
1401			retc++;
1402			continue;
1403		}
1404
1405		/*
1406		 * If malloc() fails, we return here so that we can let go
1407		 * of the victim, restore our locale, print a message,
1408		 * then exit.
1409		 */
1410		if ((r = setjmp(env)) != 0) {
1411			Prelease(Pr, 0);
1412			(void) setlocale(LC_ALL, "");
1413			(void) fprintf(stderr, "%s: out of memory: %s\n",
1414			    command, strerror(r));
1415			return (1);
1416		}
1417
1418		dmodel = Pstatus(Pr)->pr_dmodel;
1419		bzero(&datap, sizeof (datap));
1420		bcopy(Ppsinfo(Pr), &psinfo, sizeof (psinfo_t));
1421		datap.pd_proc = Pr;
1422		datap.pd_psinfo = &psinfo;
1423
1424		if (cflag)
1425			datap.pd_conv_flags |= CONV_STRICT_ASCII;
1426
1427		/*
1428		 * Strip control characters, then record process summary in
1429		 * a buffer, since we don't want to print anything out until
1430		 * after we release the process.
1431		 */
1432
1433		/*
1434		 * The process is neither a system process nor defunct.
1435		 *
1436		 * Do printing and post-processing (like name lookups) after
1437		 * gathering the raw data from the process and releasing it.
1438		 * This way, we don't deadlock on (for example) name lookup
1439		 * if we grabbed the nscd and do 'pargs -x'.
1440		 *
1441		 * We always fetch the environment of the target, so that we
1442		 * can make an educated guess about its locale.
1443		 */
1444		get_env(&datap);
1445		if (aflag != 0)
1446			get_args(&datap);
1447		if (xflag != 0)
1448			get_auxv(&datap);
1449
1450		/*
1451		 * If malloc() fails after this poiint, we return here to
1452		 * restore our locale and print a message.  If we don't
1453		 * reset this, we might erroneously try to Prelease a process
1454		 * twice.
1455		 */
1456		if ((r = setjmp(env)) != 0) {
1457			(void) setlocale(LC_ALL, "");
1458			(void) fprintf(stderr, "%s: out of memory: %s\n",
1459			    command, strerror(r));
1460			return (1);
1461		}
1462
1463		/*
1464		 * For the -l option, we need a proper name for this executable
1465		 * before we release it.
1466		 */
1467		if (lflag)
1468			datap.pd_execname = Pexecname(Pr, execname,
1469			    sizeof (execname));
1470
1471		Prelease(Pr, 0);
1472
1473		/*
1474		 * Crawl through the environment to determine the locale of
1475		 * the target.
1476		 */
1477		lookup_locale(&datap);
1478		diflocale = 0;
1479		setup_conversions(&datap, &diflocale);
1480
1481		if (lflag != 0) {
1482			unprintable = 0;
1483			convert_array(&datap, datap.pd_argv_strs,
1484			    datap.pd_argc, &unprintable);
1485			if (diflocale)
1486				(void) fprintf(stderr, "%s: Warning, target "
1487				    "locale differs from current locale\n",
1488				    command);
1489			else if (unprintable)
1490				(void) fprintf(stderr, "%s: Warning, command "
1491				    "line contains unprintable characters\n",
1492				    command);
1493
1494			retc += print_cmdline(&datap);
1495		} else {
1496			psargs_conv = convert_str(&datap, psinfo.pr_psargs,
1497			    &unprintable);
1498			info_sz = strlen(psargs_conv) + MAXPATHLEN + 32 + 1;
1499			info = malloc(info_sz);
1500			if (pstate == PS_DEAD) {
1501				(void) snprintf(info, info_sz,
1502				    "core '%s' of %d:\t%s\n",
1503				    arg, (int)psinfo.pr_pid, psargs_conv);
1504			} else {
1505				(void) snprintf(info, info_sz, "%d:\t%s\n",
1506				    (int)psinfo.pr_pid, psargs_conv);
1507			}
1508			(void) printf("%s", info);
1509			free(info);
1510			free(psargs_conv);
1511
1512			if (aflag != 0) {
1513				convert_array(&datap, datap.pd_argv_strs,
1514				    datap.pd_argc, &unprintable);
1515				print_args(&datap);
1516				if (eflag || xflag)
1517					(void) printf("\n");
1518			}
1519
1520			if (eflag != 0) {
1521				convert_array(&datap, datap.pd_envp_strs,
1522				    datap.pd_envc, &unprintable);
1523				print_env(&datap);
1524				if (xflag)
1525					(void) printf("\n");
1526			}
1527
1528			if (xflag != 0) {
1529				convert_array(&datap, datap.pd_auxv_strs,
1530				    datap.pd_auxc, &unprintable);
1531				print_auxv(&datap);
1532			}
1533		}
1534
1535		cleanup_conversions(&datap);
1536		free_data(&datap);
1537	}
1538
1539	return (retc != 0 ? 1 : 0);
1540}
1541