Implement libvirt calls.
[virt-kernel-info.git] / src / main.c
1 /* Kernel info for virtual domains.
2  * (C) Copyright 2008-2010 Red Hat Inc.
3  *
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.
8  *
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.
13  *
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.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <stdarg.h>
24 #include <string.h>
25 #include <assert.h>
26 #include <getopt.h>
27 #include <errno.h>
28 #include <limits.h>
29
30 #include <libvirt/libvirt.h>
31 #include <libvirt/virterror.h>
32
33 #include "internal.h"
34
35 /* Global flags. */
36 int csv = 0;
37 int debug = 0;
38
39 /* Linked list of registered tools. */
40 static struct tool *tools = NULL;
41
42 /* Currently selected tool (may be NULL). */
43 struct tool *tool = NULL;
44
45 /* External DB location. */
46 static const char *externaldb = DATADIR "/" PACKAGE_NAME "/kerneldb";
47 static int fail_if_no_externaldb = 0;
48
49 /* URI and libvirt connection. */
50 static const char *uri = NULL;
51 static virConnectPtr conn = NULL;
52
53 /* If -t option was passed, this is the filename of the test image. */
54 static const char *test_image = NULL;
55
56 /* If --list-kernels was passed. */
57 static int list_kernels = 0;
58
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;
63
64 static virDomainPtr *get_named_domains (char * const*, int);
65 static virDomainPtr *get_all_domains (void);
66
67 int
68 main (int argc, char *argv[])
69 {
70   int c;
71
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.
74    */
75   if (argv[0]) {
76     const char *prog = strrchr (argv[0], '/');
77
78     if (!prog)
79       prog = argv[0];
80
81     if (STRCASEEQLEN (prog+1, "virt-", 5)) {
82       struct tool *t;
83
84       prog += 6;
85
86       for (t = tools; t != NULL; t = t->next) {
87         if (STRCASEEQ (prog, t->name)) {
88           tool = t;
89           break;
90         }
91       }
92     }
93   }
94
95   /* Parse command line parameters. */
96   while (1) {
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 }
113     };
114     int option_index = 0;
115
116     c = getopt_long (argc, argv, shortopts, longopts, &option_index);
117     if (c == -1) break;
118
119     switch (c) {
120     case 0: {                   /* longopt without short equivalent */
121       const char *longopt_name = longopts[option_index].name;
122
123       if (STRCASEEQ (longopt_name, "csv")) /* csv output */
124         csv = 1;
125                                 /* list kernels and exit */
126       else if (STRCASEEQ (longopt_name, "list-kernels"))
127         list_kernels = 1;
128                                 /* set kernel-min */
129       else if (STRCASEEQ (longopt_name, "kernel-min"))
130         NOT_IMPL;
131                                 /* set kernel-max */
132       else if (STRCASEEQ (longopt_name, "kernel-max"))
133         NOT_IMPL;
134                                 /* display version and exit */
135       else if (STRCASEEQ (longopt_name, "version")) {
136         printf ("%s %s\n", PACKAGE_NAME, PACKAGE_VERSION);
137         exit (0);
138       }
139       else
140         INTERNAL_ERROR;         /* this shouldn't happen */
141
142       break;
143     }
144
145     case 'A':                   /* set architecture */
146       NOT_IMPL;
147     case 'E':                   /* set endianness */
148       NOT_IMPL;
149     case 'T':                   /* set text address */
150       NOT_IMPL;
151     case 'W':                   /* set wordsize */
152       NOT_IMPL;
153
154     case 'c':                   /* connect to URI */
155       uri = optarg;
156       break;
157
158     case 'd':                   /* enable debugging */
159       debug = 1;
160       break;
161
162     case 't':                   /* load kernel image */
163       test_image = optarg;
164       break;
165
166     case 'x':                   /* location of external database */
167       externaldb = optarg;
168       fail_if_no_externaldb = 1;
169       break;
170
171     case '?':                   /* print help */
172       usage ();
173
174     default:
175       INTERNAL_ERROR;           /* this shouldn't happen */
176     }
177   } /* while */
178
179   /* Load the external database / kernel updates, if necessary. */
180   load_externaldb (externaldb, fail_if_no_externaldb);
181
182   /* If asked, list kernels and exit. */
183   if (list_kernels)
184     do_list_kernels ();
185
186   /* If we haven't got a tool name yet, then the first anon parameter
187    * must be a tool name.
188    */
189   if (!tool) {
190     if (optind < argc) {
191       const char *p;
192       struct tool *t;
193
194       p = argv[optind++];
195
196       for (t = tools; t != NULL; t = t->next) {
197         if (STRCASEEQ (p, t->name)) {
198           tool = t;
199           goto found_tool;
200         }
201       }
202
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);
204     } else
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'."));
206
207   found_tool: ;
208   }
209
210   /* We should have worked out which tool it is by now. */
211   assert (tool != NULL);
212
213   /* If -t was passed, then we load that forensic image, else we have
214    * to connect to libvirt.
215    */
216   if (test_image) {
217     if (optind < argc)
218       error (_("If '-t' is passed then there shouldn't be any additional command line arguments."));
219
220     NOT_IMPL;
221   }
222   else {
223     virDomainPtr *doms;
224
225     /* Connect to libvirt. */
226     conn = virConnectOpenReadOnly (uri);
227     if (!conn)
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"));
230
231     /* Extra parameters are a list of domain names, IDs or UUIDs. */
232     if (optind < argc)
233       doms = get_named_domains (&argv[optind], argc - optind);
234     else
235       doms = get_all_domains ();
236
237     /* Act on each domain. */
238     NOT_IMPL;
239   }
240
241   exit (0);
242 }
243
244 /* Usage. */
245 static void usage (void)
246 {
247   struct tool *t;
248
249   printf (_("\
250 \n\
251 virt-kernel-info: Tools for providing information about virtual machines\n\
252 \n\
253 General usage is:\n\
254 \n\
255   <tool> [-options]\n\
256   <tool> [-options] [domain-name|ID|UUID ...]\n\
257   <tool> [-options] -t <image>\n\
258 \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\
261 \n\
262 General options:\n\
263 \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\
273 \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\
277 \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\
285 \n\
286 List of tools:\n\
287 "));
288
289   for (t = tools; t != NULL; t = t->next) {
290     printf ("\n");
291
292     if (t->external_cmd)
293       printf ("  virt-%-12s - %s\n", t->name, t->summary);
294     else
295       printf ("  virt-kernel-info %8s - %s\n", t->name, t->summary);
296
297     printf ("%s", t->description);
298     if (t->next) printf ("\n");
299   }
300
301   exit (0);
302 }
303
304 static void
305 load_externaldb (const char *externaldb, int fail_if_no_externaldb)
306 {
307   //NOT_IMPL;
308 }
309
310 static void
311 do_list_kernels (void)
312 {
313   NOT_IMPL;
314 }
315
316 static void
317 discard_virterror (void *user_data, virErrorPtr err)
318 {
319   /* nothing */
320 }
321
322 /* Get all running domains, named by domname/ID/UUID. */
323 static virDomainPtr *
324 get_named_domains (char * const *domains, int n)
325 {
326   assert (conn != NULL);
327
328   virDomainPtr *doms = malloc (sizeof (virDomainPtr) * (n+1));
329   if (doms == NULL) {
330     perror ("malloc");
331     exit (1);
332   }
333
334   /* Discard 'domain not found' errors from libvirt while we
335    * run this loop.
336    */
337   virConnSetErrorFunc (conn, NULL, discard_virterror);
338
339   int i;
340   for (i = 0; i < n; ++i) {
341     /* Try in the order: UUID, name, ID. */
342     doms[i] = virDomainLookupByUUIDString (conn, domains[i]);
343     if (!doms[i]) {
344       doms[i] = virDomainLookupByName (conn, domains[i]);
345       if (!doms[i] && domains[i][0] >= '0' && domains[i][0] <= '9') {
346         errno = 0;
347         char *endptr;
348         int id = strtol (domains[i], &endptr, 10);
349         if ((errno == ERANGE && (id == LONG_MAX || id == LONG_MIN)) ||
350             (errno != 0 && id == 0)) {
351           perror ("strtol");
352           exit (EXIT_FAILURE);
353         }
354         if (endptr == domains[i])
355           error (_("no digits found while trying to parse ID '%s'"),
356                  domains[i]);
357         if (*endptr)
358           error (_("parse error, extra digits after ID '%s'"),
359                  domains[i]);
360         doms[i] = virDomainLookupByID (conn, id);
361       }
362     }
363
364     if (!doms[i])
365       error (_("%s: libvirt guest not found (use name, UUID or ID here)"),
366              domains[i]);
367
368     if (!virDomainIsActive (doms[i]))
369       error (_("%s: this domain is inactive.  This program only works on running guests."),
370              virDomainGetName (doms[i]));
371   }
372
373   /* Restore default virterror behaviour. */
374   virConnSetErrorFunc (conn, NULL, NULL);
375
376   return doms;
377 }
378
379 /* Get all (running) domains. */
380 static virDomainPtr *
381 get_all_domains (void)
382 {
383   assert (conn != NULL);
384
385   int n;
386   n = virConnectNumOfDomains (conn);
387   if (n == -1)
388     error (_("failed to get number of domains"));
389
390   int ids[n];
391   n = virConnectListDomains (conn, ids, n);
392   if (n == -1)
393     error (_("failed to list domains"));
394
395   int i, j;
396   virDomainPtr *doms = malloc (sizeof (virDomainPtr) * (n+1));
397   if (doms == NULL) {
398     perror ("malloc");
399     exit (1);
400   }
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]);
404     if (doms[j]) j++;
405   }
406
407   doms[j] = NULL;
408   return doms;
409 }
410
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.
414  */
415 void
416 register_tool (struct tool *tool)
417 {
418   struct tool *t, **p;
419
420   /* Insertion sort. */
421   p = &tools;
422   for (t = tools; t != NULL; t = t->next) {
423     if (strcmp (t->name, tool->name) < 0)
424       goto insert;
425     p = &t->next;
426   }
427  insert:
428   tool->next = *p;
429   *p = tool;
430 }
431
432 /* Warning, error message functions.  See internal.h for usage. */
433 static void
434 message (const char *pre, const char *fs, va_list args)
435 {
436   fprintf (stderr, "%s: %s: ", PACKAGE_NAME, pre);
437   vfprintf (stderr, fs, args);
438   fprintf (stderr, "\n");
439 }
440
441 void
442 error (const char *fs, ...)
443 {
444   va_list args;
445   va_start (args, fs);
446   message (_("error"), fs, args);
447   va_end (args);
448   exit (1);
449 }
450
451 void
452 warning (const char *fs, ...)
453 {
454   va_list args;
455   va_start (args, fs);
456   message (_("warning"), fs, args);
457   va_end (args);
458 }
459
460 void
461 internal_error (const char *fs, ...)
462 {
463   va_list args;
464   va_start (args, fs);
465   message (_("internal error"), fs, args);
466   va_end (args);
467   abort ();
468 }