/* 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 #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 (_("\ \n\ virt-kernel-info: Tools for providing information about virtual machines\n\ \n\ General usage is:\n\ \n\ [-options]\n\ [-options] [domain-name|ID|UUID ...]\n\ [-options] -t \n\ \n\ where is 'virt-dmesg' etc. (full list below) or a subtool\n\ such as 'virt-kernel-info dmesg', 'virt-kernel-info capture' etc.\n\ \n\ General options:\n\ \n\ -c | --connect \n\ 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\ -t | --image Examine saved image (see: virt-kernel-info capture)\n\ --version Print program name and version, then exit\n\ -x | --externaldb \n\ Load/override external kernels database\n\ \n\ These options may be used to override automatic detection of guest\n\ architecture, endianness, kernel location, etc. The default for all\n\ of them is 'auto':\n\ \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 void discard_virterror (void *user_data, virErrorPtr err) { /* nothing */ } /* Get all running domains, named by domname/ID/UUID. */ static virDomainPtr * get_named_domains (char * const *domains, int n) { assert (conn != NULL); virDomainPtr *doms = malloc (sizeof (virDomainPtr) * (n+1)); if (doms == NULL) { perror ("malloc"); exit (1); } /* Discard 'domain not found' errors from libvirt while we * run this loop. */ virConnSetErrorFunc (conn, NULL, discard_virterror); int i; for (i = 0; i < n; ++i) { /* Try in the order: UUID, name, ID. */ doms[i] = virDomainLookupByUUIDString (conn, domains[i]); if (!doms[i]) { doms[i] = virDomainLookupByName (conn, domains[i]); if (!doms[i] && domains[i][0] >= '0' && domains[i][0] <= '9') { errno = 0; char *endptr; int id = strtol (domains[i], &endptr, 10); if ((errno == ERANGE && (id == LONG_MAX || id == LONG_MIN)) || (errno != 0 && id == 0)) { perror ("strtol"); exit (EXIT_FAILURE); } if (endptr == domains[i]) error (_("no digits found while trying to parse ID '%s'"), domains[i]); if (*endptr) error (_("parse error, extra digits after ID '%s'"), domains[i]); doms[i] = virDomainLookupByID (conn, id); } } if (!doms[i]) error (_("%s: libvirt guest not found (use name, UUID or ID here)"), domains[i]); if (!virDomainIsActive (doms[i])) error (_("%s: this domain is inactive. This program only works on running guests."), virDomainGetName (doms[i])); } /* Restore default virterror behaviour. */ virConnSetErrorFunc (conn, NULL, NULL); return doms; } /* Get all (running) domains. */ static virDomainPtr * get_all_domains (void) { assert (conn != NULL); int n; n = virConnectNumOfDomains (conn); if (n == -1) error (_("failed to get number of domains")); int ids[n]; n = virConnectListDomains (conn, ids, n); if (n == -1) error (_("failed to list domains")); int i, j; virDomainPtr *doms = malloc (sizeof (virDomainPtr) * (n+1)); if (doms == NULL) { perror ("malloc"); exit (1); } for (i = 0; i < n; ++i) { /* Ignore failures, but don't increment 'j' if that happens. */ doms[j] = virDomainLookupByID (conn, ids[i]); if (doms[j]) j++; } doms[j] = NULL; return doms; } /* 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: ", PACKAGE_NAME, 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 (); }