libvirt/src/util/virsysinfo.c
Daniel P. Berrangé 27a6edf50f src: remove usage of strchrnul function
The strchrnul function doesn't exist on Windows and rather
than attempt to implement it, it is simpler to just avoid
its usage, as any callers are easily adapted.

Reviewed-by: Pavel Hrdina <phrdina@redhat.com>
Signed-off-by: Daniel P. Berrangé <berrange@redhat.com>
2020-01-29 14:51:39 +00:00

1626 lines
48 KiB
C

/*
* virsysinfo.c: get SMBIOS/sysinfo information from the host
*
* Copyright (C) 2010-2014 Red Hat, Inc.
* Copyright (C) 2010 Daniel Veillard
*
* This library 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; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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 GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library. If not, see
* <http://www.gnu.org/licenses/>.
*/
#include <config.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <unistd.h>
#include "virerror.h"
#include "virsysinfo.h"
#include "viralloc.h"
#include "vircommand.h"
#include "virlog.h"
#include "virfile.h"
#include "virstring.h"
#define LIBVIRT_VIRSYSINFOPRIV_H_ALLOW
#include "virsysinfopriv.h"
#define VIR_FROM_THIS VIR_FROM_SYSINFO
VIR_LOG_INIT("util.sysinfo");
VIR_ENUM_IMPL(virSysinfo,
VIR_SYSINFO_LAST,
"smbios",
);
static const char *sysinfoDmidecode = DMIDECODE;
static const char *sysinfoSysinfo = "/proc/sysinfo";
static const char *sysinfoCpuinfo = "/proc/cpuinfo";
#define SYSINFO_SMBIOS_DECODER sysinfoDmidecode
#define SYSINFO sysinfoSysinfo
#define CPUINFO sysinfoCpuinfo
#define CPUINFO_FILE_LEN (1024*1024) /* 1MB limit for /proc/cpuinfo file */
void
virSysinfoSetup(const char *dmidecode,
const char *sysinfo,
const char *cpuinfo)
{
sysinfoDmidecode = dmidecode;
sysinfoSysinfo = sysinfo;
sysinfoCpuinfo = cpuinfo;
}
void virSysinfoBIOSDefFree(virSysinfoBIOSDefPtr def)
{
if (def == NULL)
return;
VIR_FREE(def->vendor);
VIR_FREE(def->version);
VIR_FREE(def->date);
VIR_FREE(def->release);
VIR_FREE(def);
}
void virSysinfoSystemDefFree(virSysinfoSystemDefPtr def)
{
if (def == NULL)
return;
VIR_FREE(def->manufacturer);
VIR_FREE(def->product);
VIR_FREE(def->version);
VIR_FREE(def->serial);
VIR_FREE(def->uuid);
VIR_FREE(def->sku);
VIR_FREE(def->family);
VIR_FREE(def);
}
void virSysinfoBaseBoardDefClear(virSysinfoBaseBoardDefPtr def)
{
if (def == NULL)
return;
VIR_FREE(def->manufacturer);
VIR_FREE(def->product);
VIR_FREE(def->version);
VIR_FREE(def->serial);
VIR_FREE(def->asset);
VIR_FREE(def->location);
}
void virSysinfoChassisDefFree(virSysinfoChassisDefPtr def)
{
if (def == NULL)
return;
VIR_FREE(def->manufacturer);
VIR_FREE(def->version);
VIR_FREE(def->serial);
VIR_FREE(def->asset);
VIR_FREE(def->sku);
VIR_FREE(def);
}
void virSysinfoOEMStringsDefFree(virSysinfoOEMStringsDefPtr def)
{
size_t i;
if (def == NULL)
return;
for (i = 0; i < def->nvalues; i++)
VIR_FREE(def->values[i]);
VIR_FREE(def->values);
VIR_FREE(def);
}
/**
* virSysinfoDefFree:
* @def: a sysinfo structure
*
* Free up the sysinfo structure
*/
void virSysinfoDefFree(virSysinfoDefPtr def)
{
size_t i;
if (def == NULL)
return;
virSysinfoBIOSDefFree(def->bios);
virSysinfoSystemDefFree(def->system);
for (i = 0; i < def->nbaseBoard; i++)
virSysinfoBaseBoardDefClear(def->baseBoard + i);
VIR_FREE(def->baseBoard);
virSysinfoChassisDefFree(def->chassis);
for (i = 0; i < def->nprocessor; i++) {
VIR_FREE(def->processor[i].processor_socket_destination);
VIR_FREE(def->processor[i].processor_type);
VIR_FREE(def->processor[i].processor_family);
VIR_FREE(def->processor[i].processor_manufacturer);
VIR_FREE(def->processor[i].processor_signature);
VIR_FREE(def->processor[i].processor_version);
VIR_FREE(def->processor[i].processor_external_clock);
VIR_FREE(def->processor[i].processor_max_speed);
VIR_FREE(def->processor[i].processor_status);
VIR_FREE(def->processor[i].processor_serial_number);
VIR_FREE(def->processor[i].processor_part_number);
}
VIR_FREE(def->processor);
for (i = 0; i < def->nmemory; i++) {
VIR_FREE(def->memory[i].memory_size);
VIR_FREE(def->memory[i].memory_form_factor);
VIR_FREE(def->memory[i].memory_locator);
VIR_FREE(def->memory[i].memory_bank_locator);
VIR_FREE(def->memory[i].memory_type);
VIR_FREE(def->memory[i].memory_type_detail);
VIR_FREE(def->memory[i].memory_speed);
VIR_FREE(def->memory[i].memory_manufacturer);
VIR_FREE(def->memory[i].memory_serial_number);
VIR_FREE(def->memory[i].memory_part_number);
}
VIR_FREE(def->memory);
virSysinfoOEMStringsDefFree(def->oemStrings);
VIR_FREE(def);
}
static bool
virSysinfoDefIsEmpty(const virSysinfoDef *def)
{
return !(def->bios || def->system || def->nbaseBoard > 0 ||
def->chassis || def->nprocessor > 0 ||
def->nmemory > 0 || def->oemStrings);
}
static int
virSysinfoParsePPCSystem(const char *base, virSysinfoSystemDefPtr *sysdef)
{
int ret = -1;
char *eol = NULL;
const char *cur;
virSysinfoSystemDefPtr def;
if ((cur = strstr(base, "platform")) == NULL)
return 0;
if (VIR_ALLOC(def) < 0)
return ret;
base = cur;
/* Account for format 'platform : XXXX'*/
cur = strchr(cur, ':') + 1;
eol = strchr(cur, '\n');
virSkipSpaces(&cur);
if (eol)
def->family = g_strndup(cur, eol - cur);
if ((cur = strstr(base, "model")) != NULL) {
cur = strchr(cur, ':') + 1;
eol = strchr(cur, '\n');
virSkipSpaces(&cur);
if (eol)
def->serial = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "machine")) != NULL) {
cur = strchr(cur, ':') + 1;
eol = strchr(cur, '\n');
virSkipSpaces(&cur);
if (eol)
def->version = g_strndup(cur, eol - cur);
}
if (!def->manufacturer && !def->product && !def->version &&
!def->serial && !def->uuid && !def->sku && !def->family) {
virSysinfoSystemDefFree(def);
def = NULL;
}
*sysdef = def;
def = NULL;
ret = 0;
virSysinfoSystemDefFree(def);
return ret;
}
static int
virSysinfoParsePPCProcessor(const char *base, virSysinfoDefPtr ret)
{
const char *cur;
char *eol, *tmp_base;
virSysinfoProcessorDefPtr processor;
while ((tmp_base = strstr(base, "processor")) != NULL) {
base = tmp_base;
eol = strchr(base, '\n');
cur = strchr(base, ':') + 1;
if (VIR_EXPAND_N(ret->processor, ret->nprocessor, 1) < 0)
return -1;
processor = &ret->processor[ret->nprocessor - 1];
virSkipSpaces(&cur);
if (eol)
processor->processor_socket_destination = g_strndup(cur,
eol - cur);
base = cur;
if ((cur = strstr(base, "cpu")) != NULL) {
cur = strchr(cur, ':') + 1;
eol = strchr(cur, '\n');
virSkipSpaces(&cur);
if (eol)
processor->processor_type = g_strndup(cur, eol - cur);
base = cur;
}
if ((cur = strstr(base, "revision")) != NULL) {
cur = strchr(cur, ':') + 1;
eol = strchr(cur, '\n');
virSkipSpaces(&cur);
if (eol)
processor->processor_version = g_strndup(cur, eol - cur);
base = cur;
}
}
return 0;
}
/* virSysinfoRead for PowerPC
* Gathers sysinfo data from /proc/cpuinfo */
virSysinfoDefPtr
virSysinfoReadPPC(void)
{
g_auto(virSysinfoDefPtr) ret = NULL;
g_autofree char *outbuf = NULL;
if (VIR_ALLOC(ret) < 0)
return NULL;
if (virFileReadAll(CPUINFO, CPUINFO_FILE_LEN, &outbuf) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to open %s"), CPUINFO);
return NULL;
}
ret->nprocessor = 0;
ret->processor = NULL;
if (virSysinfoParsePPCProcessor(outbuf, ret) < 0)
return NULL;
if (virSysinfoParsePPCSystem(outbuf, &ret->system) < 0)
return NULL;
return g_steal_pointer(&ret);
}
static int
virSysinfoParseARMSystem(const char *base, virSysinfoSystemDefPtr *sysdef)
{
int ret = -1;
char *eol = NULL;
const char *cur;
virSysinfoSystemDefPtr def;
if ((cur = strstr(base, "platform")) == NULL)
return 0;
if (VIR_ALLOC(def) < 0)
return ret;
base = cur;
/* Account for format 'platform : XXXX'*/
cur = strchr(cur, ':') + 1;
eol = strchr(cur, '\n');
virSkipSpaces(&cur);
if (eol)
def->family = g_strndup(cur, eol - cur);
if ((cur = strstr(base, "model")) != NULL) {
cur = strchr(cur, ':') + 1;
eol = strchr(cur, '\n');
virSkipSpaces(&cur);
if (eol)
def->serial = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "machine")) != NULL) {
cur = strchr(cur, ':') + 1;
eol = strchr(cur, '\n');
virSkipSpaces(&cur);
if (eol)
def->version = g_strndup(cur, eol - cur);
}
if (!def->manufacturer && !def->product && !def->version &&
!def->serial && !def->uuid && !def->sku && !def->family) {
virSysinfoSystemDefFree(def);
def = NULL;
}
*sysdef = def;
def = NULL;
ret = 0;
virSysinfoSystemDefFree(def);
return ret;
}
static int
virSysinfoParseARMProcessor(const char *base, virSysinfoDefPtr ret)
{
const char *cur;
char *eol, *tmp_base;
virSysinfoProcessorDefPtr processor;
char *processor_type = NULL;
if (!(tmp_base = strstr(base, "model name")) &&
!(tmp_base = strstr(base, "Processor")))
return 0;
eol = strchr(tmp_base, '\n');
cur = strchr(tmp_base, ':') + 1;
virSkipSpaces(&cur);
if (eol)
processor_type = g_strndup(cur, eol - cur);
while ((tmp_base = strstr(base, "processor")) != NULL) {
base = tmp_base;
eol = strchr(base, '\n');
cur = strchr(base, ':') + 1;
if (VIR_EXPAND_N(ret->processor, ret->nprocessor, 1) < 0)
goto error;
processor = &ret->processor[ret->nprocessor - 1];
virSkipSpaces(&cur);
if (eol)
processor->processor_socket_destination = g_strndup(cur,
eol - cur);
processor->processor_type = g_strdup(processor_type);
base = cur;
}
VIR_FREE(processor_type);
return 0;
error:
VIR_FREE(processor_type);
return -1;
}
/* virSysinfoRead for ARMv7
* Gathers sysinfo data from /proc/cpuinfo */
virSysinfoDefPtr
virSysinfoReadARM(void)
{
g_auto(virSysinfoDefPtr) ret = NULL;
g_autofree char *outbuf = NULL;
/* Some ARM systems have DMI tables available. */
if ((ret = virSysinfoReadDMI())) {
if (!virSysinfoDefIsEmpty(ret))
return g_steal_pointer(&ret);
virSysinfoDefFree(ret);
}
/* Well, we've tried. Fall back to parsing cpuinfo */
virResetLastError();
if (VIR_ALLOC(ret) < 0)
return NULL;
if (virFileReadAll(CPUINFO, CPUINFO_FILE_LEN, &outbuf) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to open %s"), CPUINFO);
return NULL;
}
ret->nprocessor = 0;
ret->processor = NULL;
if (virSysinfoParseARMProcessor(outbuf, ret) < 0)
return NULL;
if (virSysinfoParseARMSystem(outbuf, &ret->system) < 0)
return NULL;
return g_steal_pointer(&ret);
}
static const char *
virSysinfoParseS390Delimited(const char *base, const char *name, char **value,
char delim1, char delim2)
{
const char *start;
const char *end;
if (delim1 != delim2 &&
(start = strstr(base, name)) &&
(start = strchr(start, delim1))) {
start += 1;
end = strchr(start, delim2);
if (!end)
end = start + strlen(start);
virSkipSpaces(&start);
*value = g_strndup(start, end - start);
virTrimSpaces(*value, NULL);
return end;
}
return NULL;
}
static const char *
virSysinfoParseS390Line(const char *base, const char *name, char **value)
{
return virSysinfoParseS390Delimited(base, name, value, ':', '\n');
}
static int
virSysinfoParseS390System(const char *base, virSysinfoSystemDefPtr *sysdef)
{
int ret = -1;
virSysinfoSystemDefPtr def;
if (VIR_ALLOC(def) < 0)
return ret;
if (!virSysinfoParseS390Line(base, "Manufacturer", &def->manufacturer))
goto cleanup;
if (!virSysinfoParseS390Line(base, "Type", &def->family))
goto cleanup;
if (!virSysinfoParseS390Line(base, "Sequence Code", &def->serial))
goto cleanup;
if (!def->manufacturer && !def->product && !def->version &&
!def->serial && !def->uuid && !def->sku && !def->family) {
virSysinfoSystemDefFree(def);
def = NULL;
}
*sysdef = def;
def = NULL;
ret = 0;
cleanup:
virSysinfoSystemDefFree(def);
return ret;
}
static int
virSysinfoParseS390Processor(const char *base, virSysinfoDefPtr ret)
{
const char *tmp_base;
char *manufacturer = NULL;
char *procline = NULL;
char *ncpu = NULL;
int result = -1;
virSysinfoProcessorDefPtr processor;
if (!(tmp_base = virSysinfoParseS390Line(base, "vendor_id", &manufacturer)))
goto error;
/* Find processor N: line and gather the processor manufacturer,
version, serial number, and family */
while ((tmp_base = strstr(tmp_base, "processor "))
&& (tmp_base = virSysinfoParseS390Line(tmp_base, "processor ",
&procline))) {
if (VIR_EXPAND_N(ret->processor, ret->nprocessor, 1) < 0)
goto error;
processor = &ret->processor[ret->nprocessor - 1];
processor->processor_manufacturer = g_strdup(manufacturer);
if (!virSysinfoParseS390Delimited(procline, "version",
&processor->processor_version,
'=', ',') ||
!virSysinfoParseS390Delimited(procline, "identification",
&processor->processor_serial_number,
'=', ',') ||
!virSysinfoParseS390Delimited(procline, "machine",
&processor->processor_family,
'=', '\n'))
goto error;
VIR_FREE(procline);
}
/* now, for each processor found, extract the frequency information */
tmp_base = base;
while ((tmp_base = strstr(tmp_base, "cpu number")) &&
(tmp_base = virSysinfoParseS390Line(tmp_base, "cpu number", &ncpu))) {
unsigned int n;
char *mhz = NULL;
if (virStrToLong_uip(ncpu, NULL, 10, &n) < 0)
goto error;
if (n >= ret->nprocessor) {
VIR_DEBUG("CPU number '%u' out of range", n);
goto cleanup;
}
if (!(tmp_base = strstr(tmp_base, "cpu MHz static")) ||
!virSysinfoParseS390Line(tmp_base, "cpu MHz static", &mhz))
goto cleanup;
ret->processor[n].processor_max_speed = mhz;
VIR_FREE(ncpu);
}
cleanup:
result = 0;
error:
VIR_FREE(manufacturer);
VIR_FREE(procline);
VIR_FREE(ncpu);
return result;
}
/* virSysinfoRead for s390x
* Gathers sysinfo data from /proc/sysinfo and /proc/cpuinfo */
virSysinfoDefPtr
virSysinfoReadS390(void)
{
g_auto(virSysinfoDefPtr) ret = NULL;
g_autofree char *outbuf = NULL;
if (VIR_ALLOC(ret) < 0)
return NULL;
/* Gather info from /proc/cpuinfo */
if (virFileReadAll(CPUINFO, CPUINFO_FILE_LEN, &outbuf) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to open %s"), CPUINFO);
return NULL;
}
if (virSysinfoParseS390Processor(outbuf, ret) < 0)
return NULL;
/* Free buffer before reading next file */
VIR_FREE(outbuf);
/* Gather info from /proc/sysinfo */
if (virFileReadAll(SYSINFO, 8192, &outbuf) < 0) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to open %s"), SYSINFO);
return NULL;
}
if (virSysinfoParseS390System(outbuf, &ret->system) < 0)
return NULL;
return g_steal_pointer(&ret);
}
static int
virSysinfoParseBIOS(const char *base, virSysinfoBIOSDefPtr *bios)
{
int ret = -1;
const char *cur;
char *eol = NULL;
virSysinfoBIOSDefPtr def;
if ((cur = strstr(base, "BIOS Information")) == NULL)
return 0;
if (VIR_ALLOC(def) < 0)
return ret;
base = cur;
if ((cur = strstr(base, "Vendor: ")) != NULL) {
cur += 8;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->vendor = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Version: ")) != NULL) {
cur += 9;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->version = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Release Date: ")) != NULL) {
cur += 14;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->date = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "BIOS Revision: ")) != NULL) {
cur += 15;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->release = g_strndup(cur, eol - cur);
}
if (!def->vendor && !def->version &&
!def->date && !def->release) {
virSysinfoBIOSDefFree(def);
def = NULL;
}
*bios = def;
def = NULL;
ret = 0;
virSysinfoBIOSDefFree(def);
return ret;
}
static int
virSysinfoParseX86System(const char *base, virSysinfoSystemDefPtr *sysdef)
{
int ret = -1;
const char *cur;
char *eol = NULL;
virSysinfoSystemDefPtr def;
if ((cur = strstr(base, "System Information")) == NULL)
return 0;
if (VIR_ALLOC(def) < 0)
return ret;
base = cur;
if ((cur = strstr(base, "Manufacturer: ")) != NULL) {
cur += 14;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->manufacturer = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Product Name: ")) != NULL) {
cur += 14;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->product = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Version: ")) != NULL) {
cur += 9;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->version = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Serial Number: ")) != NULL) {
cur += 15;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->serial = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "UUID: ")) != NULL) {
cur += 6;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->uuid = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "SKU Number: ")) != NULL) {
cur += 12;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->sku = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Family: ")) != NULL) {
cur += 8;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->family = g_strndup(cur, eol - cur);
}
if (!def->manufacturer && !def->product && !def->version &&
!def->serial && !def->uuid && !def->sku && !def->family) {
virSysinfoSystemDefFree(def);
def = NULL;
}
*sysdef = def;
def = NULL;
ret = 0;
virSysinfoSystemDefFree(def);
return ret;
}
static int
virSysinfoParseX86BaseBoard(const char *base,
virSysinfoBaseBoardDefPtr *baseBoard,
size_t *nbaseBoard)
{
int ret = -1;
const char *cur;
char *eol = NULL;
virSysinfoBaseBoardDefPtr boards = NULL;
size_t nboards = 0;
char *board_type = NULL;
while (base && (cur = strstr(base, "Base Board Information"))) {
virSysinfoBaseBoardDefPtr def;
if (VIR_EXPAND_N(boards, nboards, 1) < 0)
goto cleanup;
def = &boards[nboards - 1];
base = cur + 22;
if ((cur = strstr(base, "Manufacturer: ")) != NULL) {
cur += 14;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->manufacturer = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Product Name: ")) != NULL) {
cur += 14;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->product = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Version: ")) != NULL) {
cur += 9;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->version = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Serial Number: ")) != NULL) {
cur += 15;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->serial = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Asset Tag: ")) != NULL) {
cur += 11;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->asset = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Location In Chassis: ")) != NULL) {
cur += 21;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->location = g_strndup(cur, eol - cur);
}
if (!def->manufacturer && !def->product && !def->version &&
!def->serial && !def->asset && !def->location)
nboards--;
}
if (nboards == 0) {
VIR_FREE(boards);
} else {
/* This is safe, as we can be only shrinking the memory */
ignore_value(VIR_REALLOC_N(boards, nboards));
}
*baseBoard = boards;
*nbaseBoard = nboards;
boards = NULL;
nboards = 0;
ret = 0;
cleanup:
while (nboards--)
virSysinfoBaseBoardDefClear(&boards[nboards]);
VIR_FREE(boards);
VIR_FREE(board_type);
return ret;
}
static int
virSysinfoParseX86Chassis(const char *base,
virSysinfoChassisDefPtr *chassisdef)
{
int ret = -1;
const char *cur;
char *eol = NULL;
virSysinfoChassisDefPtr def;
if ((cur = strstr(base, "Chassis Information")) == NULL)
return 0;
if (VIR_ALLOC(def) < 0)
return ret;
base = cur;
if ((cur = strstr(base, "Manufacturer: ")) != NULL) {
cur += 14;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->manufacturer = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Version: ")) != NULL) {
cur += 9;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->version = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Serial Number: ")) != NULL) {
cur += 15;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->serial = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Asset Tag: ")) != NULL) {
cur += 11;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->asset = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "SKU Number: ")) != NULL) {
cur += 12;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
def->sku = g_strndup(cur, eol - cur);
}
if (!def->manufacturer && !def->version &&
!def->serial && !def->asset && !def->sku) {
virSysinfoChassisDefFree(def);
def = NULL;
}
*chassisdef = def;
def = NULL;
ret = 0;
virSysinfoChassisDefFree(def);
return ret;
}
static int
virSysinfoParseX86Processor(const char *base, virSysinfoDefPtr ret)
{
const char *cur, *tmp_base;
char *eol;
virSysinfoProcessorDefPtr processor;
while ((tmp_base = strstr(base, "Processor Information")) != NULL) {
base = tmp_base;
eol = NULL;
if (VIR_EXPAND_N(ret->processor, ret->nprocessor, 1) < 0)
return -1;
processor = &ret->processor[ret->nprocessor - 1];
if ((cur = strstr(base, "Socket Designation: ")) != NULL) {
cur += 20;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
processor->processor_socket_destination = g_strndup(cur,
eol - cur);
}
if ((cur = strstr(base, "Type: ")) != NULL) {
cur += 6;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
processor->processor_type = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Family: ")) != NULL) {
cur += 8;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
processor->processor_family = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Manufacturer: ")) != NULL) {
cur += 14;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
processor->processor_manufacturer = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Signature: ")) != NULL) {
cur += 11;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
processor->processor_signature = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Version: ")) != NULL) {
cur += 9;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
processor->processor_version = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "External Clock: ")) != NULL) {
cur += 16;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
processor->processor_external_clock = g_strndup(cur,
eol - cur);
}
if ((cur = strstr(base, "Max Speed: ")) != NULL) {
cur += 11;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
processor->processor_max_speed = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Status: ")) != NULL) {
cur += 8;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
processor->processor_status = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Serial Number: ")) != NULL) {
cur += 15;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
processor->processor_serial_number = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Part Number: ")) != NULL) {
cur += 13;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
processor->processor_part_number = g_strndup(cur, eol - cur);
}
base += strlen("Processor Information");
}
return 0;
}
static int
virSysinfoParseX86Memory(const char *base, virSysinfoDefPtr ret)
{
const char *cur, *tmp_base;
char *eol;
virSysinfoMemoryDefPtr memory;
while ((tmp_base = strstr(base, "Memory Device")) != NULL) {
base = tmp_base;
eol = NULL;
if (VIR_EXPAND_N(ret->memory, ret->nmemory, 1) < 0)
return -1;
memory = &ret->memory[ret->nmemory - 1];
if ((cur = strstr(base, "Size: ")) != NULL) {
cur += 6;
eol = strchr(cur, '\n');
if (STREQLEN(cur, "No Module Installed", eol - cur))
goto next;
virSkipSpacesBackwards(cur, &eol);
if (eol)
memory->memory_size = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Form Factor: ")) != NULL) {
cur += 13;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
memory->memory_form_factor = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Locator: ")) != NULL) {
cur += 9;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
memory->memory_locator = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Bank Locator: ")) != NULL) {
cur += 14;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
memory->memory_bank_locator = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Type: ")) != NULL) {
cur += 6;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
memory->memory_type = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Type Detail: ")) != NULL) {
cur += 13;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
memory->memory_type_detail = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Speed: ")) != NULL) {
cur += 7;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
memory->memory_speed = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Manufacturer: ")) != NULL) {
cur += 14;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
memory->memory_manufacturer = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Serial Number: ")) != NULL) {
cur += 15;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
memory->memory_serial_number = g_strndup(cur, eol - cur);
}
if ((cur = strstr(base, "Part Number: ")) != NULL) {
cur += 13;
eol = strchr(cur, '\n');
virSkipSpacesBackwards(cur, &eol);
if (eol)
memory->memory_part_number = g_strndup(cur, eol - cur);
}
next:
base += strlen("Memory Device");
}
return 0;
}
virSysinfoDefPtr
virSysinfoReadDMI(void)
{
char *path;
virSysinfoDefPtr ret = NULL;
char *outbuf = NULL;
virCommandPtr cmd;
path = virFindFileInPath(SYSINFO_SMBIOS_DECODER);
if (path == NULL) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("Failed to find path for %s binary"),
SYSINFO_SMBIOS_DECODER);
return NULL;
}
cmd = virCommandNewArgList(path, "-q", "-t", "0,1,2,3,4,17", NULL);
VIR_FREE(path);
virCommandSetOutputBuffer(cmd, &outbuf);
if (virCommandRun(cmd, NULL) < 0)
goto cleanup;
if (VIR_ALLOC(ret) < 0)
goto error;
ret->type = VIR_SYSINFO_SMBIOS;
if (virSysinfoParseBIOS(outbuf, &ret->bios) < 0)
goto error;
if (virSysinfoParseX86System(outbuf, &ret->system) < 0)
goto error;
if (virSysinfoParseX86BaseBoard(outbuf, &ret->baseBoard, &ret->nbaseBoard) < 0)
goto error;
if (virSysinfoParseX86Chassis(outbuf, &ret->chassis) < 0)
goto error;
ret->nprocessor = 0;
ret->processor = NULL;
if (virSysinfoParseX86Processor(outbuf, ret) < 0)
goto error;
ret->nmemory = 0;
ret->memory = NULL;
if (virSysinfoParseX86Memory(outbuf, ret) < 0)
goto error;
cleanup:
VIR_FREE(outbuf);
virCommandFree(cmd);
return ret;
error:
virSysinfoDefFree(ret);
ret = NULL;
goto cleanup;
}
/**
* virSysinfoRead:
*
* Tries to read the SMBIOS information from the current host
*
* Returns: a filled up sysinfo structure or NULL in case of error
*/
virSysinfoDefPtr
virSysinfoRead(void)
{
#if defined(__powerpc__)
return virSysinfoReadPPC();
#elif defined(__arm__) || defined(__aarch64__)
return virSysinfoReadARM();
#elif defined(__s390__) || defined(__s390x__)
return virSysinfoReadS390();
#elif !defined(WIN32) && \
(defined(__x86_64__) || \
defined(__i386__) || \
defined(__amd64__))
return virSysinfoReadDMI();
#else /* WIN32 || not supported arch */
/*
* this can probably be extracted from Windows using API or registry
* http://www.microsoft.com/whdc/system/platform/firmware/SMBIOS.mspx
*/
virReportSystemError(ENOSYS, "%s",
_("Host sysinfo extraction not supported on this platform"));
return NULL;
#endif /* WIN32 || not supported arch */
}
static void
virSysinfoBIOSFormat(virBufferPtr buf, virSysinfoBIOSDefPtr def)
{
if (!def)
return;
virBufferAddLit(buf, "<bios>\n");
virBufferAdjustIndent(buf, 2);
virBufferEscapeString(buf, "<entry name='vendor'>%s</entry>\n",
def->vendor);
virBufferEscapeString(buf, "<entry name='version'>%s</entry>\n",
def->version);
virBufferEscapeString(buf, "<entry name='date'>%s</entry>\n",
def->date);
virBufferEscapeString(buf, "<entry name='release'>%s</entry>\n",
def->release);
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</bios>\n");
}
static void
virSysinfoSystemFormat(virBufferPtr buf, virSysinfoSystemDefPtr def)
{
if (!def)
return;
virBufferAddLit(buf, "<system>\n");
virBufferAdjustIndent(buf, 2);
virBufferEscapeString(buf, "<entry name='manufacturer'>%s</entry>\n",
def->manufacturer);
virBufferEscapeString(buf, "<entry name='product'>%s</entry>\n",
def->product);
virBufferEscapeString(buf, "<entry name='version'>%s</entry>\n",
def->version);
virBufferEscapeString(buf, "<entry name='serial'>%s</entry>\n",
def->serial);
virBufferEscapeString(buf, "<entry name='uuid'>%s</entry>\n",
def->uuid);
virBufferEscapeString(buf, "<entry name='sku'>%s</entry>\n",
def->sku);
virBufferEscapeString(buf, "<entry name='family'>%s</entry>\n",
def->family);
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</system>\n");
}
static void
virSysinfoBaseBoardFormat(virBufferPtr buf,
virSysinfoBaseBoardDefPtr baseBoard,
size_t nbaseBoard)
{
virSysinfoBaseBoardDefPtr def;
size_t i;
for (i = 0; i < nbaseBoard; i++) {
def = baseBoard + i;
virBufferAddLit(buf, "<baseBoard>\n");
virBufferAdjustIndent(buf, 2);
virBufferEscapeString(buf, "<entry name='manufacturer'>%s</entry>\n",
def->manufacturer);
virBufferEscapeString(buf, "<entry name='product'>%s</entry>\n",
def->product);
virBufferEscapeString(buf, "<entry name='version'>%s</entry>\n",
def->version);
virBufferEscapeString(buf, "<entry name='serial'>%s</entry>\n",
def->serial);
virBufferEscapeString(buf, "<entry name='asset'>%s</entry>\n",
def->asset);
virBufferEscapeString(buf, "<entry name='location'>%s</entry>\n",
def->location);
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</baseBoard>\n");
}
}
static void
virSysinfoChassisFormat(virBufferPtr buf,
virSysinfoChassisDefPtr def)
{
if (!def)
return;
virBufferAddLit(buf, "<chassis>\n");
virBufferAdjustIndent(buf, 2);
virBufferEscapeString(buf, "<entry name='manufacturer'>%s</entry>\n",
def->manufacturer);
virBufferEscapeString(buf, "<entry name='version'>%s</entry>\n",
def->version);
virBufferEscapeString(buf, "<entry name='serial'>%s</entry>\n",
def->serial);
virBufferEscapeString(buf, "<entry name='asset'>%s</entry>\n",
def->asset);
virBufferEscapeString(buf, "<entry name='sku'>%s</entry>\n",
def->sku);
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</chassis>\n");
}
static void
virSysinfoProcessorFormat(virBufferPtr buf, virSysinfoDefPtr def)
{
size_t i;
virSysinfoProcessorDefPtr processor;
for (i = 0; i < def->nprocessor; i++) {
processor = &def->processor[i];
if (!processor->processor_socket_destination &&
!processor->processor_type &&
!processor->processor_family &&
!processor->processor_manufacturer &&
!processor->processor_signature &&
!processor->processor_version &&
!processor->processor_external_clock &&
!processor->processor_max_speed &&
!processor->processor_status &&
!processor->processor_serial_number &&
!processor->processor_part_number)
continue;
virBufferAddLit(buf, "<processor>\n");
virBufferAdjustIndent(buf, 2);
virBufferEscapeString(buf,
"<entry name='socket_destination'>%s</entry>\n",
processor->processor_socket_destination);
virBufferEscapeString(buf, "<entry name='type'>%s</entry>\n",
processor->processor_type);
virBufferEscapeString(buf, "<entry name='family'>%s</entry>\n",
processor->processor_family);
virBufferEscapeString(buf, "<entry name='manufacturer'>%s</entry>\n",
processor->processor_manufacturer);
virBufferEscapeString(buf, "<entry name='signature'>%s</entry>\n",
processor->processor_signature);
virBufferEscapeString(buf, "<entry name='version'>%s</entry>\n",
processor->processor_version);
virBufferEscapeString(buf, "<entry name='external_clock'>%s</entry>\n",
processor->processor_external_clock);
virBufferEscapeString(buf, "<entry name='max_speed'>%s</entry>\n",
processor->processor_max_speed);
virBufferEscapeString(buf, "<entry name='status'>%s</entry>\n",
processor->processor_status);
virBufferEscapeString(buf, "<entry name='serial_number'>%s</entry>\n",
processor->processor_serial_number);
virBufferEscapeString(buf, "<entry name='part_number'>%s</entry>\n",
processor->processor_part_number);
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</processor>\n");
}
}
static void
virSysinfoMemoryFormat(virBufferPtr buf, virSysinfoDefPtr def)
{
size_t i;
virSysinfoMemoryDefPtr memory;
for (i = 0; i < def->nmemory; i++) {
memory = &def->memory[i];
if (!memory->memory_size &&
!memory->memory_form_factor &&
!memory->memory_locator &&
!memory->memory_bank_locator &&
!memory->memory_type &&
!memory->memory_type_detail &&
!memory->memory_speed &&
!memory->memory_manufacturer &&
!memory->memory_serial_number &&
!memory->memory_part_number)
continue;
virBufferAddLit(buf, "<memory_device>\n");
virBufferAdjustIndent(buf, 2);
virBufferEscapeString(buf, "<entry name='size'>%s</entry>\n",
memory->memory_size);
virBufferEscapeString(buf,
"<entry name='form_factor'>%s</entry>\n",
memory->memory_form_factor);
virBufferEscapeString(buf, "<entry name='locator'>%s</entry>\n",
memory->memory_locator);
virBufferEscapeString(buf,
"<entry name='bank_locator'>%s</entry>\n",
memory->memory_bank_locator);
virBufferEscapeString(buf, "<entry name='type'>%s</entry>\n",
memory->memory_type);
virBufferEscapeString(buf,
"<entry name='type_detail'>%s</entry>\n",
memory->memory_type_detail);
virBufferEscapeString(buf, "<entry name='speed'>%s</entry>\n",
memory->memory_speed);
virBufferEscapeString(buf,
"<entry name='manufacturer'>%s</entry>\n",
memory->memory_manufacturer);
virBufferEscapeString(buf,
"<entry name='serial_number'>%s</entry>\n",
memory->memory_serial_number);
virBufferEscapeString(buf,
"<entry name='part_number'>%s</entry>\n",
memory->memory_part_number);
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</memory_device>\n");
}
}
static void
virSysinfoOEMStringsFormat(virBufferPtr buf, virSysinfoOEMStringsDefPtr def)
{
size_t i;
if (!def)
return;
virBufferAddLit(buf, "<oemStrings>\n");
virBufferAdjustIndent(buf, 2);
for (i = 0; i < def->nvalues; i++) {
virBufferEscapeString(buf, "<entry>%s</entry>\n",
def->values[i]);
}
virBufferAdjustIndent(buf, -2);
virBufferAddLit(buf, "</oemStrings>\n");
}
/**
* virSysinfoFormat:
* @buf: buffer to append output to (may use auto-indentation)
* @def: structure to convert to xml string
*
* Returns 0 on success, -1 on failure after generating an error message.
*/
int
virSysinfoFormat(virBufferPtr buf, virSysinfoDefPtr def)
{
virBuffer attrBuf = VIR_BUFFER_INITIALIZER;
virBuffer childrenBuf = VIR_BUFFER_INIT_CHILD(buf);
const char *type = virSysinfoTypeToString(def->type);
if (!type) {
virReportError(VIR_ERR_INTERNAL_ERROR,
_("unexpected sysinfo type model %d"),
def->type);
virBufferFreeAndReset(buf);
return -1;
}
virSysinfoBIOSFormat(&childrenBuf, def->bios);
virSysinfoSystemFormat(&childrenBuf, def->system);
virSysinfoBaseBoardFormat(&childrenBuf, def->baseBoard, def->nbaseBoard);
virSysinfoChassisFormat(&childrenBuf, def->chassis);
virSysinfoProcessorFormat(&childrenBuf, def);
virSysinfoMemoryFormat(&childrenBuf, def);
virSysinfoOEMStringsFormat(&childrenBuf, def->oemStrings);
virBufferAsprintf(&attrBuf, " type='%s'", type);
virXMLFormatElement(buf, "sysinfo", &attrBuf, &childrenBuf);
return 0;
}
#define CHECK_FIELD(name, desc) \
do { \
if (STRNEQ_NULLABLE(src->name, dst->name)) { \
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, \
_("Target sysinfo %s %s does not match source %s"), \
desc, NULLSTR(dst->name), NULLSTR(src->name)); \
return false; \
} \
} while (0)
static bool
virSysinfoBIOSIsEqual(virSysinfoBIOSDefPtr src,
virSysinfoBIOSDefPtr dst)
{
if (!src && !dst)
return true;
if ((src && !dst) || (!src && dst)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Target sysinfo does not match source"));
return false;
}
CHECK_FIELD(vendor, "BIOS vendor");
CHECK_FIELD(version, "BIOS version");
CHECK_FIELD(date, "BIOS date");
CHECK_FIELD(release, "BIOS release");
return true;
}
static bool
virSysinfoSystemIsEqual(virSysinfoSystemDefPtr src,
virSysinfoSystemDefPtr dst)
{
if (!src && !dst)
return true;
if ((src && !dst) || (!src && dst)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Target sysinfo does not match source"));
return false;
}
CHECK_FIELD(manufacturer, "system vendor");
CHECK_FIELD(product, "system product");
CHECK_FIELD(version, "system version");
CHECK_FIELD(serial, "system serial");
CHECK_FIELD(uuid, "system uuid");
CHECK_FIELD(sku, "system sku");
CHECK_FIELD(family, "system family");
return true;
}
static bool
virSysinfoBaseBoardIsEqual(virSysinfoBaseBoardDefPtr src,
virSysinfoBaseBoardDefPtr dst)
{
if (!src && !dst)
return true;
if ((src && !dst) || (!src && dst)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Target base board does not match source"));
return false;
}
CHECK_FIELD(manufacturer, "base board vendor");
CHECK_FIELD(product, "base board product");
CHECK_FIELD(version, "base board version");
CHECK_FIELD(serial, "base board serial");
CHECK_FIELD(asset, "base board asset");
CHECK_FIELD(location, "base board location");
return true;
}
static bool
virSysinfoChassisIsEqual(virSysinfoChassisDefPtr src,
virSysinfoChassisDefPtr dst)
{
if (!src && !dst)
return true;
if ((src && !dst) || (!src && dst)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Target chassis does not match source"));
return false;
}
CHECK_FIELD(manufacturer, "chassis vendor");
CHECK_FIELD(version, "chassis version");
CHECK_FIELD(serial, "chassis serial");
CHECK_FIELD(asset, "chassis asset");
CHECK_FIELD(sku, "chassis sku");
return true;
}
#undef CHECK_FIELD
bool virSysinfoIsEqual(virSysinfoDefPtr src,
virSysinfoDefPtr dst)
{
size_t i;
if (!src && !dst)
return true;
if ((src && !dst) || (!src && dst)) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED, "%s",
_("Target sysinfo does not match source"));
return false;
}
if (src->type != dst->type) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Target sysinfo %s does not match source %s"),
virSysinfoTypeToString(dst->type),
virSysinfoTypeToString(src->type));
return false;
}
if (!virSysinfoBIOSIsEqual(src->bios, dst->bios))
return false;
if (!virSysinfoSystemIsEqual(src->system, dst->system))
return false;
if (src->nbaseBoard != dst->nbaseBoard) {
virReportError(VIR_ERR_CONFIG_UNSUPPORTED,
_("Target sysinfo base board count '%zu' does not match source '%zu'"),
dst->nbaseBoard, src->nbaseBoard);
return false;
}
for (i = 0; i < src->nbaseBoard; i++)
if (!virSysinfoBaseBoardIsEqual(src->baseBoard + i,
dst->baseBoard + i))
return false;
if (!virSysinfoChassisIsEqual(src->chassis, dst->chassis))
return false;
return true;
}