mirror of
https://github.com/libguestfs/libguestfs.git
synced 2026-03-21 22:53:37 +00:00
p2v: Use lscpu instead of libvirt to get CPU information.
Don't get the CPU information from libvirt, because including libvirt
and all dependencies in the virt-p2v ISO bloats everything.
Instead get most of the information we need from the util-linux
program 'lscpu'.
Unfortunately the CPU model cannot be retrieved.
Example output:
$ ./run virt-p2v --cmdline="p2v.dump_config_and_exit"
[...]
cpu vendor . . . Intel
cpu sockets . . 2
cpu cores . . . 8
cpu threads . . 1
flags . . . . . acpi apic pae
This updates commit 963d6c3be7.
This commit is contained in:
@@ -100,7 +100,6 @@ virt_p2v_CPPFLAGS = \
|
||||
virt_p2v_CFLAGS = \
|
||||
-pthread \
|
||||
$(WARN_CFLAGS) $(WERROR_CFLAGS) \
|
||||
$(LIBVIRT_CFLAGS) \
|
||||
$(PCRE_CFLAGS) \
|
||||
$(LIBXML2_CFLAGS) \
|
||||
$(GTK_CFLAGS) \
|
||||
@@ -109,7 +108,6 @@ virt_p2v_CFLAGS = \
|
||||
virt_p2v_LDADD = \
|
||||
$(top_builddir)/common/utils/libutils.la \
|
||||
$(top_builddir)/common/miniexpect/libminiexpect.la \
|
||||
$(LIBVIRT_LIBS) \
|
||||
$(PCRE_LIBS) \
|
||||
$(LIBXML2_LIBS) \
|
||||
$(GTK_LIBS) \
|
||||
@@ -126,16 +124,9 @@ dependencies_files = \
|
||||
dependencies.redhat \
|
||||
dependencies.suse
|
||||
|
||||
if HAVE_LIBVIRT
|
||||
dependencies_have_libvirt = -DHAVE_LIBVIRT=1
|
||||
endif
|
||||
|
||||
$(dependencies_files): dependencies.m4
|
||||
define=`echo $@ | $(SED) 's/dependencies.//;y/abcdefghijklmnopqrstuvwxyz/ABCDEFGHIJKLMNOPQRSTUVWXYZ/'`; \
|
||||
m4 -D$$define=1 \
|
||||
-DGTK_VERSION=$(GTK_VERSION) \
|
||||
$(dependencies_have_libvirt) \
|
||||
$< > $@-t
|
||||
m4 -D$$define=1 -DGTK_VERSION=$(GTK_VERSION) $< > $@-t
|
||||
mv $@-t $@
|
||||
|
||||
# Support files needed by the virt-p2v-make-* scripts.
|
||||
|
||||
345
p2v/cpuid.c
345
p2v/cpuid.c
@@ -17,20 +17,17 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Process CPU capabilities into libvirt-compatible C<E<lt>cpuE<gt>> data.
|
||||
* Find CPU vendor, topology and some CPU flags.
|
||||
*
|
||||
* If libvirt is available at compile time then this is quite
|
||||
* simple - libvirt API C<virConnectGetCapabilities> provides
|
||||
* a C<E<lt>hostE<ge>> element which has mostly what we need.
|
||||
* lscpu (from util-linux) provides CPU vendor, topology and flags.
|
||||
*
|
||||
* Flags C<acpi>, C<apic>, C<pae> still have to be parsed out of
|
||||
* F</proc/cpuinfo> because these will not necessarily be present in
|
||||
* the libvirt capabilities directly (they are implied by the
|
||||
* processor model, requiring a complex lookup in the CPU map).
|
||||
* ACPI can be read by seeing if F</sys/firmware/acpi> exists.
|
||||
*
|
||||
* CPU model is essentially impossible to get without using libvirt,
|
||||
* but we cannot use libvirt for the reasons outlined in this message:
|
||||
* https://www.redhat.com/archives/libvirt-users/2017-March/msg00071.html
|
||||
*
|
||||
* Note that #vCPUs and amount of RAM is handled by F<main.c>.
|
||||
*
|
||||
* See: L<https://libvirt.org/formatdomain.html#elementsCPU>
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
@@ -40,15 +37,10 @@
|
||||
#include <stdarg.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <error.h>
|
||||
#include <libintl.h>
|
||||
|
||||
#ifdef HAVE_LIBVIRT
|
||||
#include <libvirt/libvirt.h>
|
||||
#include <libvirt/virterror.h>
|
||||
#endif
|
||||
|
||||
#include <libxml/xpath.h>
|
||||
|
||||
#include "c-ctype.h"
|
||||
#include "getprogname.h"
|
||||
#include "ignore-value.h"
|
||||
|
||||
@@ -65,235 +57,166 @@ free_cpu_config (struct cpu_config *cpu)
|
||||
}
|
||||
|
||||
/**
|
||||
* Read flags from F</proc/cpuinfo>.
|
||||
* Get the output of lscpu as a list of (key, value) pairs (as a
|
||||
* flattened list of strings).
|
||||
*/
|
||||
static void
|
||||
cpuinfo_flags (struct cpu_config *cpu)
|
||||
static char **
|
||||
get_lscpu (void)
|
||||
{
|
||||
const char *cmd;
|
||||
CLEANUP_PCLOSE FILE *fp = NULL;
|
||||
CLEANUP_FREE char *flag = NULL;
|
||||
CLEANUP_FREE char *line = NULL;
|
||||
ssize_t len;
|
||||
size_t buflen = 0;
|
||||
char **ret = NULL;
|
||||
size_t ret_size = 0;
|
||||
|
||||
/* Get the flags, one per line. */
|
||||
cmd = "< /proc/cpuinfo "
|
||||
#if defined(__arm__)
|
||||
"grep ^Features"
|
||||
#else
|
||||
"grep ^flags"
|
||||
#endif
|
||||
" | awk '{ for (i = 3; i <= NF; ++i) { print $i }; exit }'";
|
||||
cmd = "lscpu";
|
||||
|
||||
fp = popen (cmd, "re");
|
||||
if (fp == NULL) {
|
||||
perror ("/proc/cpuinfo");
|
||||
return;
|
||||
perror (cmd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (errno = 0, (len = getline (&flag, &buflen, fp)) != -1) {
|
||||
if (len > 0 && flag[len-1] == '\n')
|
||||
flag[len-1] = '\0';
|
||||
ret = malloc (sizeof (char *));
|
||||
if (ret == NULL) error (EXIT_FAILURE, errno, "malloc");
|
||||
ret[0] = NULL;
|
||||
|
||||
if (STREQ (flag, "acpi"))
|
||||
cpu->acpi = 1;
|
||||
else if (STREQ (flag, "apic"))
|
||||
cpu->apic = 1;
|
||||
else if (STREQ (flag, "pae"))
|
||||
cpu->pae = 1;
|
||||
while (errno = 0, (len = getline (&line, &buflen, fp)) != -1) {
|
||||
char *p;
|
||||
char *key, *value;
|
||||
|
||||
if (len > 0 && line[len-1] == '\n')
|
||||
line[len-1] = '\0';
|
||||
|
||||
/* Split the line at the first ':' character. */
|
||||
p = strchr (line, ':');
|
||||
if (p == NULL)
|
||||
continue;
|
||||
|
||||
*p = '\0';
|
||||
key = strdup (line);
|
||||
/* Skip leading whitespace in the value. */
|
||||
for (++p; *p && c_isspace (*p); ++p)
|
||||
;
|
||||
value = strdup (p);
|
||||
|
||||
/* Add key and value to the list, and trailing NULL pointer. */
|
||||
ret_size += 2;
|
||||
ret = realloc (ret, (ret_size + 1) * sizeof (char *));
|
||||
if (ret == NULL) error (EXIT_FAILURE, errno, "realloc");
|
||||
ret[ret_size-2] = key;
|
||||
ret[ret_size-1] = value;
|
||||
ret[ret_size] = NULL;
|
||||
}
|
||||
|
||||
if (errno) {
|
||||
perror ("getline");
|
||||
return;
|
||||
perror (cmd);
|
||||
guestfs_int_free_string_list (ret);
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef HAVE_LIBVIRT
|
||||
|
||||
static void
|
||||
ignore_errors (void *ignore, virErrorPtr ignore2)
|
||||
{
|
||||
/* empty */
|
||||
}
|
||||
|
||||
static void libvirt_error (const char *fs, ...) __attribute__((format (printf,1,2)));
|
||||
|
||||
static void
|
||||
libvirt_error (const char *fs, ...)
|
||||
{
|
||||
va_list args;
|
||||
CLEANUP_FREE char *msg = NULL;
|
||||
int len;
|
||||
virErrorPtr err;
|
||||
|
||||
va_start (args, fs);
|
||||
len = vasprintf (&msg, fs, args);
|
||||
va_end (args);
|
||||
|
||||
if (len < 0) goto fallback;
|
||||
|
||||
/* In all recent libvirt, this retrieves the thread-local error. */
|
||||
err = virGetLastError ();
|
||||
if (err)
|
||||
fprintf (stderr,
|
||||
"%s: %s: %s [code=%d int1=%d]\n",
|
||||
getprogname (), msg, err->message, err->code, err->int1);
|
||||
else
|
||||
fallback:
|
||||
fprintf (stderr, "%s: %s\n", getprogname (), msg);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the capabilities from libvirt and parse out the fields
|
||||
* we care about.
|
||||
* Read a single field from lscpu output.
|
||||
*
|
||||
* If the field does not exist, returns C<NULL>.
|
||||
*/
|
||||
static const char *
|
||||
get_field (char **lscpu, const char *key)
|
||||
{
|
||||
size_t i;
|
||||
|
||||
for (i = 0; lscpu[i] != NULL; i += 2) {
|
||||
if (STREQ (lscpu[i], key))
|
||||
return lscpu[i+1];
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read the CPU vendor from lscpu output.
|
||||
*/
|
||||
static void
|
||||
libvirt_capabilities (struct cpu_config *cpu)
|
||||
get_vendor (char **lscpu, struct cpu_config *cpu)
|
||||
{
|
||||
virConnectPtr conn;
|
||||
CLEANUP_FREE char *capabilities_xml = NULL;
|
||||
CLEANUP_XMLFREEDOC xmlDocPtr doc = NULL;
|
||||
CLEANUP_XMLXPATHFREECONTEXT xmlXPathContextPtr xpathCtx = NULL;
|
||||
CLEANUP_XMLXPATHFREEOBJECT xmlXPathObjectPtr xpathObj = NULL;
|
||||
const char *xpathexpr;
|
||||
xmlNodeSetPtr nodes;
|
||||
size_t nr_nodes, i;
|
||||
xmlNodePtr node;
|
||||
const char *vendor = get_field (lscpu, "Vendor ID");
|
||||
|
||||
/* Connect to libvirt and get the capabilities XML. */
|
||||
conn = virConnectOpenReadOnly (NULL);
|
||||
if (!conn) {
|
||||
libvirt_error (_("could not connect to libvirt"));
|
||||
return;
|
||||
if (vendor) {
|
||||
/* Note this mapping comes from /usr/share/libvirt/cpu_map.xml */
|
||||
if (STREQ (vendor, "GenuineIntel"))
|
||||
cpu->vendor = strdup ("Intel");
|
||||
else if (STREQ (vendor, "AuthenticAMD"))
|
||||
cpu->vendor = strdup ("AMD");
|
||||
/* Currently aarch64 lscpu has no Vendor ID XXX. */
|
||||
}
|
||||
|
||||
/* Suppress default behaviour of printing errors to stderr. Note
|
||||
* you can't set this to NULL to ignore errors; setting it to NULL
|
||||
* restores the default error handler ...
|
||||
*/
|
||||
virConnSetErrorFunc (conn, NULL, ignore_errors);
|
||||
|
||||
capabilities_xml = virConnectGetCapabilities (conn);
|
||||
if (!capabilities_xml) {
|
||||
libvirt_error (_("could not get libvirt capabilities"));
|
||||
virConnectClose (conn);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Parse the capabilities XML with libxml2. */
|
||||
doc = xmlReadMemory (capabilities_xml, strlen (capabilities_xml),
|
||||
NULL, NULL, XML_PARSE_NONET);
|
||||
if (doc == NULL) {
|
||||
fprintf (stderr,
|
||||
_("%s: unable to parse capabilities XML returned by libvirt\n"),
|
||||
getprogname ());
|
||||
virConnectClose (conn);
|
||||
return;
|
||||
}
|
||||
|
||||
xpathCtx = xmlXPathNewContext (doc);
|
||||
if (xpathCtx == NULL) {
|
||||
fprintf (stderr, _("%s: unable to create new XPath context\n"),
|
||||
getprogname ());
|
||||
virConnectClose (conn);
|
||||
return;
|
||||
}
|
||||
|
||||
/* Get the CPU vendor. */
|
||||
xpathexpr = "/capabilities/host/cpu/vendor/text()";
|
||||
xpathObj = xmlXPathEvalExpression (BAD_CAST xpathexpr, xpathCtx);
|
||||
if (xpathObj == NULL) {
|
||||
fprintf (stderr, _("%s: unable to evaluate xpath expression: %s\n"),
|
||||
getprogname (), xpathexpr);
|
||||
virConnectClose (conn);
|
||||
return;
|
||||
}
|
||||
nodes = xpathObj->nodesetval;
|
||||
nr_nodes = nodes->nodeNr;
|
||||
if (nr_nodes > 0) {
|
||||
node = nodes->nodeTab[0];
|
||||
cpu->vendor = (char *) xmlNodeGetContent (node);
|
||||
}
|
||||
|
||||
/* Get the CPU model. */
|
||||
xmlXPathFreeObject (xpathObj);
|
||||
xpathexpr = "/capabilities/host/cpu/model/text()";
|
||||
xpathObj = xmlXPathEvalExpression (BAD_CAST xpathexpr, xpathCtx);
|
||||
if (xpathObj == NULL) {
|
||||
fprintf (stderr, _("%s: unable to evaluate xpath expression: %s\n"),
|
||||
getprogname (), xpathexpr);
|
||||
virConnectClose (conn);
|
||||
return;
|
||||
}
|
||||
nodes = xpathObj->nodesetval;
|
||||
nr_nodes = nodes->nodeNr;
|
||||
if (nr_nodes > 0) {
|
||||
node = nodes->nodeTab[0];
|
||||
cpu->model = (char *) xmlNodeGetContent (node);
|
||||
}
|
||||
|
||||
/* Get the topology. Note the XPath expression returns all
|
||||
* attributes of the <topology> node.
|
||||
*/
|
||||
xmlXPathFreeObject (xpathObj);
|
||||
xpathexpr = "/capabilities/host/cpu/topology/@*";
|
||||
xpathObj = xmlXPathEvalExpression (BAD_CAST xpathexpr, xpathCtx);
|
||||
if (xpathObj == NULL) {
|
||||
fprintf (stderr, _("%s: unable to evaluate xpath expression: %s\n"),
|
||||
getprogname (), xpathexpr);
|
||||
virConnectClose (conn);
|
||||
return;
|
||||
}
|
||||
nodes = xpathObj->nodesetval;
|
||||
nr_nodes = nodes->nodeNr;
|
||||
/* Iterate over the attributes of the <topology> node. */
|
||||
for (i = 0; i < nr_nodes; ++i) {
|
||||
node = nodes->nodeTab[i];
|
||||
|
||||
if (node->type == XML_ATTRIBUTE_NODE) {
|
||||
xmlAttrPtr attr = (xmlAttrPtr) node;
|
||||
CLEANUP_FREE char *content = NULL;
|
||||
unsigned *up;
|
||||
|
||||
if (STREQ ((const char *) attr->name, "sockets")) {
|
||||
up = &cpu->sockets;
|
||||
parse_attr:
|
||||
*up = 0;
|
||||
content = (char *) xmlNodeListGetString (doc, attr->children, 1);
|
||||
if (content)
|
||||
ignore_value (sscanf (content, "%u", up));
|
||||
}
|
||||
else if (STREQ ((const char *) attr->name, "cores")) {
|
||||
up = &cpu->cores;
|
||||
goto parse_attr;
|
||||
}
|
||||
else if (STREQ ((const char *) attr->name, "threads")) {
|
||||
up = &cpu->threads;
|
||||
goto parse_attr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virConnectClose (conn);
|
||||
}
|
||||
|
||||
#else /* !HAVE_LIBVIRT */
|
||||
|
||||
/**
|
||||
* Read the CPU topology from lscpu output.
|
||||
*/
|
||||
static void
|
||||
libvirt_capabilities (struct cpu_config *cpu)
|
||||
get_topology (char **lscpu, struct cpu_config *cpu)
|
||||
{
|
||||
fprintf (stderr,
|
||||
_("%s: program was compiled without libvirt support\n"),
|
||||
getprogname ());
|
||||
const char *v;
|
||||
|
||||
v = get_field (lscpu, "Socket(s)");
|
||||
if (v)
|
||||
ignore_value (sscanf (v, "%u", &cpu->sockets));
|
||||
v = get_field (lscpu, "Core(s) per socket");
|
||||
if (v)
|
||||
ignore_value (sscanf (v, "%u", &cpu->cores));
|
||||
v = get_field (lscpu, "Thread(s) per core");
|
||||
if (v)
|
||||
ignore_value (sscanf (v, "%u", &cpu->threads));
|
||||
}
|
||||
|
||||
#endif /* !HAVE_LIBVIRT */
|
||||
/**
|
||||
* Read some important flags from lscpu output.
|
||||
*/
|
||||
static void
|
||||
get_flags (char **lscpu, struct cpu_config *cpu)
|
||||
{
|
||||
const char *flags;
|
||||
|
||||
flags = get_field (lscpu, "Flags");
|
||||
if (flags) {
|
||||
cpu->apic = strstr (flags, " apic ") != NULL;
|
||||
cpu->pae = strstr (flags, " pae ") != NULL;
|
||||
|
||||
/* aarch64 /proc/cpuinfo has a "Features" field, but lscpu does
|
||||
* not expose it. However aarch64 Features does not contain any
|
||||
* of the interesting flags above.
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Find out if the system uses ACPI.
|
||||
*/
|
||||
static void
|
||||
get_acpi (struct cpu_config *cpu)
|
||||
{
|
||||
cpu->acpi = access ("/sys/firmware/acpi", F_OK) == 0;
|
||||
}
|
||||
|
||||
void
|
||||
get_cpu_config (struct cpu_config *cpu)
|
||||
{
|
||||
CLEANUP_FREE_STRING_LIST char **lscpu = NULL;
|
||||
|
||||
free_cpu_config (cpu);
|
||||
libvirt_capabilities (cpu);
|
||||
cpuinfo_flags (cpu);
|
||||
|
||||
lscpu = get_lscpu ();
|
||||
if (lscpu != NULL) {
|
||||
get_vendor (lscpu, cpu);
|
||||
get_topology (lscpu, cpu);
|
||||
get_flags (lscpu, cpu);
|
||||
}
|
||||
|
||||
get_acpi (cpu);
|
||||
}
|
||||
|
||||
@@ -25,8 +25,6 @@ ifelse(REDHAT,1,
|
||||
libxml2
|
||||
gtk`'GTK_VERSION
|
||||
dbus-libs
|
||||
dnl libvirt is optional, used just to parse the host CPU capabilities.
|
||||
ifdef(`HAVE_LIBVIRT', `libvirt-libs')
|
||||
|
||||
dnl Run as external programs by the p2v binary.
|
||||
/usr/bin/ssh
|
||||
@@ -66,7 +64,6 @@ ifelse(DEBIAN,1,
|
||||
libxml2
|
||||
ifelse(GTK_VERSION,2,libgtk`'GTK_VERSION`'.0-0,libgtk-`'GTK_VERSION`'-0)
|
||||
libdbus-1-3
|
||||
ifdef(`HAVE_LIBVIRT', `libvirt0')
|
||||
openssh-client
|
||||
qemu-utils
|
||||
debianutils
|
||||
@@ -87,7 +84,6 @@ ifelse(ARCHLINUX,1,
|
||||
libxml2
|
||||
gtk`'GTK_VERSION
|
||||
dbus
|
||||
ifdef(`HAVE_LIBVIRT', `libvirt')
|
||||
openssh
|
||||
qemu
|
||||
which
|
||||
@@ -110,7 +106,6 @@ ifelse(SUSE,1,
|
||||
libxml2
|
||||
gtk`'GTK_VERSION
|
||||
libdbus-1-3
|
||||
ifdef(`HAVE_LIBVIRT', `libvirt-libs')
|
||||
qemu-tools
|
||||
openssh
|
||||
dnl /usr/bin/which is in util-linux on SUSE
|
||||
|
||||
Reference in New Issue
Block a user