pargs.c revision 23a268cfbc75530b746495f3e157b9bc71069420
2N/A * The contents of this file are subject to the terms of the 2N/A * Common Development and Distribution License (the "License"). 2N/A * You may not use this file except in compliance with the License. 2N/A * See the License for the specific language governing permissions 2N/A * and limitations under the License. 2N/A * When distributing Covered Code, include this CDDL HEADER in each 2N/A * If applicable, add the following below this CDDL HEADER, with the 2N/A * fields enclosed by brackets "[]" replaced with your own identifying 2N/A * information: Portions Copyright [yyyy] [name of copyright owner] 2N/A * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 2N/A * Use is subject to license terms. 2N/A * Copyright (c) 2013, Joyent, Inc. All rights reserved. 2N/A * pargs examines and prints the arguments (argv), environment (environ), 2N/A * and auxiliary vector of another process. 2N/A * This utility is made more complex because it must run in internationalized 2N/A * environments. The two key cases for pargs to manage are: 2N/A * 1. pargs and target run in the same locale: pargs must respect the 2N/A * locale, but this case is straightforward. Care is taken to correctly 2N/A * use wide characters in order to print results properly. 2N/A * 2. pargs and target run in different locales: in this case, pargs examines 2N/A * the string having assumed the victim's locale. Unprintable (but valid) 2N/A * characters are escaped. Next, iconv(3c) is used to convert between the 2N/A * target and pargs codeset. Finally, a second pass to escape unprintable 2N/A * (but valid) characters is made. 2N/A * In any case in which characters are encountered which are not valid in 2N/A * their purported locale, the string "fails" and is treated as a traditional 2N/A * 7-bit ASCII encoded string, and escaped accordingly. #
define ENV_CHUNK 16 /* #env ptrs to read at a time */ * If the malloc fails we longjmp out to allow the code to Prelease() * a stopped victim if needed. * Given a wchar_t which might represent an 'escapable' sequence (see * formats(5)), return the base ascii character needed to print that * The comparisons performed may look suspect at first, but all are valid; * the characters below all appear in the "Portable Character Set." The * Single Unix Spec says: "The wide-character value for each member of the * Portable Character Set will equal its value when used as the lone * character in an integer character constant." while ((c = *
src++) !=
'\0') {
* Call get_interp_char *first*, since \ will otherwise not *
ucp++ = ((c >>
6) &
7) +
'0';
*
ucp++ = ((c >>
3) &
7) +
'0';
* Convert control characters as described in format(5) to their readable * representation; special care is taken to handle multibyte character sets. * If escape_slash is true, escaping of '\' occurs. The first time a string * is unctrl'd, this should be '1'. Subsequent iterations over the same * string should set escape_slash to 0. Otherwise you'll wind up with * We can't trust the string, since in the locale in which * this call is operating, the string contains an invalid * multibyte sequence. There isn't much to do here, so * convert the string byte by byte to wide characters, as * if it came from a C locale (char) string. This isn't * perfect, but at least the characters will make it to * Print "interpreted version" (\n, \a, etc). * Convert the wide char back into (potentially several) * multibyte characters, then escape out each of those bytes. * This is a totally invalid wide char; discard it. for (i = 0; i <
len; i++) {
/* If we've gotten this far, wcstombs shouldn't fail... */ * Try to save memory; don't waste 3 * strlen in the * These functions determine which characters are safe to be left unquoted. * Rather than starting with every printable character and subtracting out the * shell metacharacters, we take the more conservative approach of starting with * a set of safe characters and adding those few common punctuation characters * which are known to be safe. The rules are: * If this is a printable character (graph), and not punctuation, it is * safe to leave unquoted. * If it's one of known hard-coded safe characters, it's also safe to leave * Otherwise, the entire argument must be quoted. * This will cause some strings to be unecessarily quoted, but it is safer than * having a character unintentionally interpreted by the shell. * The only character we care about here is a single quote. All the * other unprintable characters (and backslashes) will have been dealt * with by unctrl_str(). We make the following subtitution when we * encounter a single quote: * In addition, we put single quotes around the entire argument. For * foo'bar = 'foo'"'"'bar' * See comment for quote_string_ascii(), above. /* If we've gotten this far, wcstombs shouldn't fail... */ * Determine the locale of the target process by traversing its environment, * making only one pass for efficiency's sake; stash the result in * It's possible that the process has called setlocale() to change its * locale to something different, but we mostly care about making a good * guess as to the locale at exec(2) time. "LC_CTYPE=",
"LC_NUMERIC=",
"LC_TIME=",
"LC_COLLATE=",
"LC_MONETARY=",
"LC_MESSAGES=" * Minor optimization-- if we find LC_ALL we're done. if ((i > 0) && (
lcs[i] !=
lcs[i-
1]))
* Pull a string from the victim, regardless of size; this routine allocates * memory for the string which must be freed by the caller. * Utility function to read an array of pointers from the victim, adjusting * for victim data model; returns the number of bytes successfully read. * Extract the argv array from the victim; store the pointer values in * datap->pd_argv and the extracted strings in datap->pd_argv_strs. for (i = 0; i <
argc; i++) {
/* env has more items than last time, skip the newer ones */ * The following at_* routines are used to decode data from the aux vector. * Note: Don't forget to add a corresponding case to isainfo(1). for (i = 0; i <
sizeof (
auxfl)/
sizeof (
struct auxfl); i++) {
* Return the aux_id entry for the given aux type; returns NULL if not found. * Fetch the aux vector from the target process. * Grab strings for those entries which have a string-decoder. * Prepare to convert characters in the victim's character set into user's * If the target's locale is "C" or "POSIX", go fast. * Switch to the victim's locale, and discover its character set. "%s: Couldn't determine locale of target process.\n",
"%s: Some strings may not be displayed properly.\n",
* Get LC_CTYPE part of target's locale, and its codeset. * Now go fully back to the pargs user's locale. * It's safe to bail here if the lc_ctype of the locales are the * same-- we know that their encodings and characters sets are the same. * If the codeset of the victim matches our codeset then iconv need * EINVAL indicates there was no conversion available * from victim charset to mycharset "%s: failed to initialize iconv: %s\n",
* Generate the "initial shift state" sequence, placing that * at the head of the string. * Outstr must be null terminated upon exit from * iconv() could in theory return EBADF, but that "%s: iconv(3C) failed unexpectedly: %s\n",
* Returns a freshly allocated string converted to the local character set, * removed of unprintable characters. * If we aren't using iconv(), convert control chars in * the string in pargs' locale, since that is the display * The logic here is a bit (ahem) tricky. Start by converting * unprintable characters *in the target's locale*. This should * eliminate a variety of unprintable or illegal characters-- in * short, it should leave us with something which iconv() won't * After allowing iconv to convert characters as needed, run unctrl * again in pargs' locale-- This time to make sure that any * characters which aren't printable according to the *current* * locale (independent of the current codeset) get taken care of. * Without this second stage, we might (for example) fail to * properly handle characters converted into the 646 character set * (which are 8-bits wide), but which must be displayed in the C * locale (which uses 646, but whose printable characters are a * subset of the 7-bit characters). * Note that assuming the victim's locale using LC_ALL will be * problematic when pargs' messages are internationalized in the * future (and it calls textdomain(3C)). In this case, any * error message fprintf'd in unctrl_str() will be in the wrong * LC_MESSAGES class. We'll cross that bridge when we come to it. * In this (rare but real) case, the iconv() failed even * though we unctrl'd the string. Treat the original string * (str) as a C locale string and strip it that way. * Run unctrl_str, but make sure not to escape \ characters, which * may have resulted from the first round of unctrl. for (i = 0; i <
count; i++) {
* Free data allocated during the gathering phase. (
void)
printf(
"argv[%d]: ", i);
(
void)
printf(
"envp[%d]: ", i);
* Go through and check to see if we have valid data. If not, print * an error message and bail. * Print the names and values of all the aux vector entries. * Fetch aux vector type string and decoded * representation of the value. case 'a':
/* show process arguments */ case 'c':
/* force 7-bit ascii */ case 'e':
/* show environment variables */ aflag++;
/* -l implies -a */ case 'x':
/* show aux vector entries */ * Since we open the process read-only, there is no need * for the -F flag. It's a documented flag, so we /* -a is the default if no options are specified */ /* -l cannot be used with the -x or -e flags */ "usage: %s [-aceFlx] { pid | core } ...\n" " (show process arguments and environment)\n" " -a: show process arguments (default)\n" " -c: interpret characters as 7-bit ascii regardless of " " -e: show environment variables\n" " -F: force grabbing of the target process\n" " -l: display arguments as command line\n" " -x: show aux vector entries\n",
command);
* Suppress extra blanks lines if we've encountered processes * First grab just the psinfo information, in case this * process is a zombie (in which case proc_arg_grab() will * fail). If so, print a nice message and continue. * If process is a "system" process (like pageout), just * print its psargs and continue on. * Open the process readonly, since we do not need to write to * If malloc() fails, we return here so that we can let go * of the victim, restore our locale, print a message, * Strip control characters, then record process summary in * a buffer, since we don't want to print anything out until * after we release the process. * The process is neither a system process nor defunct. * Do printing and post-processing (like name lookups) after * gathering the raw data from the process and releasing it. * This way, we don't deadlock on (for example) name lookup * if we grabbed the nscd and do 'pargs -x'. * We always fetch the environment of the target, so that we * can make an educated guess about its locale. * If malloc() fails after this poiint, we return here to * restore our locale and print a message. If we don't * reset this, we might erroneously try to Prelease a process * For the -l option, we need a proper name for this executable * Crawl through the environment to determine the locale of "locale differs from current locale\n",
"line contains unprintable characters\n",
"core '%s' of %d:\t%s\n",
return (
retc != 0 ?
1 : 0);