/********************************************************* * Copyright (C) 1998-2015 VMware, Inc. All rights reserved. * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation version 2.1 and no later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY * or FITNESS FOR A PARTICULAR PURPOSE. See the Lesser GNU General Public * License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. * *********************************************************/ /* * systemLinux.c -- * * System-specific routines for all guest applications. * * Linux implementation * */ #if !defined(__linux__) && !defined(__FreeBSD__) && !defined(sun) && !defined(__APPLE__) # error This file should not be compiled #endif #include #include #include #include #include #include #include #include #include #include #include #ifdef sun # include #endif #include #include /* must precede for FreeBSD to compile. */ #include #include #include #include #include #if defined sun || defined __APPLE__ # include #endif #ifdef __FreeBSD__ #include "ifaddrs.h" #endif #ifdef USERWORLD #include #include #include #endif #include "vm_assert.h" #include "system.h" #include "debug.h" #include "posix.h" #include "unicode.h" #include "dynbuf.h" #include "hashTable.h" #include "strutil.h" #include "vmstdio.h" #define MAX_IFACES 4 #define LOOPBACK "lo" #ifndef INET_ADDRSTRLEN #define INET_ADDRSTRLEN 16 #endif /* * Data types */ /* * DynBuf container used by SNEForEachCallback. * * The SNE prefix is short for "System Native Environ". */ typedef struct { DynBuf *nativeEnvironStrings; // FOO=BARBAZ=BOOM DynBuf *nativeEnvironOffsets; // off_ts relative to nativeEnvironStrings->data } SNEBufs; /* * Local function prototypes */ /* * "System Native Environ" (SNE) helpers. */ static HashTable *SNEBuildHash(const char **nativeEnviron); static const char **SNEHashToEnviron(HashTable *environTable); static int SNEForEachCallback(const char *key, void *value, void *clientData); /* * Global functions */ /* *----------------------------------------------------------------------------- * * System_GetTimeMonotonic -- * * See POSIX clock_gettime(CLOCK_MONOTONIC). * * Results: * Returns monotonically increasing time in hundredths of a second on * success, -1 on failure. * * Side effects: * None. * *----------------------------------------------------------------------------- */ uint64 System_GetTimeMonotonic(void) { /* * Dummy variable b/c times(NULL) segfaults on FreeBSD 3.2 --greg */ struct tms tp; #if !defined (VM_X86_64) static uint64 base = 0; static unsigned long last = 0; uint32 current; ASSERT(sizeof(current) == sizeof(clock_t)); current = times(&tp); // 100ths of a second if (current < last) { /* The 'jiffies' kernel variable wrapped */ base += (uint64)1 << (sizeof(current) * 8); } return base + (last = current); #else // VM_X86_64 return times(&tp); #endif } /* *----------------------------------------------------------------------------- * * System_Uptime -- * * Retrieve the time (in hundredth of s.) since the system has started. * * Result: * Returns the uptime on success or -1 on failure. * * Side effects: * None. * *----------------------------------------------------------------------------- */ uint64 System_Uptime(void) { uint64 uptime = -1; #ifdef USERWORLD { VmkuserStatus_Code status; uint64 sysUptime; status = VmkuserUptime_GetUptime(&sysUptime); if (VmkuserStatus_IsOK(status)) { uptime = sysUptime / 10000; } } #elif defined(__linux__) { FILE *procStream; char *buf = NULL; size_t bufSize; uint64 sec; unsigned int csec; if (((procStream = Posix_Fopen("/proc/uptime", "r")) != NULL) && (StdIO_ReadNextLine(procStream, &buf, 80, &bufSize) == StdIO_Success) && (sscanf(buf, "%"FMT64"u.%2u", &sec, &csec) == 2)) { uptime = sec * 100 + csec; } else { Warning("%s: Unable to parse /proc/uptime.\n", __func__); } free(buf); if (procStream) { fclose(procStream); } } #elif defined sun || defined __APPLE__ { struct utmpx *boot, tmp; tmp.ut_type = BOOT_TIME; if ((boot = getutxid(&tmp)) != NULL) { struct timeval now; struct timeval *boottime = &boot->ut_tv; gettimeofday(&now, NULL); uptime = (now.tv_sec * 100 + now.tv_usec / 10000) - (boottime->tv_sec * 100 + boottime->tv_usec / 10000); } else { Warning("%s: Unable to determine boot time.\n", __func__); } endutxent(); } #else // FreeBSD { /* * FreeBSD: src/usr.bin/w/w.c rev 1.59: * "Obtain true uptime through clock_gettime(CLOCK_MONOTONIC, * struct *timespec) instead of subtracting 'bootime' from 'now'." */ struct timespec ts; if (clock_gettime(CLOCK_MONOTONIC, &ts) != -1) { uptime = ts.tv_sec * 100 + ts.tv_nsec / 10000000; } else { Warning("%s: clock_gettime: %d\n", __func__, errno); } } #endif return uptime; } /* *----------------------------------------------------------------------------- * * System_GetTimeAsString -- * * Returns the current time as a formatted string, useful for prepending * to debugging output. * * For example: "Oct 05 18:03:24.948" * * Results: * On success, allocates and returns a string containing the formatted * time. * On failure, returns NULL. * * Side effects: * None. * *----------------------------------------------------------------------------- */ Unicode System_GetTimeAsString(void) { struct timeval tv; time_t sec; int msec; size_t charsWritten; size_t bufSize = 8; // Multiplied by 2 for the initial allocation. char *buf = NULL; Unicode dateTime = NULL; Unicode output = NULL; if (gettimeofday(&tv, NULL)) { goto out; } sec = tv.tv_sec; msec = tv.tv_usec / 1000; /* * Loop repeatedly trying to format the time into a buffer, doubling the * buffer with each failure. This should be safe as the manpage for * strftime(3) seems to suggest that it only fails if the buffer isn't large * enough. * * The resultant string is encoded according to the current locale. */ do { char *newBuf; bufSize *= 2; newBuf = realloc(buf, bufSize); if (newBuf == NULL) { goto out; } buf = newBuf; charsWritten = strftime(buf, bufSize, "%b %d %H:%M:%S", localtime(&sec)); } while (charsWritten == 0); /* * Append the milliseconds field, but only after converting the date/time * string from encoding specified in the current locale to an opaque type. */ dateTime = Unicode_Alloc(buf, STRING_ENCODING_DEFAULT); if (dateTime == NULL) { goto out; } output = Unicode_Format("%s.%03d", dateTime, msec); out: free(buf); Unicode_Free(dateTime); return output; } /* *----------------------------------------------------------------------------- * * System_Shutdown -- * * Initiate system shutdown. * * Return value: * None. * * Side effects: * None. * *----------------------------------------------------------------------------- */ void System_Shutdown(Bool reboot) // IN: "reboot or shutdown" flag { char *cmd; if (reboot) { #if defined(sun) cmd = "/usr/sbin/shutdown -g 0 -i 6 -y"; #elif defined(USERWORLD) cmd = "/bin/reboot"; #else cmd = "/sbin/shutdown -r now"; #endif } else { #if __FreeBSD__ cmd = "/sbin/shutdown -p now"; #elif defined(sun) cmd = "/usr/sbin/shutdown -g 0 -i 5 -y"; #elif defined(USERWORLD) cmd = "/bin/halt"; #else cmd = "/sbin/shutdown -h now"; #endif } if (system(cmd) == -1) { fprintf(stderr, "Unable to execute %s command: \"%s\"\n", reboot ? "reboot" : "shutdown", cmd); } } /* *----------------------------------------------------------------------------- * * System_IsUserAdmin -- * * On Windows this functions checks if the calling user has membership in * the Administrators group (for NT platforms). On POSIX machines, we simply * check if the user's effective UID is root. * * Return value: * TRUE if the user has an effective UID of root. * FALSE if not. * * Side effects: * None. * *----------------------------------------------------------------------------- */ Bool System_IsUserAdmin(void) { return geteuid() == 0; } /* *---------------------------------------------------------------------- * * System_GetEnv -- * Read environment variables. * * Results: * A copy of the environment variable encoded in UTF-8. * * Side effects: * None. * *---------------------------------------------------------------------- */ char * System_GetEnv(Bool global, // IN const char *valueName) // IN: UTF-8 { char *result; result = Posix_Getenv(valueName); if (NULL != result) { result = strdup(result); } return(result); } // System_GetEnv /* *---------------------------------------------------------------------- * * System_SetEnv -- * * Write environment variables. * * On Linux, this only affects the local process. The global flag * is ignored. * * Results: * 0 if success, -1 otherwise. * * Side effects: * Changes the environment variable. * *---------------------------------------------------------------------- */ int System_SetEnv(Bool global, // IN const char *valueName, // IN: UTF-8 const char *value) // IN: UTF-8 { return Posix_Setenv(valueName, value, 1); } // System_SetEnv /* *----------------------------------------------------------------------------- * * System_GetNodeName -- * * Returns the guest's configured node name. Does not necessarily * correspond to a proper DNS host name. * * Results: * On success, returns TRUE with node name written to outBuf. * * Side effects: * None. * *----------------------------------------------------------------------------- */ Bool System_GetNodeName(size_t outBufSize, // IN: size of output buffer char *outBuf) // OUT: output buffer { ASSERT(outBuf); if (gethostname(outBuf, outBufSize) < 0) { Debug("Error, gethostname failed\n"); return FALSE; } return TRUE; } /* *----------------------------------------------------------------------------- * * System_GetNativeEnviron -- * * Returns a copy of the native / unwrapped environment. * * VMware's compatibility library wrappers override certain environment * variables to make use of shipped libraries. This creates the * "compatibility environment". Overridden variables are saved into * corresponding VMWARE_-prefixed variables. This routine recreates the * "native environment" by restoring VMWARE_-prefixed variable values to * their native equivalents. * * Every value created by the wrapper begins with a 1 or 0 to indicate * whether the value was set in the native environment. Based on this: * VMWARE_FOO="1foo" -> FOO="foo" * VMWARE_FOO="1" -> FOO="" * VMWARE_FOO="0" -> FOO is unset in the native environment * * Variables without the VMWARE_ prefix are just copied over to the new * environment. Note, of course, that VMWARE_-prefixed variables take * precedence. (I.e., when we encounter a VMWARE_ prefix, we'll * unconditionally record the value. For all other variables, we'll * copy only if that variable had not already been set.) * * Results: * An array of strings representing the native environment. (I.e., * use it as you would environ(7).) NB: Memory allocation failures * are considered fatal. * * Side effects: * This routine allocates memory. It's up to the caller to free it by * passing the returned value to System_FreeNativeEnviron. * * This routine assumes that keys will show up in environ only once each. * *----------------------------------------------------------------------------- */ const char ** System_GetNativeEnviron(const char **compatEnviron) // IN: original "compatibility" environment { HashTable *environTable; const char **nativeEnviron; environTable = SNEBuildHash(compatEnviron); nativeEnviron = SNEHashToEnviron(environTable); HashTable_Free(environTable); return nativeEnviron; } /* *----------------------------------------------------------------------------- * * System_FreeNativeEnviron -- * * Frees memory allocated by System_GetNativeEnviron. * * Results: * Frees memory. * * Side effects: * None. * *----------------------------------------------------------------------------- */ void System_FreeNativeEnviron(const char **nativeEnviron) // IN: environment returned by System_GetNativeEnviron { /* * nativeEnviron is an array of strings, and all of the strings are located in a * single contiguous buffer. This makes freeing both array and buffer easy. */ char *stringBuf = (char *)*nativeEnviron; free(stringBuf); free(nativeEnviron); } /* * Local functions */ /* *----------------------------------------------------------------------------- * * SNEBuildHash -- * * Compile hash table of environment variables. See System_GetNativeEnviron * for rules on precedence. * * Results: * Pointer to populated hash table. This cannot fail, as any memory failures * within the HashTable and StrUtil libraries are considered fatal. * * Side effects: * Caller is responsible for freeing returned table with HashTable_Free. * *----------------------------------------------------------------------------- */ static HashTable * SNEBuildHash(const char **compatEnviron) // IN: original "compatibility" environment { HashTable *environTable; const char **p; /* * Number of buckets picked arbitrarily. We're more interested in having an * associative array than raw table performance. */ environTable = HashTable_Alloc(64, HASH_STRING_KEY | HASH_FLAG_COPYKEY, free); for (p = compatEnviron; p && *p; p++) { const size_t prefixLength = sizeof "VMWARE_" - 1; char *key; char *value; unsigned int index; index = 0; key = StrUtil_GetNextToken(&index, *p, "="); if (!key) { /* XXX Must empty environment variables still contain a '=' delimiter? */ Debug("%s: Encountered environment entry without '='.\n", __func__); continue; } /* * Copy the value beginning after the '=' delimiter (even if it's empty). */ ++index; value = Util_SafeStrdup(&(*p)[index]); if (StrUtil_StartsWith(key, "VMWARE_") && key[prefixLength] != '\0' && (value[0] == '0' || value[0] == '1')) { /* * Okay, this appears to be one of the wrapper's variables, so let's * figure out the original environment variable name (by just indexing * past the prefix) and value (by indexing past the "was this variable * in the native environment?" marker). * * XXX Should we move this marker to a separate header? */ char *realKey = &key[prefixLength]; char *realValue = (value[0] == '0') ? NULL : Util_SafeStrdup(&value[1]); HashTable_ReplaceOrInsert(environTable, realKey, realValue); } else { HashTable_LookupOrInsert(environTable, key, value); } /* * The hash table makes a copy of our key, and it takes ownership of inserted * values (via our passed freeing function). So that means we're responsible * for freeing 'key', but -not- 'value'. */ free(key); } return environTable; } /* *----------------------------------------------------------------------------- * * SNEHashToEnviron -- * * Builds up an array of strings representing a new environment based on * the caller's hash table. * * Results: * Pointer to the new environment. As memory allocation failures are * considered fatal, this routine will not return NULL. * * Side effects: * This is expected to be returned to System_GetNativeEnviron's caller, * and s/he is to free it via System_FreeNativeEnviron. * *----------------------------------------------------------------------------- */ static const char ** SNEHashToEnviron(HashTable *environTable) // IN: { DynBuf nativeEnvironOffsets; DynBuf nativeEnvironStrings; SNEBufs anonBufs = { .nativeEnvironStrings = &nativeEnvironStrings, .nativeEnvironOffsets = &nativeEnvironOffsets, }; const char **nativeEnviron; off_t *offsetIter; char *stringsBase; unsigned int numStrings; unsigned int i; DynBuf_Init(&nativeEnvironStrings); DynBuf_Init(&nativeEnvironOffsets); /* * Write out strings and string offsets to the dynbufs. */ HashTable_ForEach(environTable, SNEForEachCallback, &anonBufs); ASSERT_MEM_ALLOC(DynBuf_Trim(&nativeEnvironStrings)); /* * Allocate final buffer to contain the string array. Include extra entry * for terminator. */ numStrings = DynBuf_GetSize(&nativeEnvironOffsets) / sizeof *offsetIter; nativeEnviron = Util_SafeCalloc(1 + numStrings, sizeof *nativeEnviron); stringsBase = DynBuf_Get(&nativeEnvironStrings); offsetIter = DynBuf_Get(&nativeEnvironOffsets); for (i = 0; i < numStrings; i++, offsetIter++) { nativeEnviron[i] = stringsBase + *offsetIter; } nativeEnviron[i] = NULL; /* * Cleanup. * * Note the subtle difference in how these are handled: * - The offsets are used only to build the array of strings, which is why that * dynbuf is destroyed. * - The buffer containing all of the environment strings, however, is persistent * and is actually returned to the caller (indirectly via nativeEnviron). As * such it's only detached from the DynBuf. */ DynBuf_Destroy(&nativeEnvironOffsets); DynBuf_Detach(&nativeEnvironStrings); return nativeEnviron; } /* *----------------------------------------------------------------------------- * * SNEForEachCallback -- * * Given a key (environment variable name) and value, appends a string of * "key=value" to the nativeEnvironStrings DynBuf. Also appends a pointer * to the new string to the nativeEnviron DynBuf. * * Results: * Always zero. Memory allocation failures are considered fatal. * * Side effects: * None. * *----------------------------------------------------------------------------- */ static int SNEForEachCallback(const char *key, // IN: environment variable void *value, // IN: environment value void *clientData) // IN/OUT: DynBuf container (SNEBufs) { DynBuf *nativeEnvironStrings = ((SNEBufs *)clientData)->nativeEnvironStrings; DynBuf *nativeEnvironOffsets = ((SNEBufs *)clientData)->nativeEnvironOffsets; size_t itemSize; char *itemBuf; off_t itemOffset; /* * A NULL value indicates that this variable is not to be set. */ if (value == NULL) { return 0; } /* Determine the length of the new string inc. '=' delimiter and NUL. */ itemSize = strlen(key) + strlen(value) + sizeof "="; itemBuf = Util_SafeMalloc(itemSize); /* Create new "key=value" string. */ snprintf(itemBuf, itemSize, "%s=%s", key, (char *)value); ASSERT_MEM_ALLOC(DynBuf_AppendString(nativeEnvironStrings, itemBuf)); /* * Get the relative offset of our newly added string (relative to the DynBuf's base * address), and then append that to nativeEnvironOffsets. */ itemOffset = DynBuf_GetSize(nativeEnvironStrings) - itemSize; ASSERT_MEM_ALLOC(DynBuf_Append(nativeEnvironOffsets, &itemOffset, sizeof itemOffset)); free(itemBuf); return 0; }