1 /* Kernel info for virtual domains.
2 * (C) Copyright 2008-2010 Red Hat Inc.
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
30 #include <libvirt/libvirt.h>
31 #include <libvirt/virterror.h>
39 /* Linked list of registered tools. */
40 static struct tool *tools = NULL;
42 /* Currently selected tool (may be NULL). */
43 struct tool *tool = NULL;
45 /* External DB location. */
46 static const char *externaldb = DATADIR "/" PACKAGE_NAME "/kerneldb";
47 static int fail_if_no_externaldb = 0;
49 /* URI and libvirt connection. */
50 static const char *uri = NULL;
51 static virConnectPtr conn = NULL;
53 /* If -t option was passed, this is the filename of the test image. */
54 static const char *test_image = NULL;
56 /* If --list-kernels was passed. */
57 static int list_kernels = 0;
59 /* Local functions. */
60 static void load_externaldb (const char *, int);
61 static void do_list_kernels (void) ATTRIBUTE_NORETURN;
62 static void usage (void) ATTRIBUTE_NORETURN;
64 static virDomainPtr *get_named_domains (char * const*, int);
65 static virDomainPtr *get_all_domains (void);
68 main (int argc, char *argv[])
72 /* argv[0] can be the name of the tool, or if not recognized then
73 * the name of the tool must be the first anonymous argument.
76 const char *prog = strrchr (argv[0], '/');
81 if (STRCASEEQLEN (prog+1, "virt-", 5)) {
86 for (t = tools; t != NULL; t = t->next) {
87 if (STRCASEEQ (prog, t->name)) {
95 /* Parse command line parameters. */
97 static const char *shortopts = "A:E:T:W:c:dt:x:?";
98 static struct option longopts[] = {
99 { "arch", required_argument, 0, 'A' },
100 { "endian", required_argument, 0, 'E' },
101 { "kernel-min", required_argument, 0, 0 },
102 { "kernel-max", required_argument, 0, 0 },
103 { "text", required_argument, 0, 'T' },
104 { "wordsize", required_argument, 0, 'W' },
105 { "connect", required_argument, 0, 'c' },
106 { "csv", no_argument, 0, 0 },
107 { "debug", no_argument, 0, 'd' },
108 { "help", no_argument, 0, '?' },
109 { "list-kernels", no_argument, 0, 0 },
110 { "image", required_argument, 0, 't' },
111 { "externaldb", required_argument, 0, 'x' },
112 { "version", no_argument, 0, 0 }
114 int option_index = 0;
116 c = getopt_long (argc, argv, shortopts, longopts, &option_index);
120 case 0: { /* longopt without short equivalent */
121 const char *longopt_name = longopts[option_index].name;
123 if (STRCASEEQ (longopt_name, "csv")) /* csv output */
125 /* list kernels and exit */
126 else if (STRCASEEQ (longopt_name, "list-kernels"))
129 else if (STRCASEEQ (longopt_name, "kernel-min"))
132 else if (STRCASEEQ (longopt_name, "kernel-max"))
134 /* display version and exit */
135 else if (STRCASEEQ (longopt_name, "version")) {
136 printf ("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
140 INTERNAL_ERROR; /* this shouldn't happen */
145 case 'A': /* set architecture */
147 case 'E': /* set endianness */
149 case 'T': /* set text address */
151 case 'W': /* set wordsize */
154 case 'c': /* connect to URI */
158 case 'd': /* enable debugging */
162 case 't': /* load kernel image */
166 case 'x': /* location of external database */
168 fail_if_no_externaldb = 1;
171 case '?': /* print help */
175 INTERNAL_ERROR; /* this shouldn't happen */
179 /* Load the external database / kernel updates, if necessary. */
180 load_externaldb (externaldb, fail_if_no_externaldb);
182 /* If asked, list kernels and exit. */
186 /* If we haven't got a tool name yet, then the first anon parameter
187 * must be a tool name.
196 for (t = tools; t != NULL; t = t->next) {
197 if (STRCASEEQ (p, t->name)) {
203 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);
205 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'."));
210 /* We should have worked out which tool it is by now. */
211 assert (tool != NULL);
213 /* If -t was passed, then we load that forensic image, else we have
214 * to connect to libvirt.
218 error (_("If '-t' is passed then there shouldn't be any additional command line arguments."));
225 /* Connect to libvirt. */
226 conn = virConnectOpenReadOnly (uri);
228 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."),
229 uri != NULL ? uri : _("default"));
231 /* Extra parameters are a list of domain names, IDs or UUIDs. */
233 doms = get_named_domains (&argv[optind], argc - optind);
235 doms = get_all_domains ();
237 /* Act on each domain. */
245 static void usage (void)
251 virt-kernel-info: Tools for providing information about virtual machines\n\
256 <tool> [-options] [domain-name|ID|UUID ...]\n\
257 <tool> [-options] -t <image>\n\
259 where <tool> is 'virt-dmesg' etc. (full list below) or a subtool\n\
260 such as 'virt-kernel-info dmesg', 'virt-kernel-info capture' etc.\n\
264 -c | --connect <libvirt-uri>\n\
265 Connect to URI (default: autodetect)\n\
266 --csv Output in CSV format for spreadsheets etc.\n\
267 --debug Print extra debugging information\n\
268 --list-kernels List known kernels, then exit\n\
269 -t | --image <image> Examine saved image (see: virt-kernel-info capture)\n\
270 --version Print program name and version, then exit\n\
271 -x | --externaldb <externaldb>\n\
272 Load/override external kernels database\n\
274 These options may be used to override automatic detection of guest\n\
275 architecture, endianness, kernel location, etc. The default for all\n\
276 of them is 'auto':\n\
278 -A | --arch auto | <arch> | ...\n\
279 -E | --endian auto | le | be\n\
280 --kernel-min auto | <arch> | ... | 0x<addr>\n\
281 --kernel-max auto | <arch> | ... | 0x<addr>\n\
282 -T | --text auto | <arch> | ... | 0x<addr>\n\
283 -W | --wordsize auto | 32 | 64\n\
284 where <arch> is the name of an architecture such as i386, x86-64, etc.\n\
289 for (t = tools; t != NULL; t = t->next) {
293 printf (" virt-%-12s - %s\n", t->name, t->summary);
295 printf (" virt-kernel-info %8s - %s\n", t->name, t->summary);
297 printf ("%s", t->description);
298 if (t->next) printf ("\n");
305 load_externaldb (const char *externaldb, int fail_if_no_externaldb)
311 do_list_kernels (void)
317 discard_virterror (void *user_data, virErrorPtr err)
322 /* Get all running domains, named by domname/ID/UUID. */
323 static virDomainPtr *
324 get_named_domains (char * const *domains, int n)
326 assert (conn != NULL);
328 virDomainPtr *doms = malloc (sizeof (virDomainPtr) * (n+1));
334 /* Discard 'domain not found' errors from libvirt while we
337 virConnSetErrorFunc (conn, NULL, discard_virterror);
340 for (i = 0; i < n; ++i) {
341 /* Try in the order: UUID, name, ID. */
342 doms[i] = virDomainLookupByUUIDString (conn, domains[i]);
344 doms[i] = virDomainLookupByName (conn, domains[i]);
345 if (!doms[i] && domains[i][0] >= '0' && domains[i][0] <= '9') {
348 int id = strtol (domains[i], &endptr, 10);
349 if ((errno == ERANGE && (id == LONG_MAX || id == LONG_MIN)) ||
350 (errno != 0 && id == 0)) {
354 if (endptr == domains[i])
355 error (_("no digits found while trying to parse ID '%s'"),
358 error (_("parse error, extra digits after ID '%s'"),
360 doms[i] = virDomainLookupByID (conn, id);
365 error (_("%s: libvirt guest not found (use name, UUID or ID here)"),
368 if (!virDomainIsActive (doms[i]))
369 error (_("%s: this domain is inactive. This program only works on running guests."),
370 virDomainGetName (doms[i]));
373 /* Restore default virterror behaviour. */
374 virConnSetErrorFunc (conn, NULL, NULL);
379 /* Get all (running) domains. */
380 static virDomainPtr *
381 get_all_domains (void)
383 assert (conn != NULL);
386 n = virConnectNumOfDomains (conn);
388 error (_("failed to get number of domains"));
391 n = virConnectListDomains (conn, ids, n);
393 error (_("failed to list domains"));
396 virDomainPtr *doms = malloc (sizeof (virDomainPtr) * (n+1));
401 for (i = 0; i < n; ++i) {
402 /* Ignore failures, but don't increment 'j' if that happens. */
403 doms[j] = virDomainLookupByID (conn, ids[i]);
411 /* Tools register themselves by calling this function. Note that the
412 * function is called from constructors. In particular it is called
413 * before main(). Also can be called in unspecified order.
416 register_tool (struct tool *tool)
420 /* Insertion sort. */
422 for (t = tools; t != NULL; t = t->next) {
423 if (strcmp (t->name, tool->name) < 0)
432 /* Warning, error message functions. See internal.h for usage. */
434 message (const char *pre, const char *fs, va_list args)
436 fprintf (stderr, "%s: %s: ", PACKAGE_NAME, pre);
437 vfprintf (stderr, fs, args);
438 fprintf (stderr, "\n");
442 error (const char *fs, ...)
446 message (_("error"), fs, args);
452 warning (const char *fs, ...)
456 message (_("warning"), fs, args);
461 internal_error (const char *fs, ...)
465 message (_("internal error"), fs, args);