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