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