virt-ls: Refactor mode selection code.
[libguestfs.git] / cat / virt-ls.c
1 /* virt-ls
2  * Copyright (C) 2010-2011 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 <inttypes.h>
24 #include <unistd.h>
25 #include <getopt.h>
26 #include <fcntl.h>
27 #include <locale.h>
28 #include <assert.h>
29 #include <string.h>
30 #include <libintl.h>
31
32 #include "progname.h"
33
34 #include "guestfs.h"
35 #include "options.h"
36
37 /* Currently open libguestfs handle. */
38 guestfs_h *g;
39
40 int read_only = 1;
41 int live = 0;
42 int verbose = 0;
43 int keys_from_stdin = 0;
44 int echo_keys = 0;
45 const char *libvirt_uri = NULL;
46 int inspector = 1;
47
48 static int do_ls (const char *dir);
49 static int do_ls_l (const char *dir);
50 static int do_ls_R (const char *dir);
51
52 static inline char *
53 bad_cast (char const *s)
54 {
55   return (char *) s;
56 }
57
58 static void __attribute__((noreturn))
59 usage (int status)
60 {
61   if (status != EXIT_SUCCESS)
62     fprintf (stderr, _("Try `%s --help' for more information.\n"),
63              program_name);
64   else {
65     fprintf (stdout,
66            _("%s: list files in a virtual machine\n"
67              "Copyright (C) 2010-2011 Red Hat Inc.\n"
68              "Usage:\n"
69              "  %s [--options] -d domname dir [dir ...]\n"
70              "  %s [--options] -a disk.img [-a disk.img ...] dir [dir ...]\n"
71              "Options:\n"
72              "  -a|--add image       Add image\n"
73              "  -c|--connect uri     Specify libvirt URI for -d option\n"
74              "  -d|--domain guest    Add disks from libvirt guest\n"
75              "  --echo-keys          Don't turn off echo for passphrases\n"
76              "  --format[=raw|..]    Force disk format for -a option\n"
77              "  --help               Display brief help\n"
78              "  --keys-from-stdin    Read passphrases from stdin\n"
79              "  -l|--long            Long listing\n"
80              "  -R|--recursive       Recursive listing\n"
81              "  -v|--verbose         Verbose messages\n"
82              "  -V|--version         Display version and exit\n"
83              "  -x                   Trace libguestfs API calls\n"
84              "For more information, see the manpage %s(1).\n"),
85              program_name, program_name, program_name,
86              program_name);
87   }
88   exit (status);
89 }
90
91 int
92 main (int argc, char *argv[])
93 {
94   /* Set global program name that is not polluted with libtool artifacts.  */
95   set_program_name (argv[0]);
96
97   setlocale (LC_ALL, "");
98   bindtextdomain (PACKAGE, LOCALEBASEDIR);
99   textdomain (PACKAGE);
100
101   enum { HELP_OPTION = CHAR_MAX + 1 };
102
103   static const char *options = "a:c:d:lRvVx";
104   static const struct option long_options[] = {
105     { "add", 1, 0, 'a' },
106     { "connect", 1, 0, 'c' },
107     { "domain", 1, 0, 'd' },
108     { "echo-keys", 0, 0, 0 },
109     { "format", 2, 0, 0 },
110     { "help", 0, 0, HELP_OPTION },
111     { "keys-from-stdin", 0, 0, 0 },
112     { "long", 0, 0, 'l' },
113     { "recursive", 0, 0, 'R' },
114     { "verbose", 0, 0, 'v' },
115     { "version", 0, 0, 'V' },
116     { 0, 0, 0, 0 }
117   };
118   struct drv *drvs = NULL;
119   struct drv *drv;
120   const char *format = NULL;
121   int c;
122   int option_index;
123 #define MODE_LS_L  1
124 #define MODE_LS_R  2
125 #define MODE_LS_LR (MODE_LS_L|MODE_LS_R)
126   int mode = 0;
127
128   g = guestfs_create ();
129   if (g == NULL) {
130     fprintf (stderr, _("guestfs_create: failed to create handle\n"));
131     exit (EXIT_FAILURE);
132   }
133
134   argv[0] = bad_cast (program_name);
135
136   for (;;) {
137     c = getopt_long (argc, argv, options, long_options, &option_index);
138     if (c == -1) break;
139
140     switch (c) {
141     case 0:                     /* options which are long only */
142       if (STREQ (long_options[option_index].name, "keys-from-stdin")) {
143         keys_from_stdin = 1;
144       } else if (STREQ (long_options[option_index].name, "echo-keys")) {
145         echo_keys = 1;
146       } else if (STREQ (long_options[option_index].name, "format")) {
147         if (!optarg || STREQ (optarg, ""))
148           format = NULL;
149         else
150           format = optarg;
151       } else {
152         fprintf (stderr, _("%s: unknown long option: %s (%d)\n"),
153                  program_name, long_options[option_index].name, option_index);
154         exit (EXIT_FAILURE);
155       }
156       break;
157
158     case 'a':
159       OPTION_a;
160       break;
161
162     case 'c':
163       OPTION_c;
164       break;
165
166     case 'd':
167       OPTION_d;
168       break;
169
170     case 'h':
171       usage (EXIT_SUCCESS);
172
173     case 'l':
174       mode |= MODE_LS_L;
175       break;
176
177     case 'R':
178       mode |= MODE_LS_R;
179       break;
180
181     case 'v':
182       OPTION_v;
183       break;
184
185     case 'V':
186       OPTION_V;
187       break;
188
189     case 'x':
190       OPTION_x;
191       break;
192
193     case HELP_OPTION:
194       usage (EXIT_SUCCESS);
195
196     default:
197       usage (EXIT_FAILURE);
198     }
199   }
200
201   /* Old-style syntax?  There were no -a or -d options in the old
202    * virt-ls which is how we detect this.
203    */
204   if (drvs == NULL) {
205     /* argc - 1 because last parameter is the single directory name. */
206     while (optind < argc - 1) {
207       if (strchr (argv[optind], '/') ||
208           access (argv[optind], F_OK) == 0) { /* simulate -a option */
209         drv = malloc (sizeof (struct drv));
210         if (!drv) {
211           perror ("malloc");
212           exit (EXIT_FAILURE);
213         }
214         drv->type = drv_a;
215         drv->a.filename = argv[optind];
216         drv->a.format = NULL;
217         drv->next = drvs;
218         drvs = drv;
219       } else {                  /* simulate -d option */
220         drv = malloc (sizeof (struct drv));
221         if (!drv) {
222           perror ("malloc");
223           exit (EXIT_FAILURE);
224         }
225         drv->type = drv_d;
226         drv->d.guest = argv[optind];
227         drv->next = drvs;
228         drvs = drv;
229       }
230
231       optind++;
232     }
233   }
234
235   if (mode == MODE_LS_LR) {
236     fprintf (stderr, _("%s: cannot combine -l and -R options\n"),
237              program_name);
238     exit (EXIT_FAILURE);
239   }
240
241   /* These are really constants, but they have to be variables for the
242    * options parsing code.  Assert here that they have known-good
243    * values.
244    */
245   assert (read_only == 1);
246   assert (inspector == 1);
247   assert (live == 0);
248
249   /* User must specify at least one directory name on the command line. */
250   if (optind >= argc || argc - optind < 1)
251     usage (EXIT_FAILURE);
252
253   /* User must have specified some drives. */
254   if (drvs == NULL)
255     usage (EXIT_FAILURE);
256
257   /* Add drives, inspect and mount.  Note that inspector is always true,
258    * and there is no -m option.
259    */
260   add_drives (drvs, 'a');
261
262   if (guestfs_launch (g) == -1)
263     exit (EXIT_FAILURE);
264
265   inspect_mount ();
266
267   /* Free up data structures, no longer needed after this point. */
268   free_drives (drvs);
269
270   unsigned errors = 0;
271
272   while (optind < argc) {
273     const char *dir = argv[optind];
274
275     switch (mode) {
276     case 0:                     /* no -l or -R option */
277       if (do_ls (dir) == -1)
278         errors++;
279       break;
280
281     case MODE_LS_L:             /* virt-ls -l */
282       if (do_ls_l (dir) == -1)
283         errors++;
284       break;
285
286     case MODE_LS_R:             /* virt-ls -R */
287       if (do_ls_R (dir) == -1)
288         errors++;
289       break;
290
291     default:
292       abort ();                 /* can't happen */
293     }
294
295     optind++;
296   }
297
298   guestfs_close (g);
299
300   exit (errors == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
301 }
302
303 static int
304 do_ls (const char *dir)
305 {
306   char **lines;
307   size_t i;
308
309   if ((lines = guestfs_ls (g, dir)) == NULL) {
310     return -1;
311   }
312
313   for (i = 0; lines[i] != NULL; ++i) {
314     printf ("%s\n", lines[i]);
315     free (lines[i]);
316   }
317   free (lines);
318
319   return 0;
320 }
321
322 static int
323 do_ls_l (const char *dir)
324 {
325   char *out;
326
327   if ((out = guestfs_ll (g, dir)) == NULL)
328     return -1;
329
330   printf ("%s", out);
331   free (out);
332
333   return 0;
334 }
335
336 static int
337 do_ls_R (const char *dir)
338 {
339   /* This is TMP_TEMPLATE_ON_STACK expanded from fish.h. */
340   const char *tmpdir = guestfs_tmpdir ();
341   char tmpfile[strlen (tmpdir) + 32];
342   sprintf (tmpfile, "%s/virtlsXXXXXX", tmpdir);
343
344   int fd = mkstemp (tmpfile);
345   if (fd == -1) {
346     perror ("mkstemp");
347     exit (EXIT_FAILURE);
348   }
349
350   char buf[BUFSIZ]; /* also used below */
351   snprintf (buf, sizeof buf, "/dev/fd/%d", fd);
352
353   if (guestfs_find0 (g, dir, buf) == -1)
354     return -1;
355
356   if (close (fd) == -1) {
357     perror (tmpfile);
358     exit (EXIT_FAILURE);
359   }
360
361   /* The output of find0 is a \0-separated file.  Turn each \0 into
362    * a \n character.
363    */
364   fd = open (tmpfile, O_RDONLY);
365   if (fd == -1) {
366     perror (tmpfile);
367     exit (EXIT_FAILURE);
368   }
369
370   ssize_t r;
371   while ((r = read (fd, buf, sizeof buf)) > 0) {
372     size_t i;
373     for (i = 0; i < (size_t) r; ++i)
374       if (buf[i] == '\0')
375         buf[i] = '\n';
376
377     size_t n = r;
378     while (n > 0) {
379       r = write (1, buf, n);
380       if (r == -1) {
381         perror ("write");
382         exit (EXIT_FAILURE);
383       }
384       n -= r;
385     }
386   }
387
388   if (r == -1 || close (fd) == -1) {
389     perror (tmpfile);
390     exit (EXIT_FAILURE);
391   }
392
393  unlink (tmpfile);
394
395  return 0;
396 }