/** * Validates the extension pack version string. * * @returns true if valid, false if not. * @param pszVersion The version string to validate. */ bool VBoxExtPackIsValidVersionString(const char *pszVersion) { if (!pszVersion || *pszVersion == '\0') return false; /* 1.x.y.z... */ for (;;) { if (!RT_C_IS_DIGIT(*pszVersion)) return false; do pszVersion++; while (RT_C_IS_DIGIT(*pszVersion)); if (*pszVersion != '.') break; pszVersion++; } /* upper case string + numbers indicating the build type */ if (*pszVersion == '-' || *pszVersion == '_') { /** @todo Should probably restrict this to known build types (alpha, * beta, rc, ++). */ do pszVersion++; while ( RT_C_IS_DIGIT(*pszVersion) || RT_C_IS_UPPER(*pszVersion) || *pszVersion == '-' || *pszVersion == '_'); } return *pszVersion == '\0'; }
/** * Converts the fraction part of a generalized time into nanoseconds. * * @returns IPRT status code. * @param pCursor The cursor to use when reporting an error. * @param pchFraction Pointer to the start of the fraction (dot). * @param cchFraction The length of the fraction. * @param pThis The time object we're working on, * Time.u32Nanoseconds will be update. * @param pszErrorTag The error tag. */ static int rtAsn1Time_ConvertGeneralizedTimeFraction(PRTASN1CURSOR pCursor, const char *pchFraction, uint32_t cchFraction, PRTASN1TIME pThis, const char *pszErrorTag) { pThis->Time.u32Nanosecond = 0; /* * Check the dot. */ if (*pchFraction != '.') return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING, "%s: Expected GeneralizedTime fraction dot, found: '%c' ('%.*s')", pszErrorTag, *pchFraction, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch); pchFraction++; cchFraction--; if (!cchFraction) return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING, "%s: No digit following GeneralizedTime fraction dot: '%.*s'", pszErrorTag, pThis->Asn1Core.cb, pThis->Asn1Core); /* * Do the conversion. */ char chLastDigit; uint32_t uMult = 100000000; do { char chDigit = chLastDigit = *pchFraction; if (!RT_C_IS_DIGIT(chDigit)) return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING, "%s: Bad GeneralizedTime fraction digit: '%.*s'", pszErrorTag, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch); pThis->Time.u32Nanosecond += uMult * (uint32_t)(chDigit - '0'); /* Advance */ cchFraction--; pchFraction++; uMult /= 10; } while (cchFraction > 0 && uMult > 0); /* * Lazy bird: For now, we don't permit higher resolution than we can * internally represent. Deal with this if it ever becomes an issue. */ if (cchFraction > 0) return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING, "%s: Bad GeneralizedTime fraction too long: '%.*s'", pszErrorTag, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch); if (chLastDigit == '0') return RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING, "%s: Trailing zeros not allowed for GeneralizedTime: '%.*s'", pszErrorTag, pThis->Asn1Core.cb, pThis->Asn1Core.uData.pch); return VINF_SUCCESS; }
static boolean_t vboxSolarisAddLinkHostIface(const char *pszIface, void *pvHostNetworkInterfaceList) { /* * Skip IPSEC interfaces. It's at IP level. */ if (!strncmp(pszIface, "ip.tun", 6)) return _B_FALSE; /* * Skip our own dynamic VNICs but don't skip VNIC templates. * These names originate from VBoxNetFltBow-solaris.c, hardcoded here for now. */ if ( strncmp(pszIface, "vboxvnic_template", 17) && !strncmp(pszIface, "vboxvnic", 8)) return _B_FALSE; /* * Clip off the zone instance number from the interface name (if any). */ char szIfaceName[128]; strcpy(szIfaceName, pszIface); char *pszColon = (char *)memchr(szIfaceName, ':', sizeof(szIfaceName)); if (pszColon) *pszColon = '\0'; /* * Get the instance number from the interface name, then clip it off. */ int cbInstance = 0; int cbIface = strlen(szIfaceName); const char *pszEnd = pszIface + cbIface - 1; for (int i = 0; i < cbIface - 1; i++) { if (!RT_C_IS_DIGIT(*pszEnd)) break; cbInstance++; pszEnd--; } int Instance = atoi(pszEnd + 1); strncpy(szIfaceName, pszIface, cbIface - cbInstance); szIfaceName[cbIface - cbInstance] = '\0'; /* * Add the interface. */ vboxSolarisAddHostIface(szIfaceName, Instance, pvHostNetworkInterfaceList); /* * Continue walking... */ return _B_FALSE; }
char *CollectorLinux::trimTrailingDigits(char *pszName) { unsigned cbName = strlen(pszName); if (cbName == 0) return pszName; char *pszEnd = pszName + cbName - 1; while (pszEnd > pszName && (RT_C_IS_DIGIT(*pszEnd) || *pszEnd == '\n')) pszEnd--; pszEnd[1] = '\0'; return pszName; }
/* static */ DECLCALLBACK(int) Guest::i_staticEnumStatsCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit, STAMVISIBILITY enmVisiblity, const char *pszDesc, void *pvUser) { AssertLogRelMsgReturn(enmType == STAMTYPE_COUNTER, ("Unexpected sample type %d ('%s')\n", enmType, pszName), VINF_SUCCESS); AssertLogRelMsgReturn(enmUnit == STAMUNIT_BYTES, ("Unexpected sample unit %d ('%s')\n", enmUnit, pszName), VINF_SUCCESS); /* Get the base name w/ slash. */ const char *pszLastSlash = strrchr(pszName, '/'); AssertLogRelMsgReturn(pszLastSlash, ("Unexpected sample '%s'\n", pszName), VINF_SUCCESS); /* Receive or transmit? */ bool fRx; if (!strcmp(pszLastSlash, "/BytesReceived")) fRx = true; else if (!strcmp(pszLastSlash, "/BytesTransmitted")) fRx = false; else AssertLogRelMsgFailedReturn(("Unexpected sample '%s'\n", pszName), VINF_SUCCESS); #if 0 /* not used for anything, so don't bother parsing it. */ /* Find start of instance number. ASSUMES '/Public/Net/Name<Instance digits>/Bytes...' */ do --pszLastSlash; while (pszLastSlash > pszName && RT_C_IS_DIGIT(*pszLastSlash)); pszLastSlash++; uint8_t uInstance; int rc = RTStrToUInt8Ex(pszLastSlash, NULL, 10, &uInstance); AssertLogRelMsgReturn(RT_SUCCESS(rc) && rc != VWRN_NUMBER_TOO_BIG && rc != VWRN_NEGATIVE_UNSIGNED, ("%Rrc '%s'\n", rc, pszName), VINF_SUCCESS) #endif /* Add the bytes to our counters. */ PSTAMCOUNTER pCnt = (PSTAMCOUNTER)pvSample; Guest *pGuest = (Guest *)pvUser; uint64_t cb = pCnt->c; #if 0 LogFlowFunc(("%s i=%u d=%s %llu bytes\n", pszName, uInstance, fRx ? "RX" : "TX", cb)); #else LogFlowFunc(("%s d=%s %llu bytes\n", pszName, fRx ? "RX" : "TX", cb)); #endif if (fRx) pGuest->mNetStatRx += cb; else pGuest->mNetStatTx += cb; return VINF_SUCCESS; }
/** * Use the partition name to get the name of the disk. Any path component is stripped. * if fTrimDigits is true, trailing digits are stripped as well, for example '/dev/sda5' * is converted to 'sda'. * * @param pszDiskName Where to store the name of the disk. * @param cbDiskName The size of the buffer pszDiskName points to. * @param pszDevName The device name used to get the disk name. * @param fTrimDigits Trim trailing digits (e.g. /dev/sda5) */ void CollectorLinux::getDiskName(char *pszDiskName, size_t cbDiskName, const char *pszDevName, bool fTrimDigits) { unsigned cbName = 0; unsigned cbDevName = strlen(pszDevName); const char *pszEnd = pszDevName + cbDevName - 1; if (fTrimDigits) while (pszEnd > pszDevName && RT_C_IS_DIGIT(*pszEnd)) pszEnd--; while (pszEnd > pszDevName && *pszEnd != '/') { cbName++; pszEnd--; } RTStrCopy(pszDiskName, RT_MIN(cbName + 1, cbDiskName), pszEnd + 1); }
/** * Validates the extension pack edition string. * * @returns true if valid, false if not. * @param pszEdition The edition string to validate. */ bool VBoxExtPackIsValidEditionString(const char *pszEdition) { if (*pszEdition) { if (!RT_C_IS_UPPER(*pszEdition)) return false; do pszEdition++; while ( RT_C_IS_UPPER(*pszEdition) || RT_C_IS_DIGIT(*pszEdition) || *pszEdition == '-' || *pszEdition == '_'); } return *pszEdition == '\0'; }
/** * @interface_method_impl{RTVFSCHAINELEMENTREG,pfnValidate} */ static DECLCALLBACK(int) rtVfsChainGzip_Validate(PCRTVFSCHAINELEMENTREG pProviderReg, PRTVFSCHAINSPEC pSpec, PRTVFSCHAINELEMSPEC pElement, uint32_t *poffError, PRTERRINFO pErrInfo) { RT_NOREF(pProviderReg); /* * Basics. */ if (pElement->enmType != RTVFSOBJTYPE_IO_STREAM) return VERR_VFS_CHAIN_ONLY_IOS; if (pElement->enmTypeIn == RTVFSOBJTYPE_INVALID) return VERR_VFS_CHAIN_CANNOT_BE_FIRST_ELEMENT; if ( pElement->enmTypeIn != RTVFSOBJTYPE_FILE && pElement->enmTypeIn != RTVFSOBJTYPE_IO_STREAM) return VERR_VFS_CHAIN_TAKES_FILE_OR_IOS; if (pSpec->fOpenFile & RTFILE_O_READ) return VERR_VFS_CHAIN_WRITE_ONLY_IOS; if (pElement->cArgs > 1) return VERR_VFS_CHAIN_AT_MOST_ONE_ARG; /* * Optional argument 1..9 indicating the compression level. * We store it in pSpec->uProvider. */ if (pElement->cArgs > 0) { const char *psz = pElement->paArgs[0].psz; if (!*psz || !strcmp(psz, "default")) pElement->uProvider = 6; else if (!strcmp(psz, "fast")) pElement->uProvider = 3; else if ( RT_C_IS_DIGIT(*psz) && *psz != '0' && *RTStrStripL(psz + 1) == '\0') pElement->uProvider = *psz - '0'; else { *poffError = pElement->paArgs[0].offSpec; return RTErrInfoSet(pErrInfo, VERR_VFS_CHAIN_INVALID_ARGUMENT, "Expected compression level: 1-9, default, or fast"); } } else pElement->uProvider = 6; return VINF_SUCCESS; }
/* static */ int Guest::staticEnumStatsCallback(const char *pszName, STAMTYPE enmType, void *pvSample, STAMUNIT enmUnit, STAMVISIBILITY enmVisiblity, const char *pszDesc, void *pvUser) { PSTAMCOUNTER pCnt = (PSTAMCOUNTER)pvSample; char *pszEnd = strrchr((char*)pszName, '/'); if (pszEnd) { bool fRx; uint8_t uInstance = 0; switch (pszEnd[1]) { case 'R': fRx = true; break; case 'T': fRx = false; break; default: LogRel(("Failed to parse the name of network stat counter (unknown counter): %s\n", pszName)); return VINF_SUCCESS; } do --pszEnd; while (pszEnd > pszName && RT_C_IS_DIGIT(*pszEnd)); if (RT_SUCCESS(RTStrToUInt8Ex(pszEnd + 1, NULL, 10, &uInstance))) { Guest *pGuest = (Guest *)pvUser; LogFlowFunc(("%s i=%u d=%s %llu %s\n", pszName, uInstance, fRx ? "RX" : "TX", pCnt->c, STAMR3GetUnit(enmUnit))); if (fRx) pGuest->mNetStatRx += pCnt->c; else pGuest->mNetStatTx += pCnt->c; } else LogRel(("Failed to extract the device instance from the name of network stat counter: %s\n", pszEnd)); } else LogRel(("Failed to parse the name of network stat counter (no slash): %s\n", pszName)); return VINF_SUCCESS; }
static void test3(void) { RTTestISub("> 127"); for (int ch = 128; ch < 2000000; ch++) { RTTESTI_CHECK(!RT_C_IS_CNTRL(ch)); RTTESTI_CHECK(!RT_C_IS_SPACE(ch)); RTTESTI_CHECK(!RT_C_IS_BLANK(ch)); RTTESTI_CHECK(!RT_C_IS_PRINT(ch)); RTTESTI_CHECK(!RT_C_IS_PUNCT(ch)); RTTESTI_CHECK(!RT_C_IS_GRAPH(ch)); RTTESTI_CHECK(!RT_C_IS_DIGIT(ch)); RTTESTI_CHECK(!RT_C_IS_XDIGIT(ch)); RTTESTI_CHECK(!RT_C_IS_ODIGIT(ch)); RTTESTI_CHECK(!RT_C_IS_ALPHA(ch)); RTTESTI_CHECK(!RT_C_IS_UPPER(ch)); RTTESTI_CHECK(!RT_C_IS_LOWER(ch)); } }
static void test2(void) { RTTestISub("< 0"); for (int ch = -1; ch > -2000000; ch--) { RTTESTI_CHECK(!RT_C_IS_CNTRL(ch)); RTTESTI_CHECK(!RT_C_IS_SPACE(ch)); RTTESTI_CHECK(!RT_C_IS_BLANK(ch)); RTTESTI_CHECK(!RT_C_IS_PRINT(ch)); RTTESTI_CHECK(!RT_C_IS_PUNCT(ch)); RTTESTI_CHECK(!RT_C_IS_GRAPH(ch)); RTTESTI_CHECK(!RT_C_IS_DIGIT(ch)); RTTESTI_CHECK(!RT_C_IS_XDIGIT(ch)); RTTESTI_CHECK(!RT_C_IS_ODIGIT(ch)); RTTESTI_CHECK(!RT_C_IS_ALPHA(ch)); RTTESTI_CHECK(!RT_C_IS_UPPER(ch)); RTTESTI_CHECK(!RT_C_IS_LOWER(ch)); } }
uint32_t CollectorSolaris::getInstance(const char *pszIfaceName, char *pszDevName) { /* * Get the instance number from the interface name, then clip it off. */ int cbInstance = 0; int cbIface = strlen(pszIfaceName); const char *pszEnd = pszIfaceName + cbIface - 1; for (int i = 0; i < cbIface - 1; i++) { if (!RT_C_IS_DIGIT(*pszEnd)) break; cbInstance++; pszEnd--; } uint32_t uInstance = RTStrToUInt32(pszEnd + 1); strncpy(pszDevName, pszIfaceName, cbIface - cbInstance); pszDevName[cbIface - cbInstance] = '\0'; return uInstance; }
/** * Searches for a long option. * * @returns Pointer to a matching option. * @param pszOption The alleged long option. * @param paOptions Option array. * @param cOptions Number of items in the array. * @param fFlags Init flags. */ static PCRTGETOPTDEF rtGetOptSearchLong(const char *pszOption, PCRTGETOPTDEF paOptions, size_t cOptions, uint32_t fFlags) { PCRTGETOPTDEF pOpt = paOptions; while (cOptions-- > 0) { if (pOpt->pszLong) { if ((pOpt->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING) { /* * A value is required with the argument. We're trying to be * understanding here and will permit any of the following: * --long12:value, --long12=value, --long12 value, * --long:value, --long=value, --long value, * * If the option is index, then all trailing chars must be * digits. For error reporting reasons we also match where * there is no index. */ size_t cchLong = strlen(pOpt->pszLong); if ( !strncmp(pszOption, pOpt->pszLong, cchLong) || ( pOpt->fFlags & RTGETOPT_FLAG_ICASE && !RTStrNICmp(pszOption, pOpt->pszLong, cchLong))) { if (pOpt->fFlags & RTGETOPT_FLAG_INDEX) while (RT_C_IS_DIGIT(pszOption[cchLong])) cchLong++; if ( pszOption[cchLong] == '\0' || pszOption[cchLong] == ':' || pszOption[cchLong] == '=') return pOpt; } } else if (pOpt->fFlags & RTGETOPT_FLAG_INDEX) { /* * The option takes an index but no value. * As above, we also match where there is no index. */ size_t cchLong = strlen(pOpt->pszLong); if ( !strncmp(pszOption, pOpt->pszLong, cchLong) || ( pOpt->fFlags & RTGETOPT_FLAG_ICASE && !RTStrNICmp(pszOption, pOpt->pszLong, cchLong))) { while (RT_C_IS_DIGIT(pszOption[cchLong])) cchLong++; if (pszOption[cchLong] == '\0') return pOpt; } } else if ( !strcmp(pszOption, pOpt->pszLong) || ( pOpt->fFlags & RTGETOPT_FLAG_ICASE && !RTStrICmp(pszOption, pOpt->pszLong))) return pOpt; } pOpt++; } if (!(fFlags & RTGETOPTINIT_FLAGS_NO_STD_OPTS)) for (uint32_t i = 0; i < RT_ELEMENTS(g_aStdOptions); i++) if ( !strcmp(pszOption, g_aStdOptions[i].pszLong) || ( g_aStdOptions[i].fFlags & RTGETOPT_FLAG_ICASE && !RTStrICmp(pszOption, g_aStdOptions[i].pszLong))) return &g_aStdOptions[i]; return NULL; }
int CollectorLinux::getRawHostDiskLoad(const char *name, uint64_t *disk_ms, uint64_t *total_ms) { #if 0 int rc = VINF_SUCCESS; char szIfName[/*IFNAMSIZ*/ 16 + 36]; long long unsigned int u64Busy, tmp; RTStrPrintf(szIfName, sizeof(szIfName), "/sys/class/block/%s/stat", name); FILE *f = fopen(szIfName, "r"); if (f) { if (fscanf(f, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu", &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &u64Busy, &tmp) == 11) { *disk_ms = u64Busy; *total_ms = (uint64_t)(mSingleUser + mSingleKernel + mSingleIdle) * 1000 / mHZ; } else rc = VERR_FILE_IO_ERROR; fclose(f); } else rc = VERR_ACCESS_DENIED; #else int rc = VERR_MISSING; FILE *f = fopen("/proc/diskstats", "r"); if (f) { char szBuf[128]; while (fgets(szBuf, sizeof(szBuf), f)) { char *pszBufName = szBuf; while (*pszBufName == ' ') ++pszBufName; /* Skip spaces */ while (RT_C_IS_DIGIT(*pszBufName)) ++pszBufName; /* Skip major */ while (*pszBufName == ' ') ++pszBufName; /* Skip spaces */ while (RT_C_IS_DIGIT(*pszBufName)) ++pszBufName; /* Skip minor */ while (*pszBufName == ' ') ++pszBufName; /* Skip spaces */ char *pszBufData = strchr(pszBufName, ' '); if (!pszBufData) { LogRel(("CollectorLinux::getRawHostDiskLoad() failed to parse disk stats: %s\n", szBuf)); continue; } *pszBufData++ = '\0'; if (!strcmp(name, pszBufName)) { long long unsigned int u64Busy, tmp; if (sscanf(pszBufData, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu", &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &u64Busy, &tmp) == 11) { *disk_ms = u64Busy; *total_ms = (uint64_t)(mSingleUser + mSingleKernel + mSingleIdle) * 1000 / mHZ; rc = VINF_SUCCESS; } else rc = VERR_FILE_IO_ERROR; break; } } fclose(f); } #endif return rc; }
/** * Converts the UTCTime string into an the RTTIME member of RTASN1TIME. * * @returns IPRT status code. * @param pCursor The cursor to use when reporting an error. * @param pThis The time to parse. * @param pszErrorTag The error tag. */ static int rtAsn1Time_ConvertUTCTime(PRTASN1CURSOR pCursor, PRTASN1TIME pThis, const char *pszErrorTag) { /* * While the current spec says the seconds field is not optional, this * restriction was added later on. So, when parsing UTCTime we must deal * with it being absent. */ int rc; bool fHaveSeconds = pThis->Asn1Core.cb == sizeof("YYMMDDHHMMSSZ") - 1; if (fHaveSeconds || pThis->Asn1Core.cb == sizeof("YYMMDDHHMMZ") - 1) { const char *pachTime = pThis->Asn1Core.uData.pch; /* Basic encoding validation. */ if ( RT_C_IS_DIGIT(pachTime[0]) /* Y */ && RT_C_IS_DIGIT(pachTime[1]) /* Y */ && RT_C_IS_DIGIT(pachTime[2]) /* M */ && RT_C_IS_DIGIT(pachTime[3]) /* M */ && RT_C_IS_DIGIT(pachTime[4]) /* D */ && RT_C_IS_DIGIT(pachTime[5]) /* D */ && RT_C_IS_DIGIT(pachTime[6]) /* H */ && RT_C_IS_DIGIT(pachTime[7]) /* H */ && RT_C_IS_DIGIT(pachTime[8]) /* M */ && RT_C_IS_DIGIT(pachTime[9]) /* M */ && ( !fHaveSeconds || ( RT_C_IS_DIGIT(pachTime[10]) /* S */ && RT_C_IS_DIGIT(pachTime[11]) /* S */ ) ) && pachTime[fHaveSeconds ? 12 : 10] == 'Z' ) { /* Basic conversion. */ pThis->Time.i32Year = (pachTime[0] - '0') * 10 + (pachTime[1] - '0'); pThis->Time.i32Year += pThis->Time.i32Year < 50 ? 2000 : 1900; pThis->Time.u8Month = (pachTime[2] - '0') * 10 + (pachTime[3] - '0'); pThis->Time.u8WeekDay = 0; pThis->Time.u16YearDay = 0; pThis->Time.u8MonthDay = (pachTime[4] - '0') * 10 + (pachTime[5] - '0'); pThis->Time.u8Hour = (pachTime[6] - '0') * 10 + (pachTime[7] - '0'); pThis->Time.u8Minute = (pachTime[8] - '0') * 10 + (pachTime[9] - '0'); if (fHaveSeconds) pThis->Time.u8Second = (pachTime[10] - '0') * 10 + (pachTime[11] - '0'); else pThis->Time.u8Second = 0; pThis->Time.u32Nanosecond = 0; pThis->Time.fFlags = RTTIME_FLAGS_TYPE_UTC; pThis->Time.offUTC = 0; /* Check the convered data and normalize the time structure. */ rc = rtAsn1Time_NormalizeTime(pCursor, pThis, "UTCTime", pszErrorTag); if (RT_SUCCESS(rc)) return rc; } else rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_UTC_TIME_ENCODING, "%s: Bad UTCTime encoding: '%.*s'", pszErrorTag, pThis->Asn1Core.cb, pachTime); } else rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_UTC_TIME_ENCODING, "%s: Bad UTCTime length: %#x", pszErrorTag, pThis->Asn1Core.cb); RT_ZERO(*pThis); return rc; }
/** * Converts the GeneralizedTime string into an the RTTIME member of RTASN1TIME. * * @returns IPRT status code. * @param pCursor The cursor to use when reporting an error. * @param pThis The time to parse. * @param pszErrorTag The error tag. */ static int rtAsn1Time_ConvertGeneralizedTime(PRTASN1CURSOR pCursor, PRTASN1TIME pThis, const char *pszErrorTag) { int rc; if (pThis->Asn1Core.cb >= sizeof("YYYYMMDDHHMMSSZ") - 1) { const char *pachTime = pThis->Asn1Core.uData.pch; /* Basic encoding validation. */ if ( RT_C_IS_DIGIT(pachTime[0]) /* Y */ && RT_C_IS_DIGIT(pachTime[1]) /* Y */ && RT_C_IS_DIGIT(pachTime[2]) /* Y */ && RT_C_IS_DIGIT(pachTime[3]) /* Y */ && RT_C_IS_DIGIT(pachTime[4]) /* M */ && RT_C_IS_DIGIT(pachTime[5]) /* M */ && RT_C_IS_DIGIT(pachTime[6]) /* D */ && RT_C_IS_DIGIT(pachTime[7]) /* D */ && RT_C_IS_DIGIT(pachTime[8]) /* H */ && RT_C_IS_DIGIT(pachTime[9]) /* H */ && RT_C_IS_DIGIT(pachTime[10]) /* M */ && RT_C_IS_DIGIT(pachTime[11]) /* M */ && RT_C_IS_DIGIT(pachTime[12]) /* S */ /** @todo was this once optional? */ && RT_C_IS_DIGIT(pachTime[13]) /* S */ && pachTime[pThis->Asn1Core.cb - 1] == 'Z' ) { /* Basic conversion. */ pThis->Time.i32Year = 1000 * (pachTime[0] - '0') + 100 * (pachTime[1] - '0') + 10 * (pachTime[2] - '0') + (pachTime[3] - '0'); pThis->Time.u8Month = (pachTime[4] - '0') * 10 + (pachTime[5] - '0'); pThis->Time.u8WeekDay = 0; pThis->Time.u16YearDay = 0; pThis->Time.u8MonthDay = (pachTime[6] - '0') * 10 + (pachTime[7] - '0'); pThis->Time.u8Hour = (pachTime[8] - '0') * 10 + (pachTime[9] - '0'); pThis->Time.u8Minute = (pachTime[10] - '0') * 10 + (pachTime[11] - '0'); pThis->Time.u8Second = (pachTime[12] - '0') * 10 + (pachTime[13] - '0'); pThis->Time.u32Nanosecond = 0; pThis->Time.fFlags = RTTIME_FLAGS_TYPE_UTC; pThis->Time.offUTC = 0; /* Optional fraction part. */ rc = VINF_SUCCESS; uint32_t cchLeft = pThis->Asn1Core.cb - 14 - 1; if (cchLeft > 0) rc = rtAsn1Time_ConvertGeneralizedTimeFraction(pCursor, pachTime + 14, cchLeft, pThis, pszErrorTag); /* Check the convered data and normalize the time structure. */ if (RT_SUCCESS(rc)) { rc = rtAsn1Time_NormalizeTime(pCursor, pThis, "GeneralizedTime", pszErrorTag); if (RT_SUCCESS(rc)) return VINF_SUCCESS; } } else rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING, "%s: Bad GeneralizedTime encoding: '%.*s'", pszErrorTag, pThis->Asn1Core.cb, pachTime); } else rc = RTAsn1CursorSetInfo(pCursor, VERR_ASN1_INVALID_GENERALIZED_TIME_ENCODING, "%s: Bad GeneralizedTime length: %#x", pszErrorTag, pThis->Asn1Core.cb); RT_ZERO(*pThis); return rc; }
static char *rtUriPercentDecodeN(const char *pszString, size_t cchString) { AssertPtrReturn(pszString, NULL); AssertReturn(memchr(pszString, '\0', cchString) == NULL, NULL); /* * The new string can only get smaller, so use the input length as a * staring buffer size. */ char *pszDecoded = RTStrAlloc(cchString + 1); if (pszDecoded) { /* * Knowing that the pszString itself is valid UTF-8, we only have to * validate the escape sequences. */ size_t cchLeft = cchString; char const *pchSrc = pszString; char *pchDst = pszDecoded; while (cchLeft > 0) { const char *pchPct = (const char *)memchr(pchSrc, '%', cchLeft); if (pchPct) { size_t cchBefore = pchPct - pchSrc; if (cchBefore) { memcpy(pchDst, pchSrc, cchBefore); pchDst += cchBefore; pchSrc += cchBefore; cchLeft -= cchBefore; } char chHigh, chLow; if ( cchLeft >= 3 && RT_C_IS_XDIGIT(chHigh = pchSrc[1]) && RT_C_IS_XDIGIT(chLow = pchSrc[2])) { uint8_t b = RT_C_IS_DIGIT(chHigh) ? chHigh - '0' : (chHigh & ~0x20) - 'A' + 10; b <<= 4; b |= RT_C_IS_DIGIT(chLow) ? chLow - '0' : (chLow & ~0x20) - 'A' + 10; *pchDst++ = (char)b; pchSrc += 3; cchLeft -= 3; } else { AssertFailed(); *pchDst++ = *pchSrc++; cchLeft--; } } else { memcpy(pchDst, pchSrc, cchLeft); pchDst += cchLeft; pchSrc += cchLeft; cchLeft = 0; break; } } *pchDst = '\0'; /* * If we've got lof space room in the result string, reallocate it. */ size_t cchDecoded = pchDst - pszDecoded; Assert(cchDecoded <= cchString); if (cchString - cchDecoded > 64) RTStrRealloc(&pszDecoded, cchDecoded + 1); } return pszDecoded; }
/** * Decodes a string into a buffer. * * @returns IPRT status code. * @param pchSrc The source string. * @param cchSrc The max number of bytes to decode in the source string. * @param pszDst The destination buffer. * @param cbDst The size of the buffer (including terminator). */ static int rtUriDecodeIntoBuffer(const char *pchSrc, size_t cchSrc, char *pszDst, size_t cbDst) { AssertPtrReturn(pchSrc, VERR_INVALID_POINTER); AssertPtrReturn(pszDst, VERR_INVALID_POINTER); /* * Knowing that the pszString itself is valid UTF-8, we only have to * validate the escape sequences. */ cchSrc = RTStrNLen(pchSrc, cchSrc); while (cchSrc > 0) { const char *pchPct = (const char *)memchr(pchSrc, '%', cchSrc); if (pchPct) { size_t cchBefore = pchPct - pchSrc; AssertReturn(cchBefore + 1 < cbDst, VERR_BUFFER_OVERFLOW); if (cchBefore) { memcpy(pszDst, pchSrc, cchBefore); pszDst += cchBefore; cbDst -= cchBefore; pchSrc += cchBefore; cchSrc -= cchBefore; } char chHigh, chLow; if ( cchSrc >= 3 && RT_C_IS_XDIGIT(chHigh = pchSrc[1]) && RT_C_IS_XDIGIT(chLow = pchSrc[2])) { uint8_t b = RT_C_IS_DIGIT(chHigh) ? chHigh - '0' : (chHigh & ~0x20) - 'A' + 10; b <<= 4; b |= RT_C_IS_DIGIT(chLow) ? chLow - '0' : (chLow & ~0x20) - 'A' + 10; *pszDst++ = (char)b; pchSrc += 3; cchSrc -= 3; } else { AssertFailed(); *pszDst++ = *pchSrc++; cchSrc--; } cbDst -= 1; } else { AssertReturn(cchSrc < cbDst, VERR_BUFFER_OVERFLOW); memcpy(pszDst, pchSrc, cchSrc); pszDst += cchSrc; cbDst -= cchSrc; pchSrc += cchSrc; cchSrc = 0; break; } } AssertReturn(cbDst > 0, VERR_BUFFER_OVERFLOW); *pszDst = '\0'; return VINF_SUCCESS; }
static int rtUriParse(const char *pszUri, PRTURIPARSED pParsed) { /* * Validate the input and clear the output. */ AssertPtrReturn(pParsed, VERR_INVALID_POINTER); RT_ZERO(*pParsed); pParsed->uAuthorityPort = UINT32_MAX; AssertPtrReturn(pszUri, VERR_INVALID_POINTER); size_t const cchUri = strlen(pszUri); if (RT_LIKELY(cchUri >= 3)) { /* likely */ } else return cchUri ? VERR_URI_TOO_SHORT : VERR_URI_EMPTY; /* * Validating escaped text sequences is much simpler if we know that * that the base URI string is valid. Also, we don't necessarily trust * the developer calling us to remember to do this. */ int rc = RTStrValidateEncoding(pszUri); AssertRCReturn(rc, rc); /* * RFC-3986, section 3.1: * scheme = ALPHA *( ALPHA / DIGIT / "+" / "-" / "." ) * * The scheme ends with a ':', which we also skip here. */ size_t off = 0; char ch = pszUri[off++]; if (RT_LIKELY(RT_C_IS_ALPHA(ch))) { /* likely */ } else return VERR_URI_INVALID_SCHEME; for (;;) { ch = pszUri[off]; if (ch == ':') break; if (RT_LIKELY(RT_C_IS_ALNUM(ch) || ch == '.' || ch == '-' || ch == '+')) { /* likely */ } else return VERR_URI_INVALID_SCHEME; off++; } pParsed->cchScheme = off; /* Require the scheme length to be at least two chars so we won't confuse it with a path starting with a DOS drive letter specification. */ if (RT_LIKELY(off >= 2)) { /* likely */ } else return VERR_URI_INVALID_SCHEME; off++; /* (skip colon) */ /* * Find the end of the path, we'll need this several times. * Also, while we're potentially scanning the whole thing, check for '%'. */ size_t const offHash = RTStrOffCharOrTerm(&pszUri[off], '#') + off; size_t const offQuestionMark = RTStrOffCharOrTerm(&pszUri[off], '?') + off; if (memchr(pszUri, '%', cchUri) != NULL) pParsed->fFlags |= RTURIPARSED_F_CONTAINS_ESCAPED_CHARS; /* * RFC-3986, section 3.2: * The authority component is preceeded by a double slash ("//")... */ if ( pszUri[off] == '/' && pszUri[off + 1] == '/') { off += 2; pParsed->offAuthority = pParsed->offAuthorityUsername = pParsed->offAuthorityPassword = pParsed->offAuthorityHost = off; pParsed->fFlags |= RTURIPARSED_F_HAVE_AUTHORITY; /* * RFC-3986, section 3.2: * ...and is terminated by the next slash ("/"), question mark ("?"), * or number sign ("#") character, or by the end of the URI. */ const char *pszAuthority = &pszUri[off]; size_t cchAuthority = RTStrOffCharOrTerm(pszAuthority, '/'); cchAuthority = RT_MIN(cchAuthority, offHash - off); cchAuthority = RT_MIN(cchAuthority, offQuestionMark - off); pParsed->cchAuthority = cchAuthority; /* The Authority can be empty, like for: file:///usr/bin/grep */ if (cchAuthority > 0) { pParsed->cchAuthorityHost = cchAuthority; /* * If there is a userinfo part, it is ended by a '@'. */ const char *pszAt = (const char *)memchr(pszAuthority, '@', cchAuthority); if (pszAt) { size_t cchTmp = pszAt - pszAuthority; pParsed->offAuthorityHost += cchTmp + 1; pParsed->cchAuthorityHost -= cchTmp + 1; /* If there is a password part, it's separated from the username with a colon. */ const char *pszColon = (const char *)memchr(pszAuthority, ':', cchTmp); if (pszColon) { pParsed->cchAuthorityUsername = pszColon - pszAuthority; pParsed->offAuthorityPassword = &pszColon[1] - pszUri; pParsed->cchAuthorityPassword = pszAt - &pszColon[1]; } else { pParsed->cchAuthorityUsername = cchTmp; pParsed->offAuthorityPassword = off + cchTmp; } } /* * If there is a port part, its after the last colon in the host part. */ const char *pszColon = (const char *)memrchr(&pszUri[pParsed->offAuthorityHost], ':', pParsed->cchAuthorityHost); if (pszColon) { size_t cchTmp = &pszUri[pParsed->offAuthorityHost + pParsed->cchAuthorityHost] - &pszColon[1]; pParsed->cchAuthorityHost -= cchTmp + 1; pParsed->uAuthorityPort = 0; while (cchTmp-- > 0) { ch = *++pszColon; if ( RT_C_IS_DIGIT(ch) && pParsed->uAuthorityPort < UINT32_MAX / UINT32_C(10)) { pParsed->uAuthorityPort *= 10; pParsed->uAuthorityPort += ch - '0'; } else return VERR_URI_INVALID_PORT_NUMBER; } } } /* Skip past the authority. */ off += cchAuthority; } else pParsed->offAuthority = pParsed->offAuthorityUsername = pParsed->offAuthorityPassword = pParsed->offAuthorityHost = off; /* * RFC-3986, section 3.3: Path * The path is terminated by the first question mark ("?") * or number sign ("#") character, or by the end of the URI. */ pParsed->offPath = off; pParsed->cchPath = RT_MIN(offHash, offQuestionMark) - off; off += pParsed->cchPath; /* * RFC-3986, section 3.4: Query * The query component is indicated by the first question mark ("?") * character and terminated by a number sign ("#") character or by the * end of the URI. */ if ( off == offQuestionMark && off < cchUri) { Assert(pszUri[offQuestionMark] == '?'); pParsed->offQuery = ++off; pParsed->cchQuery = offHash - off; off = offHash; } else { Assert(!pszUri[offQuestionMark]); pParsed->offQuery = off; } /* * RFC-3986, section 3.5: Fragment * A fragment identifier component is indicated by the presence of a * number sign ("#") character and terminated by the end of the URI. */ if ( off == offHash && off < cchUri) { pParsed->offFragment = ++off; pParsed->cchFragment = cchUri - off; } else { Assert(!pszUri[offHash]); pParsed->offFragment = off; } /* * If there are any escape sequences, validate them. * * This is reasonably simple as we already know that the string is valid UTF-8 * before they get decoded. Thus we only have to validate the escaped sequences. */ if (pParsed->fFlags & RTURIPARSED_F_CONTAINS_ESCAPED_CHARS) { const char *pchSrc = (const char *)memchr(pszUri, '%', cchUri); AssertReturn(pchSrc, VERR_INTERNAL_ERROR); do { char szUtf8Seq[8]; unsigned cchUtf8Seq = 0; unsigned cchNeeded = 0; size_t cchLeft = &pszUri[cchUri] - pchSrc; do { if (cchLeft >= 3) { char chHigh = pchSrc[1]; char chLow = pchSrc[2]; if ( RT_C_IS_XDIGIT(chHigh) && RT_C_IS_XDIGIT(chLow)) { uint8_t b = RT_C_IS_DIGIT(chHigh) ? chHigh - '0' : (chHigh & ~0x20) - 'A' + 10; b <<= 4; b |= RT_C_IS_DIGIT(chLow) ? chLow - '0' : (chLow & ~0x20) - 'A' + 10; if (!(b & 0x80)) { /* We don't want the string to be terminated prematurely. */ if (RT_LIKELY(b != 0)) { /* likely */ } else return VERR_URI_ESCAPED_ZERO; /* Check that we're not expecting more UTF-8 bytes. */ if (RT_LIKELY(cchNeeded == 0)) { /* likely */ } else return VERR_URI_MISSING_UTF8_CONTINUATION_BYTE; } /* Are we waiting UTF-8 bytes? */ else if (cchNeeded > 0) { if (RT_LIKELY(!(b & 0x40))) { /* likely */ } else return VERR_URI_INVALID_ESCAPED_UTF8_CONTINUATION_BYTE; szUtf8Seq[cchUtf8Seq++] = (char)b; if (--cchNeeded == 0) { szUtf8Seq[cchUtf8Seq] = '\0'; rc = RTStrValidateEncoding(szUtf8Seq); if (RT_FAILURE(rc)) return VERR_URI_ESCAPED_CHARS_NOT_VALID_UTF8; cchUtf8Seq = 0; } } /* Start a new UTF-8 sequence. */ else { if ((b & 0xf8) == 0xf0) cchNeeded = 3; else if ((b & 0xf0) == 0xe0) cchNeeded = 2; else if ((b & 0xe0) == 0xc0) cchNeeded = 1; else return VERR_URI_INVALID_ESCAPED_UTF8_LEAD_BYTE; szUtf8Seq[0] = (char)b; cchUtf8Seq = 1; } pchSrc += 3; cchLeft -= 3; } else return VERR_URI_INVALID_ESCAPE_SEQ; } else return VERR_URI_INVALID_ESCAPE_SEQ; } while (cchLeft > 0 && pchSrc[0] == '%'); /* Check that we're not expecting more UTF-8 bytes. */ if (RT_LIKELY(cchNeeded == 0)) { /* likely */ } else return VERR_URI_MISSING_UTF8_CONTINUATION_BYTE; /* next */ pchSrc = (const char *)memchr(pchSrc, '%', cchLeft); } while (pchSrc); } pParsed->u32Magic = RTURIPARSED_MAGIC; return VINF_SUCCESS; }
/** * Gathers VM statistics and reports them to the host. */ static void VBoxServiceVMStatsReport(void) { #if defined(RT_OS_WINDOWS) SYSTEM_INFO systemInfo; PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION pProcInfo; MEMORYSTATUSEX memStatus; uint32_t cbStruct; DWORD cbReturned; Assert(gCtx.pfnGlobalMemoryStatusEx && gCtx.pfnNtQuerySystemInformation); if ( !gCtx.pfnGlobalMemoryStatusEx || !gCtx.pfnNtQuerySystemInformation) return; /* Clear the report so we don't report garbage should NtQuerySystemInformation behave in an unexpected manner. */ VMMDevReportGuestStats req; RT_ZERO(req); /* Query and report guest statistics */ GetSystemInfo(&systemInfo); memStatus.dwLength = sizeof(memStatus); gCtx.pfnGlobalMemoryStatusEx(&memStatus); req.guestStats.u32PageSize = systemInfo.dwPageSize; req.guestStats.u32PhysMemTotal = (uint32_t)(memStatus.ullTotalPhys / _4K); req.guestStats.u32PhysMemAvail = (uint32_t)(memStatus.ullAvailPhys / _4K); /* The current size of the committed memory limit, in bytes. This is physical memory plus the size of the page file, minus a small overhead. */ req.guestStats.u32PageFileSize = (uint32_t)(memStatus.ullTotalPageFile / _4K) - req.guestStats.u32PhysMemTotal; req.guestStats.u32MemoryLoad = memStatus.dwMemoryLoad; req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL | VBOX_GUEST_STAT_PHYS_MEM_AVAIL | VBOX_GUEST_STAT_PAGE_FILE_SIZE | VBOX_GUEST_STAT_MEMORY_LOAD; #ifdef VBOX_WITH_MEMBALLOON req.guestStats.u32PhysMemBalloon = VBoxServiceBalloonQueryPages(_4K); req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PHYS_MEM_BALLOON; #else req.guestStats.u32PhysMemBalloon = 0; #endif if (gCtx.pfnGetPerformanceInfo) { PERFORMANCE_INFORMATION perfInfo; if (gCtx.pfnGetPerformanceInfo(&perfInfo, sizeof(perfInfo))) { req.guestStats.u32Processes = perfInfo.ProcessCount; req.guestStats.u32Threads = perfInfo.ThreadCount; req.guestStats.u32Handles = perfInfo.HandleCount; req.guestStats.u32MemCommitTotal = perfInfo.CommitTotal; /* already in pages */ req.guestStats.u32MemKernelTotal = perfInfo.KernelTotal; /* already in pages */ req.guestStats.u32MemKernelPaged = perfInfo.KernelPaged; /* already in pages */ req.guestStats.u32MemKernelNonPaged = perfInfo.KernelNonpaged; /* already in pages */ req.guestStats.u32MemSystemCache = perfInfo.SystemCache; /* already in pages */ req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PROCESSES | VBOX_GUEST_STAT_THREADS | VBOX_GUEST_STAT_HANDLES | VBOX_GUEST_STAT_MEM_COMMIT_TOTAL | VBOX_GUEST_STAT_MEM_KERNEL_TOTAL | VBOX_GUEST_STAT_MEM_KERNEL_PAGED | VBOX_GUEST_STAT_MEM_KERNEL_NONPAGED | VBOX_GUEST_STAT_MEM_SYSTEM_CACHE; } else VBoxServiceVerbose(3, "VBoxServiceVMStatsReport: GetPerformanceInfo failed with %d\n", GetLastError()); } /* Query CPU load information */ cbStruct = systemInfo.dwNumberOfProcessors * sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION); pProcInfo = (PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)RTMemAlloc(cbStruct); if (!pProcInfo) return; /* Unfortunately GetSystemTimes is XP SP1 and up only, so we need to use the semi-undocumented NtQuerySystemInformation */ NTSTATUS rc = gCtx.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned); if ( !rc && cbReturned == cbStruct) { if (gCtx.au64LastCpuLoad_Kernel == 0) { /* first time */ gCtx.au64LastCpuLoad_Idle[0] = pProcInfo->IdleTime.QuadPart; gCtx.au64LastCpuLoad_Kernel[0] = pProcInfo->KernelTime.QuadPart; gCtx.au64LastCpuLoad_User[0] = pProcInfo->UserTime.QuadPart; Sleep(250); rc = gCtx.pfnNtQuerySystemInformation(SystemProcessorPerformanceInformation, pProcInfo, cbStruct, &cbReturned); Assert(!rc); } uint64_t deltaIdle = (pProcInfo->IdleTime.QuadPart - gCtx.au64LastCpuLoad_Idle[0]); uint64_t deltaKernel = (pProcInfo->KernelTime.QuadPart - gCtx.au64LastCpuLoad_Kernel[0]); uint64_t deltaUser = (pProcInfo->UserTime.QuadPart - gCtx.au64LastCpuLoad_User[0]); deltaKernel -= deltaIdle; /* idle time is added to kernel time */ uint64_t ullTotalTime = deltaIdle + deltaKernel + deltaUser; if (ullTotalTime == 0) /* Prevent division through zero. */ ullTotalTime = 1; req.guestStats.u32CpuLoad_Idle = (uint32_t)(deltaIdle * 100 / ullTotalTime); req.guestStats.u32CpuLoad_Kernel = (uint32_t)(deltaKernel* 100 / ullTotalTime); req.guestStats.u32CpuLoad_User = (uint32_t)(deltaUser * 100 / ullTotalTime); req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE | VBOX_GUEST_STAT_CPU_LOAD_KERNEL | VBOX_GUEST_STAT_CPU_LOAD_USER; gCtx.au64LastCpuLoad_Idle[0] = pProcInfo->IdleTime.QuadPart; gCtx.au64LastCpuLoad_Kernel[0] = pProcInfo->KernelTime.QuadPart; gCtx.au64LastCpuLoad_User[0] = pProcInfo->UserTime.QuadPart; /** @todo SMP: report details for each CPU? */ } for (uint32_t i = 0; i < systemInfo.dwNumberOfProcessors; i++) { req.guestStats.u32CpuId = i; rc = VbglR3StatReport(&req); if (RT_SUCCESS(rc)) VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics (CPU %u) reported successfully!\n", i); else VBoxServiceVerbose(3, "VBoxStatsReportStatistics: DeviceIoControl (stats report) failed with %d\n", GetLastError()); } RTMemFree(pProcInfo); #elif defined(RT_OS_LINUX) VMMDevReportGuestStats req; RT_ZERO(req); PRTSTREAM pStrm; char szLine[256]; char *psz; int rc = RTStrmOpen("/proc/meminfo", "r", &pStrm); if (RT_SUCCESS(rc)) { uint64_t u64Kb; uint64_t u64Total = 0, u64Free = 0, u64Buffers = 0, u64Cached = 0, u64PagedTotal = 0; for (;;) { rc = RTStrmGetLine(pStrm, szLine, sizeof(szLine)); if (RT_FAILURE(rc)) break; if (strstr(szLine, "MemTotal:") == szLine) { rc = RTStrToUInt64Ex(RTStrStripL(&szLine[9]), &psz, 0, &u64Kb); if (RT_SUCCESS(rc)) u64Total = u64Kb * _1K; } else if (strstr(szLine, "MemFree:") == szLine) { rc = RTStrToUInt64Ex(RTStrStripL(&szLine[8]), &psz, 0, &u64Kb); if (RT_SUCCESS(rc)) u64Free = u64Kb * _1K; } else if (strstr(szLine, "Buffers:") == szLine) { rc = RTStrToUInt64Ex(RTStrStripL(&szLine[8]), &psz, 0, &u64Kb); if (RT_SUCCESS(rc)) u64Buffers = u64Kb * _1K; } else if (strstr(szLine, "Cached:") == szLine) { rc = RTStrToUInt64Ex(RTStrStripL(&szLine[7]), &psz, 0, &u64Kb); if (RT_SUCCESS(rc)) u64Cached = u64Kb * _1K; } else if (strstr(szLine, "SwapTotal:") == szLine) { rc = RTStrToUInt64Ex(RTStrStripL(&szLine[10]), &psz, 0, &u64Kb); if (RT_SUCCESS(rc)) u64PagedTotal = u64Kb * _1K; } } req.guestStats.u32PhysMemTotal = u64Total / _4K; req.guestStats.u32PhysMemAvail = (u64Free + u64Buffers + u64Cached) / _4K; req.guestStats.u32MemSystemCache = (u64Buffers + u64Cached) / _4K; req.guestStats.u32PageFileSize = u64PagedTotal / _4K; RTStrmClose(pStrm); } else VBoxServiceVerbose(3, "VBoxStatsReportStatistics: memory info not available!\n"); req.guestStats.u32PageSize = getpagesize(); req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL | VBOX_GUEST_STAT_PHYS_MEM_AVAIL | VBOX_GUEST_STAT_MEM_SYSTEM_CACHE | VBOX_GUEST_STAT_PAGE_FILE_SIZE; #ifdef VBOX_WITH_MEMBALLOON req.guestStats.u32PhysMemBalloon = VBoxServiceBalloonQueryPages(_4K); req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PHYS_MEM_BALLOON; #else req.guestStats.u32PhysMemBalloon = 0; #endif /** @todo req.guestStats.u32Threads */ /** @todo req.guestStats.u32Processes */ /* req.guestStats.u32Handles doesn't make sense here. */ /** @todo req.guestStats.u32MemoryLoad */ /** @todo req.guestStats.u32MemCommitTotal */ /** @todo req.guestStats.u32MemKernelTotal */ /** @todo req.guestStats.u32MemKernelPaged, make any sense? = u32MemKernelTotal? */ /** @todo req.guestStats.u32MemKernelNonPaged, make any sense? = 0? */ bool fCpuInfoAvail = false; rc = RTStrmOpen("/proc/stat", "r", &pStrm); if (RT_SUCCESS(rc)) { for (;;) { rc = RTStrmGetLine(pStrm, szLine, sizeof(szLine)); if (RT_FAILURE(rc)) break; if ( strstr(szLine, "cpu") == szLine && strlen(szLine) > 3 && RT_C_IS_DIGIT(szLine[3])) { uint32_t u32CpuId; rc = RTStrToUInt32Ex(&szLine[3], &psz, 0, &u32CpuId); if (u32CpuId < VMM_MAX_CPU_COUNT) { uint64_t u64User = 0; if (RT_SUCCESS(rc)) rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64User); uint64_t u64Nice = 0; if (RT_SUCCESS(rc)) rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64Nice); uint64_t u64System = 0; if (RT_SUCCESS(rc)) rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64System); uint64_t u64Idle = 0; if (RT_SUCCESS(rc)) rc = RTStrToUInt64Ex(RTStrStripL(psz), &psz, 0, &u64Idle); uint64_t u64DeltaIdle = u64Idle - gCtx.au64LastCpuLoad_Idle[u32CpuId]; uint64_t u64DeltaSystem = u64System - gCtx.au64LastCpuLoad_Kernel[u32CpuId]; uint64_t u64DeltaUser = u64User - gCtx.au64LastCpuLoad_User[u32CpuId]; uint64_t u64DeltaNice = u64Nice - gCtx.au64LastCpuLoad_Nice[u32CpuId]; uint64_t u64DeltaAll = u64DeltaIdle + u64DeltaSystem + u64DeltaUser + u64DeltaNice; if (u64DeltaAll == 0) /* Prevent division through zero. */ u64DeltaAll = 1; gCtx.au64LastCpuLoad_Idle[u32CpuId] = u64Idle; gCtx.au64LastCpuLoad_Kernel[u32CpuId] = u64System; gCtx.au64LastCpuLoad_User[u32CpuId] = u64User; gCtx.au64LastCpuLoad_Nice[u32CpuId] = u64Nice; req.guestStats.u32CpuId = u32CpuId; req.guestStats.u32CpuLoad_Idle = (uint32_t)(u64DeltaIdle * 100 / u64DeltaAll); req.guestStats.u32CpuLoad_Kernel = (uint32_t)(u64DeltaSystem * 100 / u64DeltaAll); req.guestStats.u32CpuLoad_User = (uint32_t)((u64DeltaUser + u64DeltaNice) * 100 / u64DeltaAll); req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE | VBOX_GUEST_STAT_CPU_LOAD_KERNEL | VBOX_GUEST_STAT_CPU_LOAD_USER; fCpuInfoAvail = true; rc = VbglR3StatReport(&req); if (RT_SUCCESS(rc)) VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics (CPU %u) reported successfully!\n", u32CpuId); else VBoxServiceVerbose(3, "VBoxStatsReportStatistics: stats report failed with rc=%Rrc\n", rc); } else VBoxServiceVerbose(3, "VBoxStatsReportStatistics: skipping information for CPU%u\n", u32CpuId); } } RTStrmClose(pStrm); } if (!fCpuInfoAvail) { VBoxServiceVerbose(3, "VBoxStatsReportStatistics: CPU info not available!\n"); rc = VbglR3StatReport(&req); if (RT_SUCCESS(rc)) VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics reported successfully!\n"); else VBoxServiceVerbose(3, "VBoxStatsReportStatistics: stats report failed with rc=%Rrc\n", rc); } #elif defined(RT_OS_SOLARIS) VMMDevReportGuestStats req; RT_ZERO(req); kstat_ctl_t *pStatKern = kstat_open(); if (pStatKern) { /* * Memory statistics. */ uint64_t u64Total = 0, u64Free = 0, u64Buffers = 0, u64Cached = 0, u64PagedTotal = 0; int rc = -1; kstat_t *pStatPages = kstat_lookup(pStatKern, (char *)"unix", 0 /* instance */, (char *)"system_pages"); if (pStatPages) { rc = kstat_read(pStatKern, pStatPages, NULL /* optional-copy-buf */); if (rc != -1) { kstat_named_t *pStat = NULL; pStat = (kstat_named_t *)kstat_data_lookup(pStatPages, (char *)"pagestotal"); if (pStat) u64Total = pStat->value.ul; pStat = (kstat_named_t *)kstat_data_lookup(pStatPages, (char *)"freemem"); if (pStat) u64Free = pStat->value.ul; } } kstat_t *pStatZFS = kstat_lookup(pStatKern, (char *)"zfs", 0 /* instance */, (char *)"arcstats"); if (pStatZFS) { rc = kstat_read(pStatKern, pStatZFS, NULL /* optional-copy-buf */); if (rc != -1) { kstat_named_t *pStat = (kstat_named_t *)kstat_data_lookup(pStatZFS, (char *)"size"); if (pStat) u64Cached = pStat->value.ul; } } /* * The vminfo are accumulative counters updated every "N" ticks. Let's get the * number of stat updates so far and use that to divide the swap counter. */ kstat_t *pStatInfo = kstat_lookup(pStatKern, (char *)"unix", 0 /* instance */, (char *)"sysinfo"); if (pStatInfo) { sysinfo_t SysInfo; rc = kstat_read(pStatKern, pStatInfo, &SysInfo); if (rc != -1) { kstat_t *pStatVMInfo = kstat_lookup(pStatKern, (char *)"unix", 0 /* instance */, (char *)"vminfo"); if (pStatVMInfo) { vminfo_t VMInfo; rc = kstat_read(pStatKern, pStatVMInfo, &VMInfo); if (rc != -1) { Assert(SysInfo.updates != 0); u64PagedTotal = VMInfo.swap_avail / SysInfo.updates; } } } } req.guestStats.u32PhysMemTotal = u64Total; /* already in pages */ req.guestStats.u32PhysMemAvail = u64Free; /* already in pages */ req.guestStats.u32MemSystemCache = u64Cached / _4K; req.guestStats.u32PageFileSize = u64PagedTotal; /* already in pages */ /** @todo req.guestStats.u32Threads */ /** @todo req.guestStats.u32Processes */ /** @todo req.guestStats.u32Handles -- ??? */ /** @todo req.guestStats.u32MemoryLoad */ /** @todo req.guestStats.u32MemCommitTotal */ /** @todo req.guestStats.u32MemKernelTotal */ /** @todo req.guestStats.u32MemKernelPaged */ /** @todo req.guestStats.u32MemKernelNonPaged */ req.guestStats.u32PageSize = getpagesize(); req.guestStats.u32StatCaps = VBOX_GUEST_STAT_PHYS_MEM_TOTAL | VBOX_GUEST_STAT_PHYS_MEM_AVAIL | VBOX_GUEST_STAT_MEM_SYSTEM_CACHE | VBOX_GUEST_STAT_PAGE_FILE_SIZE; #ifdef VBOX_WITH_MEMBALLOON req.guestStats.u32PhysMemBalloon = VBoxServiceBalloonQueryPages(_4K); req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_PHYS_MEM_BALLOON; #else req.guestStats.u32PhysMemBalloon = 0; #endif /* * CPU statistics. */ cpu_stat_t StatCPU; RT_ZERO(StatCPU); kstat_t *pStatNode = NULL; uint32_t cCPUs = 0; bool fCpuInfoAvail = false; for (pStatNode = pStatKern->kc_chain; pStatNode != NULL; pStatNode = pStatNode->ks_next) { if (!strcmp(pStatNode->ks_module, "cpu_stat")) { rc = kstat_read(pStatKern, pStatNode, &StatCPU); if (rc == -1) break; uint64_t u64Idle = StatCPU.cpu_sysinfo.cpu[CPU_IDLE]; uint64_t u64User = StatCPU.cpu_sysinfo.cpu[CPU_USER]; uint64_t u64System = StatCPU.cpu_sysinfo.cpu[CPU_KERNEL]; uint64_t u64DeltaIdle = u64Idle - gCtx.au64LastCpuLoad_Idle[cCPUs]; uint64_t u64DeltaSystem = u64System - gCtx.au64LastCpuLoad_Kernel[cCPUs]; uint64_t u64DeltaUser = u64User - gCtx.au64LastCpuLoad_User[cCPUs]; uint64_t u64DeltaAll = u64DeltaIdle + u64DeltaSystem + u64DeltaUser; if (u64DeltaAll == 0) /* Prevent division through zero. */ u64DeltaAll = 1; gCtx.au64LastCpuLoad_Idle[cCPUs] = u64Idle; gCtx.au64LastCpuLoad_Kernel[cCPUs] = u64System; gCtx.au64LastCpuLoad_User[cCPUs] = u64User; req.guestStats.u32CpuId = cCPUs; req.guestStats.u32CpuLoad_Idle = (uint32_t)(u64DeltaIdle * 100 / u64DeltaAll); req.guestStats.u32CpuLoad_Kernel = (uint32_t)(u64DeltaSystem * 100 / u64DeltaAll); req.guestStats.u32CpuLoad_User = (uint32_t)(u64DeltaUser * 100 / u64DeltaAll); req.guestStats.u32StatCaps |= VBOX_GUEST_STAT_CPU_LOAD_IDLE | VBOX_GUEST_STAT_CPU_LOAD_KERNEL | VBOX_GUEST_STAT_CPU_LOAD_USER; fCpuInfoAvail = true; rc = VbglR3StatReport(&req); if (RT_SUCCESS(rc)) VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics (CPU %u) reported successfully!\n", cCPUs); else VBoxServiceVerbose(3, "VBoxStatsReportStatistics: stats report failed with rc=%Rrc\n", rc); cCPUs++; } } /* * Report whatever statistics were collected. */ if (!fCpuInfoAvail) { VBoxServiceVerbose(3, "VBoxStatsReportStatistics: CPU info not available!\n"); rc = VbglR3StatReport(&req); if (RT_SUCCESS(rc)) VBoxServiceVerbose(3, "VBoxStatsReportStatistics: new statistics reported successfully!\n"); else VBoxServiceVerbose(3, "VBoxStatsReportStatistics: stats report failed with rc=%Rrc\n", rc); } kstat_close(pStatKern); } #else /* todo: implement for other platforms. */ #endif }
/** * Parses a out the next block from a version string. * * @returns true if numeric, false if not. * @param ppszVer The string cursor, IN/OUT. * @param pi32Value Where to return the value if numeric. * @param pcchBlock Where to return the block length. */ static bool rtStrVersionParseBlock(const char **ppszVer, int32_t *pi32Value, size_t *pcchBlock) { const char *psz = *ppszVer; /* * Check for end-of-string. */ if (!*psz) { *pi32Value = 0; *pcchBlock = 0; return false; } /* * Try convert the block to a number the simple way. */ char ch; bool fNumeric = RT_C_IS_DIGIT(*psz); if (fNumeric) { do ch = *++psz; while (ch && RT_C_IS_DIGIT(ch)); int rc = RTStrToInt32Ex(*ppszVer, NULL, 10, pi32Value); if (RT_FAILURE(rc) || rc == VWRN_NUMBER_TOO_BIG) { AssertRC(rc); fNumeric = false; *pi32Value = 0; } } else { /* * Find the end of the current string. Make a special case for SVN * revision numbers that immediately follows a release tag string. */ do ch = *++psz; while ( ch && !RT_C_IS_DIGIT(ch) && !RTSTRVER_IS_PUNCTUACTION(ch)); size_t cchBlock = psz - *ppszVer; if ( cchBlock > 1 && psz[-1] == 'r' && RT_C_IS_DIGIT(*psz)) { psz--; cchBlock--; } /* * Translate standard pre release terms to negative values. */ static const struct { size_t cch; const char *psz; int32_t iValue; } s_aTerms[] = { { 2, "RC", -100000 }, { 3, "PRE", -200000 }, { 5, "GAMMA", -300000 }, { 4, "BETA", -400000 }, { 5, "ALPHA", -500000 } }; int32_t iVal1 = 0; for (unsigned i = 0; i < RT_ELEMENTS(s_aTerms); i++) if ( cchBlock == s_aTerms[i].cch && !RTStrNCmp(s_aTerms[i].psz, *ppszVer, cchBlock)) { iVal1 = s_aTerms[i].iValue; break; } if (iVal1 != 0) { /* * Does the prelease term have a trailing number? * Add it assuming BETA == BETA1. */ if (RT_C_IS_DIGIT(*psz)) { const char *psz2 = psz; do ch = *++psz; while ( ch && RT_C_IS_DIGIT(ch) && !RTSTRVER_IS_PUNCTUACTION(ch)); int rc = RTStrToInt32Ex(psz2, NULL, 10, pi32Value); if (RT_SUCCESS(rc) && rc != VWRN_NUMBER_TOO_BIG && *pi32Value) iVal1 += *pi32Value - 1; else { AssertRC(rc); psz = psz2; } } fNumeric = true; } *pi32Value = iVal1; } *pcchBlock = psz - *ppszVer; /* * Skip trailing punctuation. */ if (RTSTRVER_IS_PUNCTUACTION(*psz)) psz++; *ppszVer = psz; return fNumeric; }