9596 Initial xsave xstate_bv should not include all features
[unleashed.git] / usr / src / cmd / ptools / pargs / pargs.c
blobaa7b74dce9fb1aa4ab802d66c845fd3347a9a65d
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
22 * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
23 * Use is subject to license terms.
26 * Copyright (c) 2018, Joyent, Inc.
30 * pargs examines and prints the arguments (argv), environment (environ),
31 * and auxiliary vector of another process.
33 * This utility is made more complex because it must run in internationalized
34 * environments. The two key cases for pargs to manage are:
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.
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.
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.
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>
77 typedef struct pargs_data {
78 struct ps_prochandle *pd_proc; /* target proc handle */
79 psinfo_t *pd_psinfo; /* target psinfo */
80 char *pd_locale; /* target process locale */
81 int pd_conv_flags; /* flags governing string conversion */
82 iconv_t pd_iconv; /* iconv conversion descriptor */
83 size_t pd_argc;
84 uintptr_t *pd_argv;
85 char **pd_argv_strs;
86 size_t pd_envc;
87 size_t pd_env_space;
88 uintptr_t *pd_envp;
89 char **pd_envp_strs;
90 size_t pd_auxc;
91 auxv_t *pd_auxv;
92 char **pd_auxv_strs;
93 char *pd_execname;
94 } pargs_data_t;
96 #define CONV_USE_ICONV 0x01
97 #define CONV_STRICT_ASCII 0x02
99 static char *command;
100 static int dmodel;
102 #define EXTRACT_BUFSZ 128 /* extract_string() initial size */
103 #define ENV_CHUNK 16 /* #env ptrs to read at a time */
105 static jmp_buf env; /* malloc failure handling */
107 static void *
108 safe_zalloc(size_t size)
110 void *p;
113 * If the malloc fails we longjmp out to allow the code to Prelease()
114 * a stopped victim if needed.
116 if ((p = malloc(size)) == NULL) {
117 longjmp(env, errno);
120 bzero(p, size);
121 return (p);
124 static char *
125 safe_strdup(const char *s1)
127 char *s2;
129 s2 = safe_zalloc(strlen(s1) + 1);
130 (void) strcpy(s2, s1);
131 return (s2);
135 * Given a wchar_t which might represent an 'escapable' sequence (see
136 * formats(5)), return the base ascii character needed to print that
137 * sequence.
139 * The comparisons performed may look suspect at first, but all are valid;
140 * the characters below all appear in the "Portable Character Set." The
141 * Single Unix Spec says: "The wide-character value for each member of the
142 * Portable Character Set will equal its value when used as the lone
143 * character in an integer character constant."
145 static uchar_t
146 get_interp_char(wchar_t wc)
148 switch (wc) {
149 case L'\a':
150 return ('a');
151 case L'\b':
152 return ('b');
153 case L'\f':
154 return ('f');
155 case L'\n':
156 return ('n');
157 case L'\r':
158 return ('r');
159 case L'\t':
160 return ('t');
161 case L'\v':
162 return ('v');
163 case L'\\':
164 return ('\\');
166 return ('\0');
169 static char *
170 unctrl_str_strict_ascii(const char *src, int escape_slash, int *unprintable)
172 uchar_t *uc, *ucp, c, ic;
173 uc = ucp = safe_zalloc((strlen(src) * 4) + 1);
174 while ((c = *src++) != '\0') {
176 * Call get_interp_char *first*, since \ will otherwise not
177 * be escaped as \\.
179 if ((ic = get_interp_char((wchar_t)c)) != '\0') {
180 if (escape_slash || ic != '\\')
181 *ucp++ = '\\';
182 *ucp++ = ic;
183 } else if (isascii(c) && isprint(c)) {
184 *ucp++ = c;
185 } else {
186 *ucp++ = '\\';
187 *ucp++ = ((c >> 6) & 7) + '0';
188 *ucp++ = ((c >> 3) & 7) + '0';
189 *ucp++ = (c & 7) + '0';
190 *unprintable = 1;
193 *ucp = '\0';
194 return ((char *)uc);
198 * Convert control characters as described in format(5) to their readable
199 * representation; special care is taken to handle multibyte character sets.
201 * If escape_slash is true, escaping of '\' occurs. The first time a string
202 * is unctrl'd, this should be '1'. Subsequent iterations over the same
203 * string should set escape_slash to 0. Otherwise you'll wind up with
204 * \ --> \\ --> \\\\.
206 static char *
207 unctrl_str(const char *src, int escape_slash, int *unprintable)
209 wchar_t wc;
210 wchar_t *wide_src, *wide_srcp;
211 wchar_t *wide_dest, *wide_destp;
212 char *uc;
213 size_t srcbufsz = strlen(src) + 1;
214 size_t destbufsz = srcbufsz * 4;
215 size_t srclen, destlen;
217 wide_srcp = wide_src = safe_zalloc(srcbufsz * sizeof (wchar_t));
218 wide_destp = wide_dest = safe_zalloc(destbufsz * sizeof (wchar_t));
220 if ((srclen = mbstowcs(wide_src, src, srcbufsz - 1)) == (size_t)-1) {
222 * We can't trust the string, since in the locale in which
223 * this call is operating, the string contains an invalid
224 * multibyte sequence. There isn't much to do here, so
225 * convert the string byte by byte to wide characters, as
226 * if it came from a C locale (char) string. This isn't
227 * perfect, but at least the characters will make it to
228 * the screen.
230 free(wide_src);
231 free(wide_dest);
232 return (unctrl_str_strict_ascii(src, escape_slash,
233 unprintable));
235 if (srclen == (srcbufsz - 1)) {
236 wide_src[srclen] = L'\0';
239 while ((wc = *wide_srcp++) != L'\0') {
240 char cvt_buf[MB_LEN_MAX];
241 int len, i;
242 char c = get_interp_char(wc);
244 if ((c != '\0') && (escape_slash || c != '\\')) {
246 * Print "interpreted version" (\n, \a, etc).
248 *wide_destp++ = L'\\';
249 *wide_destp++ = (wchar_t)c;
250 continue;
253 if (iswprint(wc)) {
254 *wide_destp++ = wc;
255 continue;
259 * Convert the wide char back into (potentially several)
260 * multibyte characters, then escape out each of those bytes.
262 bzero(cvt_buf, sizeof (cvt_buf));
263 if ((len = wctomb(cvt_buf, wc)) == -1) {
265 * This is a totally invalid wide char; discard it.
267 continue;
269 for (i = 0; i < len; i++) {
270 uchar_t c = cvt_buf[i];
271 *wide_destp++ = L'\\';
272 *wide_destp++ = (wchar_t)('0' + ((c >> 6) & 7));
273 *wide_destp++ = (wchar_t)('0' + ((c >> 3) & 7));
274 *wide_destp++ = (wchar_t)('0' + (c & 7));
275 *unprintable = 1;
279 *wide_destp = '\0';
280 destlen = (wide_destp - wide_dest) * MB_CUR_MAX + 1;
281 uc = safe_zalloc(destlen);
282 if (wcstombs(uc, wide_dest, destlen) == (size_t)-1) {
283 /* If we've gotten this far, wcstombs shouldn't fail... */
284 (void) fprintf(stderr, "%s: wcstombs failed unexpectedly: %s\n",
285 command, strerror(errno));
286 exit(1);
287 } else {
288 char *tmp;
290 * Try to save memory; don't waste 3 * strlen in the
291 * common case.
293 tmp = safe_strdup(uc);
294 free(uc);
295 uc = tmp;
297 free(wide_dest);
298 free(wide_src);
299 return (uc);
303 * These functions determine which characters are safe to be left unquoted.
304 * Rather than starting with every printable character and subtracting out the
305 * shell metacharacters, we take the more conservative approach of starting with
306 * a set of safe characters and adding those few common punctuation characters
307 * which are known to be safe. The rules are:
309 * If this is a printable character (graph), and not punctuation, it is
310 * safe to leave unquoted.
312 * If it's one of the known hard-coded safe characters, it's also safe to
313 * leave unquoted.
315 * Otherwise, the entire argument must be quoted.
317 * This will cause some strings to be unnecessarily quoted, but it is safer than
318 * having a character unintentionally interpreted by the shell.
320 static int
321 issafe_ascii(char c)
323 return (isalnum(c) || strchr("_.-/@:,", c) != NULL);
326 static int
327 issafe(wchar_t wc)
329 return ((iswgraph(wc) && !iswpunct(wc)) ||
330 wschr(L"_.-/@:,", wc) != NULL);
333 /*ARGSUSED*/
334 static char *
335 quote_string_ascii(pargs_data_t *datap, char *src)
337 char *dst;
338 int quote_count = 0;
339 int need_quote = 0;
340 char *srcp, *dstp;
341 size_t dstlen;
343 for (srcp = src; *srcp != '\0'; srcp++) {
344 if (!issafe_ascii(*srcp)) {
345 need_quote = 1;
346 if (*srcp == '\'')
347 quote_count++;
351 if (!need_quote)
352 return (src);
355 * The only character we care about here is a single quote. All the
356 * other unprintable characters (and backslashes) will have been dealt
357 * with by unctrl_str(). We make the following subtitution when we
358 * encounter a single quote:
360 * ' = '"'"'
362 * In addition, we put single quotes around the entire argument. For
363 * example:
365 * foo'bar = 'foo'"'"'bar'
367 dstlen = strlen(src) + 3 + 4 * quote_count;
368 dst = safe_zalloc(dstlen);
370 dstp = dst;
371 *dstp++ = '\'';
372 for (srcp = src; *srcp != '\0'; srcp++, dstp++) {
373 *dstp = *srcp;
375 if (*srcp == '\'') {
376 dstp[1] = '"';
377 dstp[2] = '\'';
378 dstp[3] = '"';
379 dstp[4] = '\'';
380 dstp += 4;
383 *dstp++ = '\'';
384 *dstp = '\0';
386 free(src);
388 return (dst);
391 static char *
392 quote_string(pargs_data_t *datap, char *src)
394 wchar_t *wide_src, *wide_srcp;
395 wchar_t *wide_dest, *wide_destp;
396 char *uc;
397 size_t srcbufsz = strlen(src) + 1;
398 size_t srclen;
399 size_t destbufsz;
400 size_t destlen;
401 int quote_count = 0;
402 int need_quote = 0;
404 if (datap->pd_conv_flags & CONV_STRICT_ASCII)
405 return (quote_string_ascii(datap, src));
407 wide_srcp = wide_src = safe_zalloc(srcbufsz * sizeof (wchar_t));
409 if ((srclen = mbstowcs(wide_src, src, srcbufsz - 1)) == (size_t)-1) {
410 free(wide_src);
411 return (quote_string_ascii(datap, src));
414 if (srclen == srcbufsz - 1)
415 wide_src[srclen] = L'\0';
417 for (wide_srcp = wide_src; *wide_srcp != '\0'; wide_srcp++) {
418 if (!issafe(*wide_srcp)) {
419 need_quote = 1;
420 if (*wide_srcp == L'\'')
421 quote_count++;
425 if (!need_quote) {
426 free(wide_src);
427 return (src);
431 * See comment for quote_string_ascii(), above.
433 destbufsz = srcbufsz + 3 + 4 * quote_count;
434 wide_destp = wide_dest = safe_zalloc(destbufsz * sizeof (wchar_t));
436 *wide_destp++ = L'\'';
437 for (wide_srcp = wide_src; *wide_srcp != L'\0';
438 wide_srcp++, wide_destp++) {
439 *wide_destp = *wide_srcp;
441 if (*wide_srcp == L'\'') {
442 wide_destp[1] = L'"';
443 wide_destp[2] = L'\'';
444 wide_destp[3] = L'"';
445 wide_destp[4] = L'\'';
446 wide_destp += 4;
449 *wide_destp++ = L'\'';
450 *wide_destp = L'\0';
452 destlen = destbufsz * MB_CUR_MAX + 1;
453 uc = safe_zalloc(destlen);
454 if (wcstombs(uc, wide_dest, destlen) == (size_t)-1) {
455 /* If we've gotten this far, wcstombs shouldn't fail... */
456 (void) fprintf(stderr, "%s: wcstombs failed unexpectedly: %s\n",
457 command, strerror(errno));
458 exit(1);
461 free(wide_dest);
462 free(wide_src);
464 return (uc);
469 * Determine the locale of the target process by traversing its environment,
470 * making only one pass for efficiency's sake; stash the result in
471 * datap->pd_locale.
473 * It's possible that the process has called setlocale() to change its
474 * locale to something different, but we mostly care about making a good
475 * guess as to the locale at exec(2) time.
477 static void
478 lookup_locale(pargs_data_t *datap)
480 int i, j, composite = 0;
481 size_t len = 0;
482 char *pd_locale;
483 char *lc_all = NULL, *lang = NULL;
484 char *lcs[] = { NULL, NULL, NULL, NULL, NULL, NULL };
485 static const char *cat_names[] = {
486 "LC_CTYPE=", "LC_NUMERIC=", "LC_TIME=",
487 "LC_COLLATE=", "LC_MONETARY=", "LC_MESSAGES="
490 for (i = 0; i < datap->pd_envc; i++) {
491 char *s = datap->pd_envp_strs[i];
493 if (s == NULL)
494 continue;
496 if (strncmp("LC_ALL=", s, strlen("LC_ALL=")) == 0) {
498 * Minor optimization-- if we find LC_ALL we're done.
500 lc_all = s + strlen("LC_ALL=");
501 break;
503 for (j = 0; j <= _LastCategory; j++) {
504 if (strncmp(cat_names[j], s,
505 strlen(cat_names[j])) == 0) {
506 lcs[j] = s + strlen(cat_names[j]);
509 if (strncmp("LANG=", s, strlen("LANG=")) == 0) {
510 lang = s + strlen("LANG=");
514 if (lc_all && (*lc_all == '\0'))
515 lc_all = NULL;
516 if (lang && (*lang == '\0'))
517 lang = NULL;
519 for (i = 0; i <= _LastCategory; i++) {
520 if (lc_all != NULL) {
521 lcs[i] = lc_all;
522 } else if (lcs[i] != NULL) {
523 lcs[i] = lcs[i];
524 } else if (lang != NULL) {
525 lcs[i] = lang;
526 } else {
527 lcs[i] = "C";
529 if ((i > 0) && (lcs[i] != lcs[i-1]))
530 composite++;
532 len += 1 + strlen(lcs[i]); /* 1 extra byte for '/' */
535 if (composite == 0) {
536 /* simple locale */
537 pd_locale = safe_strdup(lcs[0]);
538 } else {
539 /* composite locale */
540 pd_locale = safe_zalloc(len + 1);
541 (void) snprintf(pd_locale, len + 1, "/%s/%s/%s/%s/%s/%s",
542 lcs[0], lcs[1], lcs[2], lcs[3], lcs[4], lcs[5]);
544 datap->pd_locale = pd_locale;
548 * Pull a string from the victim, regardless of size; this routine allocates
549 * memory for the string which must be freed by the caller.
551 static char *
552 extract_string(pargs_data_t *datap, uintptr_t addr)
554 int size = EXTRACT_BUFSZ;
555 char *result;
557 result = safe_zalloc(size);
559 for (;;) {
560 if (Pread_string(datap->pd_proc, result, size, addr) < 0) {
561 free(result);
562 return (NULL);
563 } else if (strlen(result) == (size - 1)) {
564 free(result);
565 size *= 2;
566 result = safe_zalloc(size);
567 } else {
568 break;
571 return (result);
575 * Utility function to read an array of pointers from the victim, adjusting
576 * for victim data model; returns the number of bytes successfully read.
578 static ssize_t
579 read_ptr_array(pargs_data_t *datap, uintptr_t offset, uintptr_t *buf,
580 size_t nelems)
582 ssize_t res;
584 if (dmodel == PR_MODEL_NATIVE) {
585 res = Pread(datap->pd_proc, buf, nelems * sizeof (uintptr_t),
586 offset);
587 } else {
588 int i;
589 uint32_t *arr32 = safe_zalloc(nelems * sizeof (uint32_t));
591 res = Pread(datap->pd_proc, arr32, nelems * sizeof (uint32_t),
592 offset);
593 if (res > 0) {
594 for (i = 0; i < nelems; i++)
595 buf[i] = arr32[i];
597 free(arr32);
599 return (res);
603 * Extract the argv array from the victim; store the pointer values in
604 * datap->pd_argv and the extracted strings in datap->pd_argv_strs.
606 static void
607 get_args(pargs_data_t *datap)
609 size_t argc = datap->pd_psinfo->pr_argc;
610 uintptr_t argvoff = datap->pd_psinfo->pr_argv;
611 int i;
613 datap->pd_argc = argc;
614 datap->pd_argv = safe_zalloc(argc * sizeof (uintptr_t));
616 if (read_ptr_array(datap, argvoff, datap->pd_argv, argc) <= 0) {
617 free(datap->pd_argv);
618 datap->pd_argv = NULL;
619 return;
622 datap->pd_argv_strs = safe_zalloc(argc * sizeof (char *));
623 for (i = 0; i < argc; i++) {
624 if (datap->pd_argv[i] == 0)
625 continue;
626 datap->pd_argv_strs[i] = extract_string(datap,
627 datap->pd_argv[i]);
631 /*ARGSUSED*/
632 static int
633 build_env(void *data, struct ps_prochandle *pr, uintptr_t addr, const char *str)
635 pargs_data_t *datap = data;
637 if (datap->pd_envp != NULL) {
638 if (datap->pd_envc == datap->pd_env_space) {
640 * Not enough space for storing the env (it has more
641 * items than before). Try to grow both arrays.
643 void *new = realloc(datap->pd_envp,
644 sizeof (uintptr_t) * datap->pd_env_space * 2);
645 if (new == NULL)
646 return (1);
647 datap->pd_envp = new;
649 new = realloc(datap->pd_envp_strs,
650 sizeof (char *) * datap->pd_env_space * 2);
651 if (new == NULL)
652 return (1);
653 datap->pd_envp_strs = new;
655 datap->pd_env_space *= 2;
658 datap->pd_envp[datap->pd_envc] = addr;
659 if (str == NULL)
660 datap->pd_envp_strs[datap->pd_envc] = NULL;
661 else
662 datap->pd_envp_strs[datap->pd_envc] = strdup(str);
665 datap->pd_envc++;
667 return (0);
670 static void
671 get_env(pargs_data_t *datap)
673 struct ps_prochandle *pr = datap->pd_proc;
675 datap->pd_envc = 0;
676 (void) Penv_iter(pr, build_env, datap);
678 /* We must allocate space for at least one entry */
679 datap->pd_env_space = datap->pd_envc != 0 ? datap->pd_envc : 1;
680 datap->pd_envp = safe_zalloc(sizeof (uintptr_t) * datap->pd_env_space);
681 datap->pd_envp_strs =
682 safe_zalloc(sizeof (char *) * datap->pd_env_space);
684 datap->pd_envc = 0;
685 (void) Penv_iter(pr, build_env, datap);
689 * The following at_* routines are used to decode data from the aux vector.
692 /*ARGSUSED*/
693 static void
694 at_null(long val, char *instr, size_t n, char *str)
696 str[0] = '\0';
699 /*ARGSUSED*/
700 static void
701 at_str(long val, char *instr, size_t n, char *str)
703 str[0] = '\0';
704 if (instr != NULL) {
705 (void) strlcpy(str, instr, n);
710 * Note: Don't forget to add a corresponding case to isainfo(1).
713 #define FMT_AV(s, n, hwcap, mask, name) \
714 if ((hwcap) & (mask)) \
715 (void) snprintf(s, n, "%s" name " | ", s)
717 /*ARGSUSED*/
718 static void
719 at_hwcap(long val, char *instr, size_t n, char *str)
721 #if defined(__sparc) || defined(__sparcv9)
722 (void) elfcap_hw1_to_str(ELFCAP_STYLE_UC, val, str, n,
723 ELFCAP_FMT_PIPSPACE, EM_SPARC);
725 #elif defined(__i386) || defined(__amd64)
726 (void) elfcap_hw1_to_str(ELFCAP_STYLE_UC, val, str, n,
727 ELFCAP_FMT_PIPSPACE, EM_386);
728 #else
729 #error "port me"
730 #endif
733 /*ARGSUSED*/
734 static void
735 at_hwcap2(long val, char *instr, size_t n, char *str)
737 #if defined(__sparc) || defined(__sparcv9)
738 (void) elfcap_hw2_to_str(ELFCAP_STYLE_UC, val, str, n,
739 ELFCAP_FMT_PIPSPACE, EM_SPARC);
741 #elif defined(__i386) || defined(__amd64)
742 (void) elfcap_hw2_to_str(ELFCAP_STYLE_UC, val, str, n,
743 ELFCAP_FMT_PIPSPACE, EM_386);
744 #else
745 #error "port me"
746 #endif
750 /*ARGSUSED*/
751 static void
752 at_uid(long val, char *instr, size_t n, char *str)
754 struct passwd *pw = getpwuid((uid_t)val);
756 if ((pw == NULL) || (pw->pw_name == NULL))
757 str[0] = '\0';
758 else
759 (void) snprintf(str, n, "%lu(%s)", val, pw->pw_name);
763 /*ARGSUSED*/
764 static void
765 at_gid(long val, char *instr, size_t n, char *str)
767 struct group *gr = getgrgid((gid_t)val);
769 if ((gr == NULL) || (gr->gr_name == NULL))
770 str[0] = '\0';
771 else
772 (void) snprintf(str, n, "%lu(%s)", val, gr->gr_name);
775 static struct auxfl {
776 int af_flag;
777 const char *af_name;
778 } auxfl[] = {
779 { AF_SUN_SETUGID, "setugid" },
782 /*ARGSUSED*/
783 static void
784 at_flags(long val, char *instr, size_t n, char *str)
786 int i;
788 *str = '\0';
790 for (i = 0; i < sizeof (auxfl)/sizeof (struct auxfl); i++) {
791 if ((val & auxfl[i].af_flag) != 0) {
792 if (*str != '\0')
793 (void) strlcat(str, ",", n);
794 (void) strlcat(str, auxfl[i].af_name, n);
799 #define MAX_AT_NAME_LEN 15
801 struct aux_id {
802 int aux_type;
803 const char *aux_name;
804 void (*aux_decode)(long, char *, size_t, char *);
807 static struct aux_id aux_arr[] = {
808 { AT_NULL, "AT_NULL", at_null },
809 { AT_IGNORE, "AT_IGNORE", at_null },
810 { AT_EXECFD, "AT_EXECFD", at_null },
811 { AT_PHDR, "AT_PHDR", at_null },
812 { AT_PHENT, "AT_PHENT", at_null },
813 { AT_PHNUM, "AT_PHNUM", at_null },
814 { AT_PAGESZ, "AT_PAGESZ", at_null },
815 { AT_BASE, "AT_BASE", at_null },
816 { AT_FLAGS, "AT_FLAGS", at_null },
817 { AT_ENTRY, "AT_ENTRY", at_null },
818 { AT_SUN_UID, "AT_SUN_UID", at_uid },
819 { AT_SUN_RUID, "AT_SUN_RUID", at_uid },
820 { AT_SUN_GID, "AT_SUN_GID", at_gid },
821 { AT_SUN_RGID, "AT_SUN_RGID", at_gid },
822 { AT_SUN_LDELF, "AT_SUN_LDELF", at_null },
823 { AT_SUN_LDSHDR, "AT_SUN_LDSHDR", at_null },
824 { AT_SUN_LDNAME, "AT_SUN_LDNAME", at_null },
825 { AT_SUN_LPAGESZ, "AT_SUN_LPAGESZ", at_null },
826 { AT_SUN_PLATFORM, "AT_SUN_PLATFORM", at_str },
827 { AT_SUN_EXECNAME, "AT_SUN_EXECNAME", at_str },
828 { AT_SUN_HWCAP, "AT_SUN_HWCAP", at_hwcap },
829 { AT_SUN_HWCAP2, "AT_SUN_HWCAP2", at_hwcap2 },
830 { AT_SUN_IFLUSH, "AT_SUN_IFLUSH", at_null },
831 { AT_SUN_CPU, "AT_SUN_CPU", at_null },
832 { AT_SUN_MMU, "AT_SUN_MMU", at_null },
833 { AT_SUN_LDDATA, "AT_SUN_LDDATA", at_null },
834 { AT_SUN_AUXFLAGS, "AT_SUN_AUXFLAGS", at_flags },
835 { AT_SUN_EMULATOR, "AT_SUN_EMULATOR", at_str },
836 { AT_SUN_BRANDNAME, "AT_SUN_BRANDNAME", at_str },
837 { AT_SUN_BRAND_AUX1, "AT_SUN_BRAND_AUX1", at_null },
838 { AT_SUN_BRAND_AUX2, "AT_SUN_BRAND_AUX2", at_null },
839 { AT_SUN_BRAND_AUX3, "AT_SUN_BRAND_AUX3", at_null },
840 { AT_SUN_COMMPAGE, "AT_SUN_COMMPAGE", at_null },
841 { AT_SUN_FPTYPE, "AT_SUN_FPTYPE", at_null },
842 { AT_SUN_FPSIZE, "AT_SUN_FPSIZE", at_null }
845 #define N_AT_ENTS (sizeof (aux_arr) / sizeof (struct aux_id))
848 * Return the aux_id entry for the given aux type; returns NULL if not found.
850 static struct aux_id *
851 aux_find(int type)
853 int i;
855 for (i = 0; i < N_AT_ENTS; i++) {
856 if (type == aux_arr[i].aux_type)
857 return (&aux_arr[i]);
860 return (NULL);
863 static void
864 get_auxv(pargs_data_t *datap)
866 int i;
867 const auxv_t *auxvp;
870 * Fetch the aux vector from the target process.
872 if (ps_pauxv(datap->pd_proc, &auxvp) != PS_OK)
873 return;
875 for (i = 0; auxvp[i].a_type != AT_NULL; i++)
876 continue;
878 datap->pd_auxc = i;
879 datap->pd_auxv = safe_zalloc(i * sizeof (auxv_t));
880 bcopy(auxvp, datap->pd_auxv, i * sizeof (auxv_t));
882 datap->pd_auxv_strs = safe_zalloc(datap->pd_auxc * sizeof (char *));
883 for (i = 0; i < datap->pd_auxc; i++) {
884 struct aux_id *aux = aux_find(datap->pd_auxv[i].a_type);
887 * Grab strings for those entries which have a string-decoder.
889 if ((aux != NULL) && (aux->aux_decode == at_str)) {
890 datap->pd_auxv_strs[i] =
891 extract_string(datap, datap->pd_auxv[i].a_un.a_val);
897 * Prepare to convert characters in the victim's character set into user's
898 * character set.
900 static void
901 setup_conversions(pargs_data_t *datap, int *diflocale)
903 char *mylocale = NULL, *mycharset = NULL;
904 char *targetlocale = NULL, *targetcharset = NULL;
906 mycharset = safe_strdup(nl_langinfo(CODESET));
908 mylocale = setlocale(LC_CTYPE, NULL);
909 if ((mylocale == NULL) || (strcmp(mylocale, "") == 0))
910 mylocale = "C";
911 mylocale = safe_strdup(mylocale);
913 if (datap->pd_conv_flags & CONV_STRICT_ASCII)
914 goto done;
917 * If the target's locale is "C" or "POSIX", go fast.
919 if ((strcmp(datap->pd_locale, "C") == 0) ||
920 (strcmp(datap->pd_locale, "POSIX") == 0)) {
921 datap->pd_conv_flags |= CONV_STRICT_ASCII;
922 goto done;
926 * Switch to the victim's locale, and discover its character set.
928 if (setlocale(LC_ALL, datap->pd_locale) == NULL) {
929 (void) fprintf(stderr,
930 "%s: Couldn't determine locale of target process.\n",
931 command);
932 (void) fprintf(stderr,
933 "%s: Some strings may not be displayed properly.\n",
934 command);
935 goto done;
939 * Get LC_CTYPE part of target's locale, and its codeset.
941 targetlocale = safe_strdup(setlocale(LC_CTYPE, NULL));
942 targetcharset = safe_strdup(nl_langinfo(CODESET));
945 * Now go fully back to the pargs user's locale.
947 (void) setlocale(LC_ALL, "");
950 * It's safe to bail here if the lc_ctype of the locales are the
951 * same-- we know that their encodings and characters sets are the same.
953 if (strcmp(targetlocale, mylocale) == 0)
954 goto done;
956 *diflocale = 1;
959 * If the codeset of the victim matches our codeset then iconv need
960 * not be involved.
962 if (strcmp(mycharset, targetcharset) == 0)
963 goto done;
965 if ((datap->pd_iconv = iconv_open(mycharset, targetcharset))
966 == (iconv_t)-1) {
968 * EINVAL indicates there was no conversion available
969 * from victim charset to mycharset
971 if (errno != EINVAL) {
972 (void) fprintf(stderr,
973 "%s: failed to initialize iconv: %s\n",
974 command, strerror(errno));
975 exit(1);
977 datap->pd_conv_flags |= CONV_STRICT_ASCII;
978 } else {
979 datap->pd_conv_flags |= CONV_USE_ICONV;
981 done:
982 free(mycharset);
983 free(mylocale);
984 free(targetcharset);
985 free(targetlocale);
988 static void
989 cleanup_conversions(pargs_data_t *datap)
991 if (datap->pd_conv_flags & CONV_USE_ICONV) {
992 (void) iconv_close(datap->pd_iconv);
996 static char *
997 convert_run_iconv(pargs_data_t *datap, const char *str)
999 size_t inleft, outleft, bufsz = 64;
1000 char *outstr, *outstrptr;
1001 const char *instrptr;
1003 for (;;) {
1004 outstrptr = outstr = safe_zalloc(bufsz + 1);
1005 outleft = bufsz;
1008 * Generate the "initial shift state" sequence, placing that
1009 * at the head of the string.
1011 inleft = 0;
1012 (void) iconv(datap->pd_iconv, NULL, &inleft,
1013 &outstrptr, &outleft);
1015 inleft = strlen(str);
1016 instrptr = str;
1017 if (iconv(datap->pd_iconv, &instrptr, &inleft, &outstrptr,
1018 &outleft) != (size_t)-1) {
1020 * Outstr must be null terminated upon exit from
1021 * iconv().
1023 *(outstr + (bufsz - outleft)) = '\0';
1024 break;
1025 } else if (errno == E2BIG) {
1026 bufsz *= 2;
1027 free(outstr);
1028 } else if ((errno == EILSEQ) || (errno == EINVAL)) {
1029 free(outstr);
1030 return (NULL);
1031 } else {
1033 * iconv() could in theory return EBADF, but that
1034 * shouldn't happen.
1036 (void) fprintf(stderr,
1037 "%s: iconv(3C) failed unexpectedly: %s\n",
1038 command, strerror(errno));
1040 exit(1);
1043 return (outstr);
1047 * Returns a freshly allocated string converted to the local character set,
1048 * removed of unprintable characters.
1050 static char *
1051 convert_str(pargs_data_t *datap, const char *str, int *unprintable)
1053 char *retstr, *tmp;
1055 if (datap->pd_conv_flags & CONV_STRICT_ASCII) {
1056 retstr = unctrl_str_strict_ascii(str, 1, unprintable);
1057 return (retstr);
1060 if ((datap->pd_conv_flags & CONV_USE_ICONV) == 0) {
1062 * If we aren't using iconv(), convert control chars in
1063 * the string in pargs' locale, since that is the display
1064 * locale.
1066 retstr = unctrl_str(str, 1, unprintable);
1067 return (retstr);
1071 * The logic here is a bit (ahem) tricky. Start by converting
1072 * unprintable characters *in the target's locale*. This should
1073 * eliminate a variety of unprintable or illegal characters-- in
1074 * short, it should leave us with something which iconv() won't
1075 * have trouble with.
1077 * After allowing iconv to convert characters as needed, run unctrl
1078 * again in pargs' locale-- This time to make sure that any
1079 * characters which aren't printable according to the *current*
1080 * locale (independent of the current codeset) get taken care of.
1081 * Without this second stage, we might (for example) fail to
1082 * properly handle characters converted into the 646 character set
1083 * (which are 8-bits wide), but which must be displayed in the C
1084 * locale (which uses 646, but whose printable characters are a
1085 * subset of the 7-bit characters).
1087 * Note that assuming the victim's locale using LC_ALL will be
1088 * problematic when pargs' messages are internationalized in the
1089 * future (and it calls textdomain(3C)). In this case, any
1090 * error message fprintf'd in unctrl_str() will be in the wrong
1091 * LC_MESSAGES class. We'll cross that bridge when we come to it.
1093 (void) setlocale(LC_ALL, datap->pd_locale);
1094 retstr = unctrl_str(str, 1, unprintable);
1095 (void) setlocale(LC_ALL, "");
1097 tmp = retstr;
1098 if ((retstr = convert_run_iconv(datap, retstr)) == NULL) {
1100 * In this (rare but real) case, the iconv() failed even
1101 * though we unctrl'd the string. Treat the original string
1102 * (str) as a C locale string and strip it that way.
1104 free(tmp);
1105 return (unctrl_str_strict_ascii(str, 0, unprintable));
1108 free(tmp);
1109 tmp = retstr;
1111 * Run unctrl_str, but make sure not to escape \ characters, which
1112 * may have resulted from the first round of unctrl.
1114 retstr = unctrl_str(retstr, 0, unprintable);
1115 free(tmp);
1116 return (retstr);
1120 static void
1121 convert_array(pargs_data_t *datap, char **arr, size_t count, int *unprintable)
1123 int i;
1124 char *tmp;
1126 if (arr == NULL)
1127 return;
1129 for (i = 0; i < count; i++) {
1130 if ((tmp = arr[i]) == NULL)
1131 continue;
1132 arr[i] = convert_str(datap, arr[i], unprintable);
1133 free(tmp);
1138 * Free data allocated during the gathering phase.
1140 static void
1141 free_data(pargs_data_t *datap)
1143 int i;
1145 for (i = 0; i < datap->pd_argc; i++)
1146 free(datap->pd_argv_strs[i]);
1147 free(datap->pd_argv);
1148 free(datap->pd_argv_strs);
1150 for (i = 0; i < datap->pd_envc; i++)
1151 free(datap->pd_envp_strs[i]);
1152 free(datap->pd_envp);
1153 free(datap->pd_envp_strs);
1155 for (i = 0; i < datap->pd_auxc; i++)
1156 free(datap->pd_auxv_strs[i]);
1157 free(datap->pd_auxv);
1158 free(datap->pd_auxv_strs);
1161 static void
1162 print_args(pargs_data_t *datap)
1164 int i;
1166 if (datap->pd_argv == NULL) {
1167 (void) fprintf(stderr, "%s: failed to read argv[]\n", command);
1168 return;
1171 for (i = 0; i < datap->pd_argc; i++) {
1172 (void) printf("argv[%d]: ", i);
1173 if (datap->pd_argv[i] == NULL) {
1174 (void) printf("<NULL>\n");
1175 } else if (datap->pd_argv_strs[i] == NULL) {
1176 (void) printf("<0x%0*lx>\n",
1177 (dmodel == PR_MODEL_LP64)? 16 : 8,
1178 (long)datap->pd_argv[i]);
1179 } else {
1180 (void) printf("%s\n", datap->pd_argv_strs[i]);
1185 static void
1186 print_env(pargs_data_t *datap)
1188 int i;
1190 if (datap->pd_envp == NULL) {
1191 (void) fprintf(stderr, "%s: failed to read envp[]\n", command);
1192 return;
1195 for (i = 0; i < datap->pd_envc; i++) {
1196 (void) printf("envp[%d]: ", i);
1197 if (datap->pd_envp[i] == 0) {
1198 break;
1199 } else if (datap->pd_envp_strs[i] == NULL) {
1200 (void) printf("<0x%0*lx>\n",
1201 (dmodel == PR_MODEL_LP64)? 16 : 8,
1202 (long)datap->pd_envp[i]);
1203 } else {
1204 (void) printf("%s\n", datap->pd_envp_strs[i]);
1209 static int
1210 print_cmdline(pargs_data_t *datap)
1212 int i;
1215 * Go through and check to see if we have valid data. If not, print
1216 * an error message and bail.
1218 for (i = 0; i < datap->pd_argc; i++) {
1219 if (datap->pd_argv == NULL || datap->pd_argv[i] == NULL ||
1220 datap->pd_argv_strs[i] == NULL) {
1221 (void) fprintf(stderr, "%s: target has corrupted "
1222 "argument list\n", command);
1223 return (1);
1226 datap->pd_argv_strs[i] =
1227 quote_string(datap, datap->pd_argv_strs[i]);
1230 if (datap->pd_execname == NULL) {
1231 (void) fprintf(stderr, "%s: cannot determine name of "
1232 "executable\n", command);
1233 return (1);
1236 (void) printf("%s ", datap->pd_execname);
1238 for (i = 1; i < datap->pd_argc; i++)
1239 (void) printf("%s ", datap->pd_argv_strs[i]);
1241 (void) printf("\n");
1243 return (0);
1246 static void
1247 print_auxv(pargs_data_t *datap)
1249 int i;
1250 const auxv_t *pa;
1253 * Print the names and values of all the aux vector entries.
1255 for (i = 0; i < datap->pd_auxc; i++) {
1256 char type[32];
1257 char decode[PATH_MAX];
1258 struct aux_id *aux;
1259 long v;
1260 pa = &datap->pd_auxv[i];
1262 aux = aux_find(pa->a_type);
1263 v = (long)pa->a_un.a_val;
1265 if (aux != NULL) {
1267 * Fetch aux vector type string and decoded
1268 * representation of the value.
1270 (void) strlcpy(type, aux->aux_name, sizeof (type));
1271 aux->aux_decode(v, datap->pd_auxv_strs[i],
1272 sizeof (decode), decode);
1273 } else {
1274 (void) snprintf(type, sizeof (type), "%d", pa->a_type);
1275 decode[0] = '\0';
1278 (void) printf("%-*s 0x%0*lx %s\n", MAX_AT_NAME_LEN, type,
1279 (dmodel == PR_MODEL_LP64)? 16 : 8, v, decode);
1284 main(int argc, char *argv[])
1286 int aflag = 0, cflag = 0, eflag = 0, xflag = 0, lflag = 0;
1287 int errflg = 0, retc = 0;
1288 int opt;
1289 int error = 1;
1290 core_content_t content = 0;
1292 (void) setlocale(LC_ALL, "");
1294 if ((command = strrchr(argv[0], '/')) != NULL)
1295 command++;
1296 else
1297 command = argv[0];
1299 while ((opt = getopt(argc, argv, "acelxF")) != EOF) {
1300 switch (opt) {
1301 case 'a': /* show process arguments */
1302 content |= CC_CONTENT_STACK;
1303 aflag++;
1304 break;
1305 case 'c': /* force 7-bit ascii */
1306 cflag++;
1307 break;
1308 case 'e': /* show environment variables */
1309 content |= CC_CONTENT_STACK;
1310 eflag++;
1311 break;
1312 case 'l':
1313 lflag++;
1314 aflag++; /* -l implies -a */
1315 break;
1316 case 'x': /* show aux vector entries */
1317 xflag++;
1318 break;
1319 case 'F':
1321 * Since we open the process read-only, there is no need
1322 * for the -F flag. It's a documented flag, so we
1323 * consume it silently.
1325 break;
1326 default:
1327 errflg++;
1328 break;
1332 /* -a is the default if no options are specified */
1333 if ((aflag + eflag + xflag + lflag) == 0) {
1334 aflag++;
1335 content |= CC_CONTENT_STACK;
1338 /* -l cannot be used with the -x or -e flags */
1339 if (lflag && (xflag || eflag)) {
1340 (void) fprintf(stderr, "-l is incompatible with -x and -e\n");
1341 errflg++;
1344 argc -= optind;
1345 argv += optind;
1347 if (errflg || argc <= 0) {
1348 (void) fprintf(stderr,
1349 "usage: %s [-aceFlx] { pid | core } ...\n"
1350 " (show process arguments and environment)\n"
1351 " -a: show process arguments (default)\n"
1352 " -c: interpret characters as 7-bit ascii regardless of "
1353 "locale\n"
1354 " -e: show environment variables\n"
1355 " -F: force grabbing of the target process\n"
1356 " -l: display arguments as command line\n"
1357 " -x: show aux vector entries\n", command);
1358 return (2);
1361 while (argc-- > 0) {
1362 char *arg;
1363 int gret, r;
1364 psinfo_t psinfo;
1365 char *psargs_conv;
1366 struct ps_prochandle *Pr;
1367 pargs_data_t datap;
1368 char *info;
1369 size_t info_sz;
1370 int pstate;
1371 char execname[PATH_MAX];
1372 int unprintable;
1373 int diflocale;
1375 (void) fflush(stdout);
1376 arg = *argv++;
1379 * Suppress extra blanks lines if we've encountered processes
1380 * which can't be opened.
1382 if (error == 0) {
1383 (void) printf("\n");
1385 error = 0;
1388 * First grab just the psinfo information, in case this
1389 * process is a zombie (in which case proc_arg_grab() will
1390 * fail). If so, print a nice message and continue.
1392 if (proc_arg_psinfo(arg, PR_ARG_ANY, &psinfo,
1393 &gret) == -1) {
1394 (void) fprintf(stderr, "%s: cannot examine %s: %s\n",
1395 command, arg, Pgrab_error(gret));
1396 retc++;
1397 error = 1;
1398 continue;
1401 if (psinfo.pr_nlwp == 0) {
1402 (void) printf("%d: <defunct>\n", (int)psinfo.pr_pid);
1403 continue;
1407 * If process is a "system" process (like pageout), just
1408 * print its psargs and continue on.
1410 if (psinfo.pr_size == 0 && psinfo.pr_rssize == 0) {
1411 proc_unctrl_psinfo(&psinfo);
1412 if (!lflag)
1413 (void) printf("%d: ", (int)psinfo.pr_pid);
1414 (void) printf("%s\n", psinfo.pr_psargs);
1415 continue;
1419 * Open the process readonly, since we do not need to write to
1420 * the control file.
1422 if ((Pr = proc_arg_grab(arg, PR_ARG_ANY, PGRAB_RDONLY,
1423 &gret)) == NULL) {
1424 (void) fprintf(stderr, "%s: cannot examine %s: %s\n",
1425 command, arg, Pgrab_error(gret));
1426 retc++;
1427 error = 1;
1428 continue;
1431 pstate = Pstate(Pr);
1433 if (pstate == PS_DEAD &&
1434 (Pcontent(Pr) & content) != content) {
1435 (void) fprintf(stderr, "%s: core '%s' has "
1436 "insufficient content\n", command, arg);
1437 retc++;
1438 continue;
1442 * If malloc() fails, we return here so that we can let go
1443 * of the victim, restore our locale, print a message,
1444 * then exit.
1446 if ((r = setjmp(env)) != 0) {
1447 Prelease(Pr, 0);
1448 (void) setlocale(LC_ALL, "");
1449 (void) fprintf(stderr, "%s: out of memory: %s\n",
1450 command, strerror(r));
1451 return (1);
1454 dmodel = Pstatus(Pr)->pr_dmodel;
1455 bzero(&datap, sizeof (datap));
1456 bcopy(Ppsinfo(Pr), &psinfo, sizeof (psinfo_t));
1457 datap.pd_proc = Pr;
1458 datap.pd_psinfo = &psinfo;
1460 if (cflag)
1461 datap.pd_conv_flags |= CONV_STRICT_ASCII;
1464 * Strip control characters, then record process summary in
1465 * a buffer, since we don't want to print anything out until
1466 * after we release the process.
1470 * The process is neither a system process nor defunct.
1472 * Do printing and post-processing (like name lookups) after
1473 * gathering the raw data from the process and releasing it.
1474 * This way, we don't deadlock on (for example) name lookup
1475 * if we grabbed the nscd and do 'pargs -x'.
1477 * We always fetch the environment of the target, so that we
1478 * can make an educated guess about its locale.
1480 get_env(&datap);
1481 if (aflag != 0)
1482 get_args(&datap);
1483 if (xflag != 0)
1484 get_auxv(&datap);
1487 * If malloc() fails after this poiint, we return here to
1488 * restore our locale and print a message. If we don't
1489 * reset this, we might erroneously try to Prelease a process
1490 * twice.
1492 if ((r = setjmp(env)) != 0) {
1493 (void) setlocale(LC_ALL, "");
1494 (void) fprintf(stderr, "%s: out of memory: %s\n",
1495 command, strerror(r));
1496 return (1);
1500 * For the -l option, we need a proper name for this executable
1501 * before we release it.
1503 if (lflag)
1504 datap.pd_execname = Pexecname(Pr, execname,
1505 sizeof (execname));
1507 Prelease(Pr, 0);
1510 * Crawl through the environment to determine the locale of
1511 * the target.
1513 lookup_locale(&datap);
1514 diflocale = 0;
1515 setup_conversions(&datap, &diflocale);
1517 if (lflag != 0) {
1518 unprintable = 0;
1519 convert_array(&datap, datap.pd_argv_strs,
1520 datap.pd_argc, &unprintable);
1521 if (diflocale)
1522 (void) fprintf(stderr, "%s: Warning, target "
1523 "locale differs from current locale\n",
1524 command);
1525 else if (unprintable)
1526 (void) fprintf(stderr, "%s: Warning, command "
1527 "line contains unprintable characters\n",
1528 command);
1530 retc += print_cmdline(&datap);
1531 } else {
1532 psargs_conv = convert_str(&datap, psinfo.pr_psargs,
1533 &unprintable);
1534 info_sz = strlen(psargs_conv) + MAXPATHLEN + 32 + 1;
1535 info = malloc(info_sz);
1536 if (pstate == PS_DEAD) {
1537 (void) snprintf(info, info_sz,
1538 "core '%s' of %d:\t%s\n",
1539 arg, (int)psinfo.pr_pid, psargs_conv);
1540 } else {
1541 (void) snprintf(info, info_sz, "%d:\t%s\n",
1542 (int)psinfo.pr_pid, psargs_conv);
1544 (void) printf("%s", info);
1545 free(info);
1546 free(psargs_conv);
1548 if (aflag != 0) {
1549 convert_array(&datap, datap.pd_argv_strs,
1550 datap.pd_argc, &unprintable);
1551 print_args(&datap);
1552 if (eflag || xflag)
1553 (void) printf("\n");
1556 if (eflag != 0) {
1557 convert_array(&datap, datap.pd_envp_strs,
1558 datap.pd_envc, &unprintable);
1559 print_env(&datap);
1560 if (xflag)
1561 (void) printf("\n");
1564 if (xflag != 0) {
1565 convert_array(&datap, datap.pd_auxv_strs,
1566 datap.pd_auxc, &unprintable);
1567 print_auxv(&datap);
1571 cleanup_conversions(&datap);
1572 free_data(&datap);
1575 return (retc != 0 ? 1 : 0);