From 83af63b2bdbd36e9c6c05e3f4dec321f8ef6a937 Mon Sep 17 00:00:00 2001 From: Richard Jones Date: Mon, 15 Mar 2010 11:58:12 +0000 Subject: [PATCH 1/1] Import old 'virt-mem-new' program. --- .gitignore | 8 +- Makefile.am | 2 + configure.ac | 3 + src/Makefile.am | 11 ++ src/dmesg.c | 51 ++++++++ src/internal.h | 164 ++++++++++++++++++++++++ src/main.c | 383 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/output.c | 174 +++++++++++++++++++++++++ 8 files changed, 795 insertions(+), 1 deletion(-) create mode 100644 src/dmesg.c create mode 100644 src/internal.h create mode 100644 src/main.c create mode 100644 src/output.c diff --git a/.gitignore b/.gitignore index 6415d1a..a478abd 100644 --- a/.gitignore +++ b/.gitignore @@ -1,13 +1,19 @@ *~ +*.o +.deps Makefile Makefile.in aclocal.m4 autom4te.cache compile +config.h +config.h.in config.log config.status configure +depcomp install-sh missing src/Makefile -src/Makefile.in \ No newline at end of file +src/Makefile.in +stamp-h1 \ No newline at end of file diff --git a/Makefile.am b/Makefile.am index 9051206..2aca109 100644 --- a/Makefile.am +++ b/Makefile.am @@ -15,4 +15,6 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +ACLOCAL_AMFLAGS = -I m4 + SUBDIRS = src diff --git a/configure.ac b/configure.ac index 2c41680..5dd76ed 100644 --- a/configure.ac +++ b/configure.ac @@ -33,7 +33,10 @@ AM_PROG_CC_C_O dnl Check support for 64 bit file offsets. AC_SYS_LARGEFILE +AC_GNU_SOURCE + # Generate output files. +AC_CONFIG_HEADERS([config.h]) AC_CONFIG_FILES([Makefile src/Makefile]) AC_OUTPUT diff --git a/src/Makefile.am b/src/Makefile.am index 1ee3bc7..74f48dd 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -15,3 +15,14 @@ # along with this program; if not, write to the Free Software # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +bin_PROGRAMS = virt-kernel-info +virt_kernel_info_SOURCES = \ + internal.h \ + main.c \ + output.c + +# Plugins: +virt_kernel_info_SOURCES += \ + dmesg.c + +virt_kernel_info_CFLAGS = -DDATADIR=\"$(datadir)\" diff --git a/src/dmesg.c b/src/dmesg.c new file mode 100644 index 0000000..74f91dd --- /dev/null +++ b/src/dmesg.c @@ -0,0 +1,51 @@ +/* Kernel info for virtual domains. + * (C) Copyright 2008-2010 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include +#include + +#include "internal.h" + +static void init (void) ATTRIBUTE_CONSTRUCTOR; +static void run (/*struct kimage * */ void); + +static struct tool me = { + .name = "dmesg", + .external_cmd = 1, + .run_fn = run, +}; + +static void +init (void) +{ + me.summary = _("show kernel messages from a Linux virtual machine"); + me.description = + _("\ +This tool prints the kernel messages ('dmesg') from a running +Linux virtual machine.\n\ +"); + register_tool (&me); +} + +static void +run (/*struct kimage *kimage*/) +{ + NOT_IMPL; +} diff --git a/src/internal.h b/src/internal.h new file mode 100644 index 0000000..7bc4832 --- /dev/null +++ b/src/internal.h @@ -0,0 +1,164 @@ +/* Kernel info for virtual domains. + * (C) Copyright 2008-2010 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef INTERNAL_H +#define INTERNAL_H + +#include + +#ifdef __GNUC__ + +#ifndef __GNUC_PREREQ +#define __GNUC_PREREQ(maj,min) 0 +#endif + +#define ATTRIBUTE_UNUSED __attribute__((__unused__)) +#define ATTRIBUTE_FORMAT(args...) __attribute__((__format__ (args))) +#define ATTRIBUTE_NORETURN __attribute__((__noreturn__)) +#define ATTRIBUTE_CONSTRUCTOR __attribute__((__constructor__)) + +#if __GNUC_PREREQ (3, 4) +#define ATTRIBUTE_RETURN_CHECK __attribute__((__warn_unused_result__)) +#else +#define ATTRIBUTE_RETURN_CHECK +#endif + +#else /* !__GNUC__ */ +#define ATTRIBUTE_UNUSED +#define ATTRIBUTE_FORMAT(...) +#define ATTRIBUTE_NORETURN +#error "we must be able to define attribute((constructor)) for this compiler" +#define ATTRIBUTE_RETURN_CHECK +#endif /* __GNUC__ */ + +/* Gettext functions. + * + * PACKAGE (usually the string "virt-kernel-info") should be the same as the + * $(DOMAIN) variable declared in po/Makevars. + */ +#define _(str) dgettext(PACKAGE, (str)) +#define N_(str) dgettext(PACKAGE, (str)) + +/* String equality tests, suggested by Jim Meyering. */ +#define STREQ(a,b) (strcmp((a),(b)) == 0) +#define STRCASEEQ(a,b) (strcasecmp((a),(b)) == 0) +#define STRNEQ(a,b) (strcmp((a),(b)) != 0) +#define STRCASENEQ(a,b) (strcasecmp((a),(b)) != 0) +#define STREQLEN(a,b,n) (strncmp((a),(b),(n)) == 0) +#define STRCASEEQLEN(a,b,n) (strncasecmp((a),(b),(n)) == 0) +#define STRNEQLEN(a,b,n) (strncmp((a),(b),(n)) != 0) +#define STRCASENEQLEN(a,b,n) (strncasecmp((a),(b),(n)) != 0) +#define STRPREFIX(a,b) (strncmp((a),(b),strlen((b))) == 0) + +/* Each tool describes itself to the main program by + * defining and registering this structure. + */ +typedef void (*tool_run_fn) (void); /* XXX */ + +struct tool { + const char *name; /* Tool name, eg. "ps" */ + + int external_cmd; /* Is the tool an external command, eg. "virt-ps" */ + + const char *summary; /* Tool-specific one line summary. */ + const char *description; /* Long description. */ + + tool_run_fn run_fn; /* Tool entry point. */ + + /* The main program links tools after they are registered + * through this field. + */ + struct tool *next; +}; + +/* Register a tool with the main program. */ +extern void register_tool (struct tool *me); + +/* Error printing functions: + * + * error() will print an error and exit. Only use this if the error + * really is fatal. NOT usually used inside tools, because they can + * be called repeatedly for different domains. + * + * warning() will print a warning message and continue. Use this + * inside tools (and return), instead of exiting, because the tool + * can be called repeatedly for different domains. + * + * internal_error() is like error() but used for errors that shouldn't + * happen. + * + * INTERNAL_ERROR is also used for errors which shouldn't happen, but + * prints only file and line number. + * + * NOT_IMPL is used to mark functions and code sections which are not + * implemented. + * + * All of these functions append \n after the message. + */ +extern void error (const char *fs, ...) + ATTRIBUTE_FORMAT(printf, 1, 2) ATTRIBUTE_NORETURN; +extern void warning (const char *fs, ...) + ATTRIBUTE_FORMAT(printf, 1, 2); +extern void internal_error (const char *fs, ...) + ATTRIBUTE_FORMAT(printf, 1, 2) ATTRIBUTE_NORETURN; + +#define INTERNAL_ERROR \ + internal_error (_("%s:%d: internal error"), \ + __FILE__, __LINE__) + +#define NOT_IMPL \ + internal_error (_("%s:%d: this feature is not implemented"), \ + __FILE__, __LINE__) + +/* Output functions. These handle CSV output automatically, but + * must be used with care. + * + * output_heading() is used to write the heading (title) row. + * The format string is converted such that any %-sequence which + * _isn't_ %s is turned into %s. (So only string fields are + * expected in headings). + * + * output_row() is used to write each row of data. The format + * string is used directly for non-CSV output, so this is mostly + * equivalent to printf(). However for CSV output, the format + * string is used to tell the types of each field, other characters + * being ignored. + * + * Only fairly simple format strings are understood. + * + * Usual usage is: + * + * const char *fs = "%-9d %s"; + * + * output_heading (fs, _("PID"), _("NAME")); + * for (i = 0; i < nr_procs; ++i) { + * output_row (fs, proc[i].pid, proc[i].name); + * } + * + * A \n is added automatically after each heading/row. + */ +extern void output_heading (const char *fs, ...) + /* not ATTRIBUTE_FORMAT */; +extern void output_row (const char *fs, ...) + ATTRIBUTE_FORMAT(printf, 1, 2); + +/* Global flags. */ +extern int csv; +extern int debug; + +#endif /* INTERNAL_H */ diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..5de205a --- /dev/null +++ b/src/main.c @@ -0,0 +1,383 @@ +/* Kernel info for virtual domains. + * (C) Copyright 2008-2010 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "internal.h" + +/* Global flags. */ +int csv = 0; +int debug = 0; + +/* Linked list of registered tools. */ +static struct tool *tools = NULL; + +/* Currently selected tool (may be NULL). */ +struct tool *tool = NULL; + +/* External DB location. */ +static const char *externaldb = DATADIR "/" PACKAGE_NAME "/kerneldb"; +static int fail_if_no_externaldb = 0; + +/* URI and libvirt connection. */ +static const char *uri = NULL; +static virConnectPtr conn = NULL; + +/* If -t option was passed, this is the filename of the test image. */ +static const char *test_image = NULL; + +/* If --list-kernels was passed. */ +static int list_kernels = 0; + +/* Local functions. */ +static void load_externaldb (const char *, int); +static void do_list_kernels (void) ATTRIBUTE_NORETURN; +static void usage (void) ATTRIBUTE_NORETURN; + +static virDomainPtr *get_named_domains (char * const*, int); +static virDomainPtr *get_all_domains (void); + +int +main (int argc, char *argv[]) +{ + int c; + + /* argv[0] can be the name of the tool, or if not recognized then + * the name of the tool must be the first anonymous argument. + */ + if (argv[0]) { + const char *prog = strrchr (argv[0], '/'); + + if (!prog) + prog = argv[0]; + + if (STRCASEEQLEN (prog+1, "virt-", 5)) { + struct tool *t; + + prog += 6; + + for (t = tools; t != NULL; t = t->next) { + if (STRCASEEQ (prog, t->name)) { + tool = t; + break; + } + } + } + } + + /* Parse command line parameters. */ + while (1) { + static const char *shortopts = "A:E:T:W:c:dt:x:?"; + static struct option longopts[] = { + { "arch", required_argument, 0, 'A' }, + { "endian", required_argument, 0, 'E' }, + { "kernel-min", required_argument, 0, 0 }, + { "kernel-max", required_argument, 0, 0 }, + { "text", required_argument, 0, 'T' }, + { "wordsize", required_argument, 0, 'W' }, + { "connect", required_argument, 0, 'c' }, + { "csv", no_argument, 0, 0 }, + { "debug", no_argument, 0, 'd' }, + { "help", no_argument, 0, '?' }, + { "list-kernels", no_argument, 0, 0 }, + { "image", required_argument, 0, 't' }, + { "externaldb", required_argument, 0, 'x' }, + { "version", no_argument, 0, 0 } + }; + int option_index = 0; + + c = getopt_long (argc, argv, shortopts, longopts, &option_index); + if (c == -1) break; + + switch (c) { + case 0: { /* longopt without short equivalent */ + const char *longopt_name = longopts[option_index].name; + + if (STRCASEEQ (longopt_name, "csv")) /* csv output */ + csv = 1; + /* list kernels and exit */ + else if (STRCASEEQ (longopt_name, "list-kernels")) + list_kernels = 1; + /* set kernel-min */ + else if (STRCASEEQ (longopt_name, "kernel-min")) + NOT_IMPL; + /* set kernel-max */ + else if (STRCASEEQ (longopt_name, "kernel-max")) + NOT_IMPL; + /* display version and exit */ + else if (STRCASEEQ (longopt_name, "version")) { + printf ("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION); + exit (0); + } + else + INTERNAL_ERROR; /* this shouldn't happen */ + + break; + } + + case 'A': /* set architecture */ + NOT_IMPL; + case 'E': /* set endianness */ + NOT_IMPL; + case 'T': /* set text address */ + NOT_IMPL; + case 'W': /* set wordsize */ + NOT_IMPL; + + case 'c': /* connect to URI */ + uri = optarg; + break; + + case 'd': /* enable debugging */ + debug = 1; + break; + + case 't': /* load kernel image */ + test_image = optarg; + break; + + case 'x': /* location of external database */ + externaldb = optarg; + fail_if_no_externaldb = 1; + break; + + case '?': /* print help */ + usage (); + + default: + INTERNAL_ERROR; /* this shouldn't happen */ + } + } /* while */ + + /* Load the external database / kernel updates, if necessary. */ + load_externaldb (externaldb, fail_if_no_externaldb); + + /* If asked, list kernels and exit. */ + if (list_kernels) + do_list_kernels (); + + /* If we haven't got a tool name yet, then the first anon parameter + * must be a tool name. + */ + if (!tool) { + if (optind < argc) { + const char *p; + struct tool *t; + + p = argv[optind++]; + + for (t = tools; t != NULL; t = t->next) { + if (STRCASEEQ (p, t->name)) { + tool = t; + goto found_tool; + } + } + + error (_("'%s' is not a recognized virt-kernel-info tool.\n\nMake sure you specify the virt-kernel-info tool as the first argument on the command line. To get a list of recognized tools, use 'virt-kernel-info --help'."), p); + } else + error (_("Could not work out which virt-kernel-info tool you are trying to use.\n\nMake sure you specify the virt-kernel-info tool as the first argument on the command line. To get a list of recognized tools, use 'virt-kernel-info --help'.")); + + found_tool: ; + } + + /* We should have worked out which tool it is by now. */ + assert (tool != NULL); + + /* If -t was passed, then we load that forensic image, else we have + * to connect to libvirt. + */ + if (test_image) { + if (optind < argc) + error (_("If '-t' is passed then there shouldn't be any additional command line arguments.")); + + NOT_IMPL; + } + else { + virDomainPtr *doms; + + /* Connect to libvirt. */ + conn = virConnectOpenReadOnly (uri); + if (!conn) + error (_("failed to connect to hypervisor '%s'.\nWhen connecting to some hypervisors you may need to be running as root.\nThere may be a more detailed error message above this, but if not then we didn't print one because libvirt's virterror mechanism is next to useless."), + uri != NULL ? uri : _("default")); + + /* Extra parameters are a list of domain names, IDs or UUIDs. */ + if (optind < argc) + doms = get_named_domains (&argv[optind], argc - optind); + else + doms = get_all_domains (); + + /* Act on each domain. */ + NOT_IMPL; + } + + exit (0); +} + +/* Usage. */ +static void usage (void) +{ + struct tool *t; + + printf (_("\ +virt-kernel-info: Tools for providing information about virtual machines\n\ +\n\ +General usage is:\n\ + [-options]\n\ + [-options] [domain-name|ID|UUID ...]\n\ + [-options] -t \n\ +where is 'virt-ps', 'virt-dmesg' etc. (full list below) or a\n\ +subtool such as 'virt-kernel-info ps', 'virt-kernel-info capture' etc.\n\ +\n\ +General options:\n\ + --connect | -c Connect to URI (default: autodetect)\n\ + --csv Output in CSV format for spreadsheets etc.\n\ + --debug Print extra debugging information\n\ + --list-kernels List known kernels, then exit\n\ + --image | -t Examine saved image (see: virt-kernel-info capture)\n\ + --externaldb | -x Load/override external kernels database\n\ + --version Print program name and version, then exit\n\ +\n\ +These options may be used to override automatic detection of guest\n\ +architecture, endianness, kernel location, etc.:\n\ + -A | --arch auto | | ...\n\ + -E | --endian auto | le | be\n\ + --kernel-min auto | | ... | 0x\n\ + --kernel-max auto | | ... | 0x\n\ + -T | --text auto | | ... | 0x\n\ + -W | --wordsize auto | 32 | 64\n\ +where is the name of an architecture such as i386, x86-64, etc.\n\ +\n\ +List of tools:\n\ +")); + + for (t = tools; t != NULL; t = t->next) { + printf ("\n"); + + if (t->external_cmd) + printf (" virt-%-12s - %s\n", t->name, t->summary); + else + printf (" virt-kernel-info %8s - %s\n", t->name, t->summary); + + printf ("%s", t->description); + if (t->next) printf ("\n"); + } + + exit (0); +} + +static void +load_externaldb (const char *externaldb, int fail_if_no_externaldb) +{ + + + + + + + + + NOT_IMPL; +} + +static void +do_list_kernels (void) +{ + NOT_IMPL; +} + +static virDomainPtr * +get_named_domains (char * const *domains, int nr_domains) +{ + NOT_IMPL; +} + +static virDomainPtr * +get_all_domains (void) +{ + NOT_IMPL; +} + +/* Tools register themselves by calling this function. Note that the + * function is called from constructors. In particular it is called + * before main(). Also can be called in unspecified order. + */ +void +register_tool (struct tool *tool) +{ + struct tool *t, **p; + + /* Insertion sort. */ + p = &tools; + for (t = tools; t != NULL; t = t->next) { + if (strcmp (t->name, tool->name) < 0) + goto insert; + p = &t->next; + } + insert: + tool->next = *p; + *p = tool; +} + +/* Warning, error message functions. See internal.h for usage. */ +static void +message (const char *pre, const char *fs, va_list args) +{ + fprintf (stderr, "%s-%s: %s: ", PACKAGE_NAME, PACKAGE_VERSION, pre); + vfprintf (stderr, fs, args); + fprintf (stderr, "\n"); +} + +void +error (const char *fs, ...) +{ + va_list args; + va_start (args, fs); + message (_("error"), fs, args); + va_end (args); + exit (1); +} + +void +warning (const char *fs, ...) +{ + va_list args; + va_start (args, fs); + message (_("warning"), fs, args); + va_end (args); +} + +void +internal_error (const char *fs, ...) +{ + va_list args; + va_start (args, fs); + message (_("internal error"), fs, args); + va_end (args); + abort (); +} diff --git a/src/output.c b/src/output.c new file mode 100644 index 0000000..0c15472 --- /dev/null +++ b/src/output.c @@ -0,0 +1,174 @@ +/* Kernel info for virtual domains. + * (C) Copyright 2008-2010 Red Hat Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Generic output routines which can write either plain + * text tables or CSV files. + */ + +#include + +#include +#include +#include +#include + +#include "internal.h" + +static char *convert_fs_to_strings (const char *fs); +static void output_to_csv (int to_strings, const char *fs, va_list args); + +void +output_heading (const char *fs, ...) +{ + va_list args; + + va_start (args, fs); + + if (!csv) { + /* Convert each %..x in the format string into %..s */ + char *fs2 = convert_fs_to_strings (fs); + vprintf (fs2, args); + free (fs2); + printf ("\n"); + } else + output_to_csv (1, fs, args); + + va_end (args); +} + +void +output_row (const char *fs, ...) +{ + va_list args; + + va_start (args, fs); + + if (!csv) { + vprintf (fs, args); + printf ("\n"); + } + else + output_to_csv (0, fs, args); + + va_end (args); +} + +/* Return a newly allocated format string with each '%..x' sequence + * converted to '%..s'. We only attempt to parse simple format + * strings, and we assume the length of the format string won't + * change. + */ +static const char *end_of_specifier = "diouxXeEfFgGaAcsp%"; + +static char * +convert_fs_to_strings (const char *fs) +{ + char *fs2; + int i, len; + + len = strlen (fs); + fs2 = strdup (fs); + for (i = 0; i < len; ++i) { + if (fs2[i] == '%') { + /* Search for the end of the %-sequence. */ + do { i++; } + while (i < len && strchr (end_of_specifier, fs2[i]) == NULL); + + if (i == len) + internal_error (_("unrecognized format string in output_heading function")); + + fs2[i] = 's'; + } + } + + return fs2; /* caller frees */ +} + +/* Output a row to a CSV file. The format string is just used to + * get the correct types for each parameter - any non-%-specifiers + * in the format string are ignored. If to_strings is true, then + * each parameter is assumed to be a string. + * + * The CSV output is very conservative, designed to be correct + * rather than concise. + */ +static void +output_to_csv (int to_strings, const char *fs, va_list args) +{ + int i, j, len, comma = 0; + char fs2[16]; + + len = strlen (fs); + + for (i = 0; i < len; ++i) { + /* Look for the next %-specifier. */ + if (fs[i] == '%') { + j = 0; + do { fs2[j++] = fs[i++]; } + while (j < sizeof (fs2) - 1 && + i < len && + strchr (end_of_specifier, fs[i]) == NULL); + + if (j == sizeof (fs2) - 1 || i == len) + internal_error (_("unrecognized format string in output_* function")); + + fs2[j] = fs[i++]; + fs2[j+1] = '\0'; + } + + if (fs2[j] != '%') { + char *str; + int len2; + + if (to_strings) + fs2[j] = 's'; + + /* Convert the next parameter into a string. */ + if (vasprintf (&str, fs2, args) == -1) + internal_error (_("unable to convert next argument to string using '%s'"), + fs2); + len2 = strlen (str); + + /* Output the next parameter as a CSV field. */ + if (comma) + putchar (','); + comma = 1; + + putchar ('"'); + for (j = 0; j < len2; ++j) { + switch (str[j]) { + case '"': + putchar ('"'); + putchar ('"'); + break; + case '\0': + putchar ('"'); + putchar ('0'); + break; + default: + putchar (str[j]); + } + } + putchar ('"'); + + free (str); + } + } + + putchar ('\n'); +} -- 1.8.3.1