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