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 }