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