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 (c) 2013, Joyent, Inc. All rights reserved.
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 uintptr_t *pd_envp;
88 char **pd_envp_strs;
89 size_t pd_auxc;
90 auxv_t *pd_auxv;
91 char **pd_auxv_strs;
92 char *pd_execname;
93 } pargs_data_t;
94
95 #define CONV_USE_ICONV 0x01
96 #define CONV_STRICT_ASCII 0x02
97
98 static char *command;
99 static int dmodel;
100
101 #define EXTRACT_BUFSZ 128 /* extract_string() initial size */
102 #define ENV_CHUNK 16 /* #env ptrs to read at a time */
103
104 static jmp_buf env; /* malloc failure handling */
105
106 static void *
107 safe_zalloc(size_t size)
108 {
109 void *p;
110
111 /*
112 * If the malloc fails we longjmp out to allow the code to Prelease()
113 * a stopped victim if needed.
114 */
115 if ((p = malloc(size)) == NULL) {
116 longjmp(env, errno);
117 }
118
119 bzero(p, size);
120 return (p);
121 }
122
123 static char *
124 safe_strdup(const char *s1)
125 {
126 char *s2;
127
128 s2 = safe_zalloc(strlen(s1) + 1);
129 (void) strcpy(s2, s1);
130 return (s2);
131 }
132
133 /*
134 * Given a wchar_t which might represent an 'escapable' sequence (see
135 * formats(5)), return the base ascii character needed to print that
136 * sequence.
137 *
138 * The comparisons performed may look suspect at first, but all are valid;
139 * the characters below all appear in the "Portable Character Set." The
140 * Single Unix Spec says: "The wide-character value for each member of the
141 * Portable Character Set will equal its value when used as the lone
142 * character in an integer character constant."
143 */
144 static uchar_t
145 get_interp_char(wchar_t wc)
146 {
147 switch (wc) {
148 case L'\a':
149 return ('a');
150 case L'\b':
151 return ('b');
152 case L'\f':
153 return ('f');
154 case L'\n':
155 return ('n');
156 case L'\r':
157 return ('r');
158 case L'\t':
159 return ('t');
160 case L'\v':
161 return ('v');
162 case L'\\':
163 return ('\\');
164 }
165 return ('\0');
166 }
167
168 static char *
169 unctrl_str_strict_ascii(const char *src, int escape_slash, int *unprintable)
170 {
171 uchar_t *uc, *ucp, c, ic;
172 uc = ucp = safe_zalloc((strlen(src) * 4) + 1);
173 while ((c = *src++) != '\0') {
174 /*
175 * Call get_interp_char *first*, since \ will otherwise not
176 * be escaped as \\.
177 */
178 if ((ic = get_interp_char((wchar_t)c)) != '\0') {
179 if (escape_slash || ic != '\\')
180 *ucp++ = '\\';
181 *ucp++ = ic;
182 } else if (isascii(c) && isprint(c)) {
183 *ucp++ = c;
184 } else {
185 *ucp++ = '\\';
186 *ucp++ = ((c >> 6) & 7) + '0';
187 *ucp++ = ((c >> 3) & 7) + '0';
188 *ucp++ = (c & 7) + '0';
189 *unprintable = 1;
190 }
191 }
192 *ucp = '\0';
193 return ((char *)uc);
194 }
195
196 /*
197 * Convert control characters as described in format(5) to their readable
198 * representation; special care is taken to handle multibyte character sets.
199 *
200 * If escape_slash is true, escaping of '\' occurs. The first time a string
201 * is unctrl'd, this should be '1'. Subsequent iterations over the same
202 * string should set escape_slash to 0. Otherwise you'll wind up with
203 * \ --> \\ --> \\\\.
204 */
205 static char *
206 unctrl_str(const char *src, int escape_slash, int *unprintable)
207 {
208 wchar_t wc;
209 wchar_t *wide_src, *wide_srcp;
210 wchar_t *wide_dest, *wide_destp;
211 char *uc;
212 size_t srcbufsz = strlen(src) + 1;
213 size_t destbufsz = srcbufsz * 4;
214 size_t srclen, destlen;
215
216 wide_srcp = wide_src = safe_zalloc(srcbufsz * sizeof (wchar_t));
217 wide_destp = wide_dest = safe_zalloc(destbufsz * sizeof (wchar_t));
218
219 if ((srclen = mbstowcs(wide_src, src, srcbufsz - 1)) == (size_t)-1) {
220 /*
221 * We can't trust the string, since in the locale in which
222 * this call is operating, the string contains an invalid
223 * multibyte sequence. There isn't much to do here, so
224 * convert the string byte by byte to wide characters, as
225 * if it came from a C locale (char) string. This isn't
226 * perfect, but at least the characters will make it to
227 * the screen.
228 */
229 free(wide_src);
230 free(wide_dest);
231 return (unctrl_str_strict_ascii(src, escape_slash,
232 unprintable));
233 }
234 if (srclen == (srcbufsz - 1)) {
235 wide_src[srclen] = L'\0';
236 }
237
238 while ((wc = *wide_srcp++) != L'\0') {
239 char cvt_buf[MB_LEN_MAX];
240 int len, i;
241 char c = get_interp_char(wc);
242
243 if ((c != '\0') && (escape_slash || c != '\\')) {
244 /*
245 * Print "interpreted version" (\n, \a, etc).
246 */
247 *wide_destp++ = L'\\';
248 *wide_destp++ = (wchar_t)c;
249 continue;
250 }
251
252 if (iswprint(wc)) {
253 *wide_destp++ = wc;
254 continue;
255 }
256
257 /*
258 * Convert the wide char back into (potentially several)
259 * multibyte characters, then escape out each of those bytes.
260 */
261 bzero(cvt_buf, sizeof (cvt_buf));
262 if ((len = wctomb(cvt_buf, wc)) == -1) {
263 /*
264 * This is a totally invalid wide char; discard it.
265 */
266 continue;
267 }
268 for (i = 0; i < len; i++) {
269 uchar_t c = cvt_buf[i];
270 *wide_destp++ = L'\\';
271 *wide_destp++ = (wchar_t)('0' + ((c >> 6) & 7));
272 *wide_destp++ = (wchar_t)('0' + ((c >> 3) & 7));
273 *wide_destp++ = (wchar_t)('0' + (c & 7));
274 *unprintable = 1;
275 }
276 }
277
278 *wide_destp = '\0';
279 destlen = (wide_destp - wide_dest) * MB_CUR_MAX + 1;
280 uc = safe_zalloc(destlen);
281 if (wcstombs(uc, wide_dest, destlen) == (size_t)-1) {
282 /* If we've gotten this far, wcstombs shouldn't fail... */
283 (void) fprintf(stderr, "%s: wcstombs failed unexpectedly: %s\n",
284 command, strerror(errno));
285 exit(1);
286 } else {
287 char *tmp;
288 /*
289 * Try to save memory; don't waste 3 * strlen in the
290 * common case.
291 */
292 tmp = safe_strdup(uc);
293 free(uc);
294 uc = tmp;
295 }
296 free(wide_dest);
297 free(wide_src);
298 return (uc);
299 }
300
301 /*
302 * These functions determine which characters are safe to be left unquoted.
303 * Rather than starting with every printable character and subtracting out the
304 * shell metacharacters, we take the more conservative approach of starting with
305 * a set of safe characters and adding those few common punctuation characters
306 * which are known to be safe. The rules are:
307 *
308 * If this is a printable character (graph), and not punctuation, it is
309 * safe to leave unquoted.
310 *
311 * If it's one of known hard-coded safe characters, it's also safe to leave
312 * unquoted.
313 *
314 * Otherwise, the entire argument must be quoted.
315 *
316 * This will cause some strings to be unecessarily quoted, but it is safer than
317 * having a character unintentionally interpreted by the shell.
318 */
319 static int
320 issafe_ascii(char c)
321 {
322 return (isalnum(c) || strchr("_.-/@:,", c) != NULL);
323 }
324
325 static int
326 issafe(wchar_t wc)
327 {
328 return ((iswgraph(wc) && !iswpunct(wc)) ||
329 wschr(L"_.-/@:,", wc) != NULL);
330 }
331
332 /*ARGSUSED*/
333 static char *
334 quote_string_ascii(pargs_data_t *datap, char *src)
335 {
336 char *dst;
337 int quote_count = 0;
338 int need_quote = 0;
339 char *srcp, *dstp;
340 size_t dstlen;
341
342 for (srcp = src; *srcp != '\0'; srcp++) {
343 if (!issafe_ascii(*srcp)) {
344 need_quote = 1;
345 if (*srcp == '\'')
346 quote_count++;
347 }
348 }
349
350 if (!need_quote)
351 return (src);
352
353 /*
354 * The only character we care about here is a single quote. All the
355 * other unprintable characters (and backslashes) will have been dealt
356 * with by unctrl_str(). We make the following subtitution when we
357 * encounter a single quote:
358 *
359 * ' = '"'"'
360 *
361 * In addition, we put single quotes around the entire argument. For
362 * example:
363 *
364 * foo'bar = 'foo'"'"'bar'
365 */
366 dstlen = strlen(src) + 3 + 4 * quote_count;
367 dst = safe_zalloc(dstlen);
368
369 dstp = dst;
370 *dstp++ = '\'';
371 for (srcp = src; *srcp != '\0'; srcp++, dstp++) {
372 *dstp = *srcp;
373
374 if (*srcp == '\'') {
375 dstp[1] = '"';
376 dstp[2] = '\'';
377 dstp[3] = '"';
378 dstp[4] = '\'';
379 dstp += 4;
380 }
381 }
382 *dstp++ = '\'';
383 *dstp = '\0';
384
385 free(src);
386
387 return (dst);
388 }
389
390 static char *
391 quote_string(pargs_data_t *datap, char *src)
392 {
393 wchar_t *wide_src, *wide_srcp;
394 wchar_t *wide_dest, *wide_destp;
395 char *uc;
396 size_t srcbufsz = strlen(src) + 1;
397 size_t srclen;
398 size_t destbufsz;
399 size_t destlen;
400 int quote_count = 0;
401 int need_quote = 0;
402
403 if (datap->pd_conv_flags & CONV_STRICT_ASCII)
404 return (quote_string_ascii(datap, src));
405
406 wide_srcp = wide_src = safe_zalloc(srcbufsz * sizeof (wchar_t));
407
408 if ((srclen = mbstowcs(wide_src, src, srcbufsz - 1)) == (size_t)-1) {
409 free(wide_src);
410 return (quote_string_ascii(datap, src));
411 }
412
413 if (srclen == srcbufsz - 1)
414 wide_src[srclen] = L'\0';
415
416 for (wide_srcp = wide_src; *wide_srcp != '\0'; wide_srcp++) {
417 if (!issafe(*wide_srcp)) {
418 need_quote = 1;
419 if (*wide_srcp == L'\'')
420 quote_count++;
421 }
422 }
423
424 if (!need_quote) {
425 free(wide_src);
426 return (src);
427 }
428
429 /*
430 * See comment for quote_string_ascii(), above.
431 */
432 destbufsz = srcbufsz + 3 + 4 * quote_count;
433 wide_destp = wide_dest = safe_zalloc(destbufsz * sizeof (wchar_t));
434
435 *wide_destp++ = L'\'';
436 for (wide_srcp = wide_src; *wide_srcp != L'\0';
437 wide_srcp++, wide_destp++) {
438 *wide_destp = *wide_srcp;
439
440 if (*wide_srcp == L'\'') {
441 wide_destp[1] = L'"';
442 wide_destp[2] = L'\'';
443 wide_destp[3] = L'"';
444 wide_destp[4] = L'\'';
445 wide_destp += 4;
446 }
447 }
448 *wide_destp++ = L'\'';
449 *wide_destp = L'\0';
450
451 destlen = destbufsz * MB_CUR_MAX + 1;
452 uc = safe_zalloc(destlen);
453 if (wcstombs(uc, wide_dest, destlen) == (size_t)-1) {
454 /* If we've gotten this far, wcstombs shouldn't fail... */
455 (void) fprintf(stderr, "%s: wcstombs failed unexpectedly: %s\n",
456 command, strerror(errno));
457 exit(1);
458 }
459
460 free(wide_dest);
461 free(wide_src);
462
463 return (uc);
464 }
465
466
467 /*
468 * Determine the locale of the target process by traversing its environment,
469 * making only one pass for efficiency's sake; stash the result in
470 * datap->pd_locale.
471 *
472 * It's possible that the process has called setlocale() to change its
473 * locale to something different, but we mostly care about making a good
474 * guess as to the locale at exec(2) time.
475 */
476 static void
477 lookup_locale(pargs_data_t *datap)
478 {
479 int i, j, composite = 0;
480 size_t len = 0;
481 char *pd_locale;
482 char *lc_all = NULL, *lang = NULL;
483 char *lcs[] = { NULL, NULL, NULL, NULL, NULL, NULL };
484 static const char *cat_names[] = {
485 "LC_CTYPE=", "LC_NUMERIC=", "LC_TIME=",
486 "LC_COLLATE=", "LC_MONETARY=", "LC_MESSAGES="
487 };
488
489 for (i = 0; i < datap->pd_envc; i++) {
490 char *s = datap->pd_envp_strs[i];
491
492 if (s == NULL)
493 continue;
494
495 if (strncmp("LC_ALL=", s, strlen("LC_ALL=")) == 0) {
496 /*
497 * Minor optimization-- if we find LC_ALL we're done.
498 */
499 lc_all = s + strlen("LC_ALL=");
500 break;
501 }
502 for (j = 0; j <= _LastCategory; j++) {
503 if (strncmp(cat_names[j], s,
504 strlen(cat_names[j])) == 0) {
505 lcs[j] = s + strlen(cat_names[j]);
506 }
507 }
508 if (strncmp("LANG=", s, strlen("LANG=")) == 0) {
509 lang = s + strlen("LANG=");
510 }
511 }
512
513 if (lc_all && (*lc_all == '\0'))
514 lc_all = NULL;
515 if (lang && (*lang == '\0'))
516 lang = NULL;
517
518 for (i = 0; i <= _LastCategory; i++) {
519 if (lc_all != NULL) {
520 lcs[i] = lc_all;
521 } else if (lcs[i] != NULL) {
522 lcs[i] = lcs[i];
523 } else if (lang != NULL) {
524 lcs[i] = lang;
525 } else {
526 lcs[i] = "C";
527 }
528 if ((i > 0) && (lcs[i] != lcs[i-1]))
529 composite++;
530
531 len += 1 + strlen(lcs[i]); /* 1 extra byte for '/' */
532 }
533
534 if (composite == 0) {
535 /* simple locale */
536 pd_locale = safe_strdup(lcs[0]);
537 } else {
538 /* composite locale */
539 pd_locale = safe_zalloc(len + 1);
540 (void) snprintf(pd_locale, len + 1, "/%s/%s/%s/%s/%s/%s",
541 lcs[0], lcs[1], lcs[2], lcs[3], lcs[4], lcs[5]);
542 }
543 datap->pd_locale = pd_locale;
544 }
545
546 /*
547 * Pull a string from the victim, regardless of size; this routine allocates
548 * memory for the string which must be freed by the caller.
549 */
550 static char *
551 extract_string(pargs_data_t *datap, uintptr_t addr)
552 {
553 int size = EXTRACT_BUFSZ;
554 char *result;
555
556 result = safe_zalloc(size);
557
558 for (;;) {
559 if (Pread_string(datap->pd_proc, result, size, addr) < 0) {
560 free(result);
561 return (NULL);
562 } else if (strlen(result) == (size - 1)) {
563 free(result);
564 size *= 2;
565 result = safe_zalloc(size);
566 } else {
567 break;
568 }
569 }
570 return (result);
571 }
572
573 /*
574 * Utility function to read an array of pointers from the victim, adjusting
575 * for victim data model; returns the number of bytes successfully read.
576 */
577 static ssize_t
578 read_ptr_array(pargs_data_t *datap, uintptr_t offset, uintptr_t *buf,
579 size_t nelems)
580 {
581 ssize_t res;
582
583 if (dmodel == PR_MODEL_NATIVE) {
584 res = Pread(datap->pd_proc, buf, nelems * sizeof (uintptr_t),
585 offset);
586 } else {
587 int i;
588 uint32_t *arr32 = safe_zalloc(nelems * sizeof (uint32_t));
589
590 res = Pread(datap->pd_proc, arr32, nelems * sizeof (uint32_t),
591 offset);
592 if (res > 0) {
593 for (i = 0; i < nelems; i++)
594 buf[i] = arr32[i];
595 }
596 free(arr32);
597 }
598 return (res);
599 }
600
601 /*
602 * Extract the argv array from the victim; store the pointer values in
603 * datap->pd_argv and the extracted strings in datap->pd_argv_strs.
604 */
605 static void
606 get_args(pargs_data_t *datap)
607 {
608 size_t argc = datap->pd_psinfo->pr_argc;
609 uintptr_t argvoff = datap->pd_psinfo->pr_argv;
610 int i;
611
612 datap->pd_argc = argc;
613 datap->pd_argv = safe_zalloc(argc * sizeof (uintptr_t));
614
615 if (read_ptr_array(datap, argvoff, datap->pd_argv, argc) <= 0) {
616 free(datap->pd_argv);
617 datap->pd_argv = NULL;
618 return;
619 }
620
621 datap->pd_argv_strs = safe_zalloc(argc * sizeof (char *));
622 for (i = 0; i < argc; i++) {
623 if (datap->pd_argv[i] == 0)
624 continue;
625 datap->pd_argv_strs[i] = extract_string(datap,
626 datap->pd_argv[i]);
627 }
628 }
629
630 /*ARGSUSED*/
631 static int
632 build_env(void *data, struct ps_prochandle *pr, uintptr_t addr, const char *str)
633 {
634 pargs_data_t *datap = data;
635
636 if (datap->pd_envp != NULL) {
637 datap->pd_envp[datap->pd_envc] = addr;
638 if (str == NULL)
639 datap->pd_envp_strs[datap->pd_envc] = NULL;
640 else
641 datap->pd_envp_strs[datap->pd_envc] = strdup(str);
642 }
643
644 datap->pd_envc++;
645
646 return (0);
647 }
648
649 static void
650 get_env(pargs_data_t *datap)
651 {
652 struct ps_prochandle *pr = datap->pd_proc;
653
654 datap->pd_envc = 0;
655 (void) Penv_iter(pr, build_env, datap);
656
657 datap->pd_envp = safe_zalloc(sizeof (uintptr_t) * datap->pd_envc);
658 datap->pd_envp_strs = safe_zalloc(sizeof (char *) * datap->pd_envc);
659
660 datap->pd_envc = 0;
661 (void) Penv_iter(pr, build_env, datap);
662 }
663
664 /*
665 * The following at_* routines are used to decode data from the aux vector.
666 */
667
668 /*ARGSUSED*/
669 static void
670 at_null(long val, char *instr, size_t n, char *str)
671 {
672 str[0] = '\0';
673 }
674
675 /*ARGSUSED*/
676 static void
677 at_str(long val, char *instr, size_t n, char *str)
678 {
679 str[0] = '\0';
680 if (instr != NULL) {
681 (void) strlcpy(str, instr, n);
682 }
683 }
684
685 /*
686 * Note: Don't forget to add a corresponding case to isainfo(1).
687 */
688
689 #define FMT_AV(s, n, hwcap, mask, name) \
690 if ((hwcap) & (mask)) \
691 (void) snprintf(s, n, "%s" name " | ", s)
692
693 /*ARGSUSED*/
694 static void
695 at_hwcap(long val, char *instr, size_t n, char *str)
696 {
697 #if defined(__sparc) || defined(__sparcv9)
698 (void) elfcap_hw1_to_str(ELFCAP_STYLE_UC, val, str, n,
699 ELFCAP_FMT_PIPSPACE, EM_SPARC);
700
701 #elif defined(__i386) || defined(__amd64)
702 (void) elfcap_hw1_to_str(ELFCAP_STYLE_UC, val, str, n,
703 ELFCAP_FMT_PIPSPACE, EM_386);
704 #else
705 #error "port me"
706 #endif
707 }
708
709 /*ARGSUSED*/
710 static void
711 at_hwcap2(long val, char *instr, size_t n, char *str)
712 {
713 #if defined(__sparc) || defined(__sparcv9)
714 (void) elfcap_hw2_to_str(ELFCAP_STYLE_UC, val, str, n,
715 ELFCAP_FMT_PIPSPACE, EM_SPARC);
716
717 #elif defined(__i386) || defined(__amd64)
718 (void) elfcap_hw2_to_str(ELFCAP_STYLE_UC, val, str, n,
719 ELFCAP_FMT_PIPSPACE, EM_386);
720 #else
721 #error "port me"
722 #endif
723 }
724
725
726 /*ARGSUSED*/
727 static void
728 at_uid(long val, char *instr, size_t n, char *str)
729 {
730 struct passwd *pw = getpwuid((uid_t)val);
731
732 if ((pw == NULL) || (pw->pw_name == NULL))
733 str[0] = '\0';
734 else
735 (void) snprintf(str, n, "%lu(%s)", val, pw->pw_name);
736 }
737
738
739 /*ARGSUSED*/
740 static void
741 at_gid(long val, char *instr, size_t n, char *str)
742 {
743 struct group *gr = getgrgid((gid_t)val);
744
745 if ((gr == NULL) || (gr->gr_name == NULL))
746 str[0] = '\0';
747 else
748 (void) snprintf(str, n, "%lu(%s)", val, gr->gr_name);
749 }
750
751 static struct auxfl {
752 int af_flag;
753 const char *af_name;
754 } auxfl[] = {
755 { AF_SUN_SETUGID, "setugid" },
756 };
757
758 /*ARGSUSED*/
759 static void
760 at_flags(long val, char *instr, size_t n, char *str)
761 {
762 int i;
763
764 *str = '\0';
765
766 for (i = 0; i < sizeof (auxfl)/sizeof (struct auxfl); i++) {
767 if ((val & auxfl[i].af_flag) != 0) {
768 if (*str != '\0')
769 (void) strlcat(str, ",", n);
770 (void) strlcat(str, auxfl[i].af_name, n);
771 }
772 }
773 }
774
775 static struct auxsecfl {
776 uint_t af_flag;
777 const char *af_name;
778 } auxsecfl[] = {
779 { PROC_SEC_ASLR, "aslr" },
780 };
781
782 /*ARGSUSED*/
783 static void
784 at_secflags(long val, char *instr, size_t n, char *str)
785 {
786 int i;
787
788 *str = '\0';
789
790 for (i = 0; i < sizeof (auxsecfl)/sizeof (struct auxsecfl); i++) {
791 if ((val & auxsecfl[i].af_flag) != 0) {
792 if (*str != '\0')
793 (void) strlcat(str, ",", n);
794 (void) strlcat(str, auxsecfl[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_SECFLAGS, "AT_SUN_SECFLAGS", at_secflags },
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 *
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
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
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
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 *
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 *
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
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
1139 free_data(pargs_data_t *datap)
1140 {
1141 int i;
1142
1143 if (datap->pd_argv) {
1144 for (i = 0; i < datap->pd_argc; i++) {
1145 if (datap->pd_argv_strs[i] != NULL)
1146 free(datap->pd_argv_strs[i]);
1147 }
1148 free(datap->pd_argv);
1149 free(datap->pd_argv_strs);
1150 }
1151
1152 if (datap->pd_envp) {
1153 for (i = 0; i < datap->pd_envc; i++) {
1154 if (datap->pd_envp_strs[i] != NULL)
1155 free(datap->pd_envp_strs[i]);
1156 }
1157 free(datap->pd_envp);
1158 free(datap->pd_envp_strs);
1159 }
1160
1161 if (datap->pd_auxv) {
1162 for (i = 0; i < datap->pd_auxc; i++) {
1163 if (datap->pd_auxv_strs[i] != NULL)
1164 free(datap->pd_auxv_strs[i]);
1165 }
1166 free(datap->pd_auxv);
1167 free(datap->pd_auxv_strs);
1168 }
1169 }
1170
1171 static void
1172 print_args(pargs_data_t *datap)
1173 {
1174 int i;
1175
1176 if (datap->pd_argv == NULL) {
1177 (void) fprintf(stderr, "%s: failed to read argv[]\n", command);
1178 return;
1179 }
1180
1181 for (i = 0; i < datap->pd_argc; i++) {
1182 (void) printf("argv[%d]: ", i);
1183 if (datap->pd_argv[i] == NULL) {
1184 (void) printf("<NULL>\n");
1185 } else if (datap->pd_argv_strs[i] == NULL) {
1186 (void) printf("<0x%0*lx>\n",
1187 (dmodel == PR_MODEL_LP64)? 16 : 8,
1188 (long)datap->pd_argv[i]);
1189 } else {
1190 (void) printf("%s\n", datap->pd_argv_strs[i]);
1191 }
1192 }
1193 }
1194
1195 static void
1196 print_env(pargs_data_t *datap)
1197 {
1198 int i;
1199
1200 if (datap->pd_envp == NULL) {
1201 (void) fprintf(stderr, "%s: failed to read envp[]\n", command);
1202 return;
1203 }
1204
1205 for (i = 0; i < datap->pd_envc; i++) {
1206 (void) printf("envp[%d]: ", i);
1207 if (datap->pd_envp[i] == 0) {
1208 break;
1209 } else if (datap->pd_envp_strs[i] == NULL) {
1210 (void) printf("<0x%0*lx>\n",
1211 (dmodel == PR_MODEL_LP64)? 16 : 8,
1212 (long)datap->pd_envp[i]);
1213 } else {
1214 (void) printf("%s\n", datap->pd_envp_strs[i]);
1215 }
1216 }
1217 }
1218
1219 static int
1220 print_cmdline(pargs_data_t *datap)
1221 {
1222 int i;
1223
1224 /*
1225 * Go through and check to see if we have valid data. If not, print
1226 * an error message and bail.
1227 */
1228 for (i = 0; i < datap->pd_argc; i++) {
1229 if (datap->pd_argv == NULL || datap->pd_argv[i] == NULL ||
1230 datap->pd_argv_strs[i] == NULL) {
1231 (void) fprintf(stderr, "%s: target has corrupted "
1232 "argument list\n", command);
1233 return (1);
1234 }
1235
1236 datap->pd_argv_strs[i] =
1237 quote_string(datap, datap->pd_argv_strs[i]);
1238 }
1239
1240 if (datap->pd_execname == NULL) {
1241 (void) fprintf(stderr, "%s: cannot determine name of "
1242 "executable\n", command);
1243 return (1);
1244 }
1245
1246 (void) printf("%s ", datap->pd_execname);
1247
1248 for (i = 1; i < datap->pd_argc; i++)
1249 (void) printf("%s ", datap->pd_argv_strs[i]);
1250
1251 (void) printf("\n");
1252
1253 return (0);
1254 }
1255
1256 static void
1257 print_auxv(pargs_data_t *datap)
1258 {
1259 int i;
1260 const auxv_t *pa;
1261
1262 /*
1263 * Print the names and values of all the aux vector entries.
1264 */
1265 for (i = 0; i < datap->pd_auxc; i++) {
1266 char type[32];
1267 char decode[PATH_MAX];
1268 struct aux_id *aux;
1269 long v;
1270 pa = &datap->pd_auxv[i];
1271
1272 aux = aux_find(pa->a_type);
1273 v = (long)pa->a_un.a_val;
1274
1275 if (aux != NULL) {
1276 /*
1277 * Fetch aux vector type string and decoded
1278 * representation of the value.
1279 */
1280 (void) strlcpy(type, aux->aux_name, sizeof (type));
1281 aux->aux_decode(v, datap->pd_auxv_strs[i],
1282 sizeof (decode), decode);
1283 } else {
1284 (void) snprintf(type, sizeof (type), "%d", pa->a_type);
1285 decode[0] = '\0';
1286 }
1287
1288 (void) printf("%-*s 0x%0*lx %s\n", MAX_AT_NAME_LEN, type,
1289 (dmodel == PR_MODEL_LP64)? 16 : 8, v, decode);
1290 }
1291 }
1292
1293 int
1294 main(int argc, char *argv[])
1295 {
1296 int aflag = 0, cflag = 0, eflag = 0, xflag = 0, lflag = 0;
1297 int errflg = 0, retc = 0;
1298 int opt;
1299 int error = 1;
1300 core_content_t content = 0;
1301
1302 (void) setlocale(LC_ALL, "");
1303
1304 if ((command = strrchr(argv[0], '/')) != NULL)
1305 command++;
1306 else
1307 command = argv[0];
1308
1309 while ((opt = getopt(argc, argv, "acelxF")) != EOF) {
1310 switch (opt) {
1311 case 'a': /* show process arguments */
1312 content |= CC_CONTENT_STACK;
1313 aflag++;
1314 break;
1315 case 'c': /* force 7-bit ascii */
1316 cflag++;
1317 break;
1318 case 'e': /* show environment variables */
1319 content |= CC_CONTENT_STACK;
1320 eflag++;
1321 break;
1322 case 'l':
1323 lflag++;
1324 aflag++; /* -l implies -a */
1325 break;
1326 case 'x': /* show aux vector entries */
1327 xflag++;
1328 break;
1329 case 'F':
1330 /*
1331 * Since we open the process read-only, there is no need
1332 * for the -F flag. It's a documented flag, so we
1333 * consume it silently.
1334 */
1335 break;
1336 default:
1337 errflg++;
1338 break;
1339 }
1340 }
1341
1342 /* -a is the default if no options are specified */
1343 if ((aflag + eflag + xflag + lflag) == 0) {
1344 aflag++;
1345 content |= CC_CONTENT_STACK;
1346 }
1347
1348 /* -l cannot be used with the -x or -e flags */
1349 if (lflag && (xflag || eflag)) {
1350 (void) fprintf(stderr, "-l is incompatible with -x and -e\n");
1351 errflg++;
1352 }
1353
1354 argc -= optind;
1355 argv += optind;
1356
1357 if (errflg || argc <= 0) {
1358 (void) fprintf(stderr,
1359 "usage: %s [-aceFlx] { pid | core } ...\n"
1360 " (show process arguments and environment)\n"
1361 " -a: show process arguments (default)\n"
1362 " -c: interpret characters as 7-bit ascii regardless of "
1363 "locale\n"
1364 " -e: show environment variables\n"
1365 " -F: force grabbing of the target process\n"
1366 " -l: display arguments as command line\n"
1367 " -x: show aux vector entries\n", command);
1368 return (2);
1369 }
1370
1371 while (argc-- > 0) {
1372 char *arg;
1373 int gret, r;
1374 psinfo_t psinfo;
1375 char *psargs_conv;
1376 struct ps_prochandle *Pr;
1377 pargs_data_t datap;
1378 char *info;
1379 size_t info_sz;
1380 int pstate;
1381 char execname[PATH_MAX];
1382 int unprintable;
1383 int diflocale;
1384
1385 (void) fflush(stdout);
1386 arg = *argv++;
1387
1388 /*
1389 * Suppress extra blanks lines if we've encountered processes
1390 * which can't be opened.
1391 */
1392 if (error == 0) {
1393 (void) printf("\n");
1394 }
1395 error = 0;
1396
1397 /*
1398 * First grab just the psinfo information, in case this
1399 * process is a zombie (in which case proc_arg_grab() will
1400 * fail). If so, print a nice message and continue.
1401 */
1402 if (proc_arg_psinfo(arg, PR_ARG_ANY, &psinfo,
1403 &gret) == -1) {
1404 (void) fprintf(stderr, "%s: cannot examine %s: %s\n",
1405 command, arg, Pgrab_error(gret));
1406 retc++;
1407 error = 1;
1408 continue;
1409 }
1410
1411 if (psinfo.pr_nlwp == 0) {
1412 (void) printf("%d: <defunct>\n", (int)psinfo.pr_pid);
1413 continue;
1414 }
1415
1416 /*
1417 * If process is a "system" process (like pageout), just
1418 * print its psargs and continue on.
1419 */
1420 if (psinfo.pr_size == 0 && psinfo.pr_rssize == 0) {
1421 proc_unctrl_psinfo(&psinfo);
1422 if (!lflag)
1423 (void) printf("%d: ", (int)psinfo.pr_pid);
1424 (void) printf("%s\n", psinfo.pr_psargs);
1425 continue;
1426 }
1427
1428 /*
1429 * Open the process readonly, since we do not need to write to
1430 * the control file.
1431 */
1432 if ((Pr = proc_arg_grab(arg, PR_ARG_ANY, PGRAB_RDONLY,
1433 &gret)) == NULL) {
1434 (void) fprintf(stderr, "%s: cannot examine %s: %s\n",
1435 command, arg, Pgrab_error(gret));
1436 retc++;
1437 error = 1;
1438 continue;
1439 }
1440
1441 pstate = Pstate(Pr);
1442
1443 if (pstate == PS_DEAD &&
1444 (Pcontent(Pr) & content) != content) {
1445 (void) fprintf(stderr, "%s: core '%s' has "
1446 "insufficient content\n", command, arg);
1447 retc++;
1448 continue;
1449 }
1450
1451 /*
1452 * If malloc() fails, we return here so that we can let go
1453 * of the victim, restore our locale, print a message,
1454 * then exit.
1455 */
1456 if ((r = setjmp(env)) != 0) {
1457 Prelease(Pr, 0);
1458 (void) setlocale(LC_ALL, "");
1459 (void) fprintf(stderr, "%s: out of memory: %s\n",
1460 command, strerror(r));
1461 return (1);
1462 }
1463
1464 dmodel = Pstatus(Pr)->pr_dmodel;
1465 bzero(&datap, sizeof (datap));
1466 bcopy(Ppsinfo(Pr), &psinfo, sizeof (psinfo_t));
1467 datap.pd_proc = Pr;
1468 datap.pd_psinfo = &psinfo;
1469
1470 if (cflag)
1471 datap.pd_conv_flags |= CONV_STRICT_ASCII;
1472
1473 /*
1474 * Strip control characters, then record process summary in
1475 * a buffer, since we don't want to print anything out until
1476 * after we release the process.
1477 */
1478
1479 /*
1480 * The process is neither a system process nor defunct.
1481 *
1482 * Do printing and post-processing (like name lookups) after
1483 * gathering the raw data from the process and releasing it.
1484 * This way, we don't deadlock on (for example) name lookup
1485 * if we grabbed the nscd and do 'pargs -x'.
1486 *
1487 * We always fetch the environment of the target, so that we
1488 * can make an educated guess about its locale.
1489 */
1490 get_env(&datap);
1491 if (aflag != 0)
1492 get_args(&datap);
1493 if (xflag != 0)
1494 get_auxv(&datap);
1495
1496 /*
1497 * If malloc() fails after this poiint, we return here to
1498 * restore our locale and print a message. If we don't
1499 * reset this, we might erroneously try to Prelease a process
1500 * twice.
1501 */
1502 if ((r = setjmp(env)) != 0) {
1503 (void) setlocale(LC_ALL, "");
1504 (void) fprintf(stderr, "%s: out of memory: %s\n",
1505 command, strerror(r));
1506 return (1);
1507 }
1508
1509 /*
1510 * For the -l option, we need a proper name for this executable
1511 * before we release it.
1512 */
1513 if (lflag)
1514 datap.pd_execname = Pexecname(Pr, execname,
1515 sizeof (execname));
1516
1517 Prelease(Pr, 0);
1518
1519 /*
1520 * Crawl through the environment to determine the locale of
1521 * the target.
1522 */
1523 lookup_locale(&datap);
1524 diflocale = 0;
1525 setup_conversions(&datap, &diflocale);
1526
1527 if (lflag != 0) {
1528 unprintable = 0;
1529 convert_array(&datap, datap.pd_argv_strs,
1530 datap.pd_argc, &unprintable);
1531 if (diflocale)
1532 (void) fprintf(stderr, "%s: Warning, target "
1533 "locale differs from current locale\n",
1534 command);
1535 else if (unprintable)
1536 (void) fprintf(stderr, "%s: Warning, command "
1537 "line contains unprintable characters\n",
1538 command);
1539
1540 retc += print_cmdline(&datap);
1541 } else {
1542 psargs_conv = convert_str(&datap, psinfo.pr_psargs,
1543 &unprintable);
1544 info_sz = strlen(psargs_conv) + MAXPATHLEN + 32 + 1;
1545 info = malloc(info_sz);
1546 if (pstate == PS_DEAD) {
1547 (void) snprintf(info, info_sz,
1548 "core '%s' of %d:\t%s\n",
1549 arg, (int)psinfo.pr_pid, psargs_conv);
1550 } else {
1551 (void) snprintf(info, info_sz, "%d:\t%s\n",
1552 (int)psinfo.pr_pid, psargs_conv);
1553 }
1554 (void) printf("%s", info);
1555 free(info);
1556 free(psargs_conv);
1557
1558 if (aflag != 0) {
1559 convert_array(&datap, datap.pd_argv_strs,
1560 datap.pd_argc, &unprintable);
1561 print_args(&datap);
1562 if (eflag || xflag)
1563 (void) printf("\n");
1564 }
1565
1566 if (eflag != 0) {
1567 convert_array(&datap, datap.pd_envp_strs,
1568 datap.pd_envc, &unprintable);
1569 print_env(&datap);
1570 if (xflag)
1571 (void) printf("\n");
1572 }
1573
1574 if (xflag != 0) {
1575 convert_array(&datap, datap.pd_auxv_strs,
1576 datap.pd_auxc, &unprintable);
1577 print_auxv(&datap);
1578 }
1579 }
1580
1581 cleanup_conversions(&datap);
1582 free_data(&datap);
1583 }
1584
1585 return (retc != 0 ? 1 : 0);
1586 }