util: Improve CPU frequency parsing

Make the parser both more strict, by not ignoring errors reported
by virStrToLong_ui(), and more permissive, by not failing due to
unrelated fields which just happen to have a know prefix and
accepting any amount of whitespace before the numeric value.

Signed-off-by: Andrea Bolognani <abologna@redhat.com>
Reviewed-by: Bjoern Walk <bwalk@linux.vnet.ibm.com>
This commit is contained in:
Andrea Bolognani 2017-12-11 15:32:49 +01:00
parent 5e07b28a7a
commit 6512b0ddc1
2 changed files with 57 additions and 9 deletions

View File

@ -509,6 +509,27 @@ virHostCPUHasValidSubcoreConfiguration(int threads_per_subcore)
} }
/**
* virHostCPUParseFrequencyString:
* @str: string to be parsed
* @prefix: expected prefix
* @mhz: output location
*
* Parse a /proc/cpuinfo line and extract the CPU frequency, if present.
*
* The expected format of @str looks like
*
* cpu MHz : 2100.000
*
* where @prefix ("cpu MHz" in the example), is architecture-dependent.
*
* The decimal part of the CPU frequency, as well as all whitespace, is
* ignored.
*
* Returns: 0 when the string has been parsed successfully and the CPU
* frequency has been stored in @mhz, >0 when the string has not
* been parsed but no error has occurred, <0 on failure.
*/
static int static int
virHostCPUParseFrequencyString(const char *str, virHostCPUParseFrequencyString(const char *str,
const char *prefix, const char *prefix,
@ -517,26 +538,49 @@ virHostCPUParseFrequencyString(const char *str,
char *p; char *p;
unsigned int ui; unsigned int ui;
/* If the string doesn't start with the expected prefix, then
* we're not looking at the right string and we should move on */
if (!STRPREFIX(str, prefix)) if (!STRPREFIX(str, prefix))
return 1; return 1;
/* Skip the prefix */
str += strlen(prefix); str += strlen(prefix);
while (*str && c_isspace(*str)) /* Skip all whitespace */
while (c_isspace(*str))
str++;
if (*str == '\0')
goto error;
/* Skip the colon. If anything but a colon is found, then we're
* not looking at the right string and we should move on */
if (*str != ':')
return 1;
str++; str++;
if (*str != ':' || !str[1]) { /* Skip all whitespace */
virReportError(VIR_ERR_INTERNAL_ERROR, "%s", while (c_isspace(*str))
_("parsing cpu MHz from cpuinfo")); str++;
return -1; if (*str == '\0')
goto error;
/* Parse the frequency. We expect an unsigned value, optionally
* followed by a fractional part (which gets discarded) or some
* leading whitespace */
if (virStrToLong_ui(str, &p, 10, &ui) < 0 ||
(*p != '.' && *p != '\0' && !c_isspace(*p))) {
goto error;
} }
if (virStrToLong_ui(str + 1, &p, 10, &ui) == 0 &&
/* Accept trailing fractional part. */
(*p == '\0' || *p == '.' || c_isspace(*p)))
*mhz = ui; *mhz = ui;
return 0; return 0;
error:
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Missing or invalid CPU frequency in %s"),
CPUINFO_PATH);
return -1;
} }

View File

@ -28,6 +28,10 @@ model : 4
model name : Intel(R) Xeon(TM) CPU 2.80GHz model name : Intel(R) Xeon(TM) CPU 2.80GHz
stepping : 8 stepping : 8
cpu MHz : 2800.000 cpu MHz : 2800.000
# The following field is a made-up one, intended to make sure our cpuinfo
# parser deals correctly with the introduction of new fields that just so
# happen to share a prefix with the one used for CPU frequency
cpu MHz rounded up to GHz : 3000.000
cache size : 2048 KB cache size : 2048 KB
physical id : 0 physical id : 0
siblings : 2 siblings : 2