fish: -i option should fail if / is not mountable, but warn about others.
[libguestfs.git] / fish / inspect.c
1 /* libguestfs - guestfish and guestmount shared option parsing
2  * Copyright (C) 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17  */
18
19 #include <config.h>
20
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24
25 #include "c-ctype.h"
26
27 #include "guestfs.h"
28
29 #include "options.h"
30
31 /* Global that saves the root device between inspect_mount and
32  * print_inspect_prompt.
33  */
34 static char *root = NULL;
35
36 static void
37 free_strings (char **argv)
38 {
39   int argc;
40
41   for (argc = 0; argv[argc] != NULL; ++argc)
42     free (argv[argc]);
43   free (argv);
44 }
45
46 static size_t
47 count_strings (char *const *argv)
48 {
49   size_t i;
50
51   for (i = 0; argv[i]; ++i)
52     ;
53   return i;
54 }
55
56 static int
57 compare_keys_len (const void *p1, const void *p2)
58 {
59   const char *key1 = * (char * const *) p1;
60   const char *key2 = * (char * const *) p2;
61   return strlen (key1) - strlen (key2);
62 }
63
64 static int
65 compare_keys (const void *p1, const void *p2)
66 {
67   const char *key1 = * (char * const *) p1;
68   const char *key2 = * (char * const *) p2;
69   return strcasecmp (key1, key2);
70 }
71
72 /* This function implements the -i option. */
73 void
74 inspect_mount (void)
75 {
76   if (live) {
77     fprintf (stderr, _("%s: don't use --live and -i options together\n"),
78              program_name);
79     exit (EXIT_FAILURE);
80   }
81
82   inspect_do_decrypt ();
83
84   char **roots = guestfs_inspect_os (g);
85   if (roots == NULL)
86     exit (EXIT_FAILURE);
87
88   if (roots[0] == NULL) {
89     fprintf (stderr,
90       _("%s: no operating system was found on this disk\n"
91         "\n"
92         "If using guestfish '-i' option, remove this option and instead\n"
93         "use the commands 'run' followed by 'list-filesystems'.\n"
94         "You can then mount filesystems you want by hand using the\n"
95         "'mount' or 'mount-ro' command.\n"
96         "\n"
97         "If using guestmount '-i', remove this option and choose the\n"
98         "filesystem(s) you want to see by manually adding '-m' option(s).\n"
99         "Use 'virt-filesystems' to see what filesystems are available.\n"
100         "\n"
101         "If using other virt tools, this disk image won't work\n"
102         "with these tools.  Use the guestfish equivalent commands\n"
103         "(see the virt tool manual page).\n"),
104              program_name);
105     free_strings (roots);
106     exit (EXIT_FAILURE);
107   }
108
109   if (roots[1] != NULL) {
110     fprintf (stderr,
111       _("%s: multi-boot operating systems are not supported\n"
112         "\n"
113         "If using guestfish '-i' option, remove this option and instead\n"
114         "use the commands 'run' followed by 'list-filesystems'.\n"
115         "You can then mount filesystems you want by hand using the\n"
116         "'mount' or 'mount-ro' command.\n"
117         "\n"
118         "If using guestmount '-i', remove this option and choose the\n"
119         "filesystem(s) you want to see by manually adding '-m' option(s).\n"
120         "Use 'virt-filesystems' to see what filesystems are available.\n"
121         "\n"
122         "If using other virt tools, multi-boot operating systems won't work\n"
123         "with these tools.  Use the guestfish equivalent commands\n"
124         "(see the virt tool manual page).\n"),
125              program_name);
126     free_strings (roots);
127     exit (EXIT_FAILURE);
128   }
129
130   root = roots[0];
131   free (roots);
132
133   inspect_mount_root (root);
134 }
135
136 void
137 inspect_mount_root (const char *root)
138 {
139   char **mountpoints = guestfs_inspect_get_mountpoints (g, root);
140   if (mountpoints == NULL)
141     exit (EXIT_FAILURE);
142
143   /* Sort by key length, shortest key first, so that we end up
144    * mounting the filesystems in the correct order.
145    */
146   qsort (mountpoints, count_strings (mountpoints) / 2, 2 * sizeof (char *),
147          compare_keys_len);
148
149   size_t i;
150   size_t mount_errors = 0;
151   for (i = 0; mountpoints[i] != NULL; i += 2) {
152     int r;
153     if (!read_only)
154       r = guestfs_mount_options (g, "", mountpoints[i+1], mountpoints[i]);
155     else
156       r = guestfs_mount_ro (g, mountpoints[i+1], mountpoints[i]);
157     if (r == -1) {
158       /* If the "/" filesystem could not be mounted, give up, else
159        * just count the errors and print a warning.
160        */
161       if (STREQ (mountpoints[i], "/"))
162         exit (EXIT_FAILURE);
163       mount_errors++;
164     }
165   }
166
167   free_strings (mountpoints);
168
169   if (mount_errors)
170     fprintf (stderr, _("%s: some filesystems could not be mounted (ignored)\n"),
171              program_name);
172 }
173
174 /* This function is called only if the above function was called,
175  * and only after we've printed the prompt in interactive mode.
176  */
177 void
178 print_inspect_prompt (void)
179 {
180   char *name = guestfs_inspect_get_product_name (g, root);
181   if (name && STRNEQ (name, "unknown"))
182     printf (_("Operating system: %s\n"), name);
183   free (name);
184
185   char **mountpoints = guestfs_inspect_get_mountpoints (g, root);
186   if (mountpoints == NULL)
187     return;
188
189   /* Sort by key. */
190   qsort (mountpoints, count_strings (mountpoints) / 2, 2 * sizeof (char *),
191          compare_keys);
192
193   size_t i;
194   for (i = 0; mountpoints[i] != NULL; i += 2)
195     printf (_("%s mounted on %s\n"), mountpoints[i+1], mountpoints[i]);
196
197   free_strings (mountpoints);
198 }
199
200 /* Make a LUKS map name from the partition name,
201  * eg "/dev/vda2" => "luksvda2"
202  */
203 static void
204 make_mapname (const char *device, char *mapname, size_t len)
205 {
206   size_t i = 0;
207
208   if (len < 5)
209     abort ();
210   strcpy (mapname, "luks");
211   mapname += 4;
212   len -= 4;
213
214   if (STRPREFIX (device, "/dev/"))
215     i = 5;
216
217   for (; device[i] != '\0' && len >= 1; ++i) {
218     if (c_isalnum (device[i])) {
219       *mapname++ = device[i];
220       len--;
221     }
222   }
223
224   *mapname = '\0';
225 }
226
227 /* Simple implementation of decryption: look for any crypto_LUKS
228  * partitions and decrypt them, then rescan for VGs.  This only works
229  * for Fedora whole-disk encryption.  WIP to make this work for other
230  * encryption schemes.
231  */
232 void
233 inspect_do_decrypt (void)
234 {
235   char **partitions = guestfs_list_partitions (g);
236   if (partitions == NULL)
237     exit (EXIT_FAILURE);
238
239   int need_rescan = 0;
240   size_t i;
241   for (i = 0; partitions[i] != NULL; ++i) {
242     char *type = guestfs_vfs_type (g, partitions[i]);
243     if (type && STREQ (type, "crypto_LUKS")) {
244       char mapname[32];
245       make_mapname (partitions[i], mapname, sizeof mapname);
246
247       char *key = read_key (partitions[i]);
248       /* XXX Should we call guestfs_luks_open_ro if readonly flag
249        * is set?  This might break 'mount_ro'.
250        */
251       if (guestfs_luks_open (g, partitions[i], key, mapname) == -1)
252         exit (EXIT_FAILURE);
253
254       free (key);
255
256       need_rescan = 1;
257     }
258     free (type);
259   }
260
261   free_strings (partitions);
262
263   if (need_rescan) {
264     if (guestfs_vgscan (g) == -1)
265       exit (EXIT_FAILURE);
266     if (guestfs_vg_activate_all (g, 1) == -1)
267       exit (EXIT_FAILURE);
268   }
269 }