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