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