7d4401e2732707f5d15e608b439b9b03fccd69e1
[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       mount_errors++;
159   }
160
161   free_strings (mountpoints);
162
163   if (mount_errors)
164     fprintf (stderr, _("%s: some filesystems could not be mounted (ignored)\n"),
165              program_name);
166 }
167
168 /* This function is called only if the above function was called,
169  * and only after we've printed the prompt in interactive mode.
170  */
171 void
172 print_inspect_prompt (void)
173 {
174   char *name = guestfs_inspect_get_product_name (g, root);
175   if (name && STRNEQ (name, "unknown"))
176     printf (_("Operating system: %s\n"), name);
177   free (name);
178
179   char **mountpoints = guestfs_inspect_get_mountpoints (g, root);
180   if (mountpoints == NULL)
181     return;
182
183   /* Sort by key. */
184   qsort (mountpoints, count_strings (mountpoints) / 2, 2 * sizeof (char *),
185          compare_keys);
186
187   size_t i;
188   for (i = 0; mountpoints[i] != NULL; i += 2)
189     printf (_("%s mounted on %s\n"), mountpoints[i+1], mountpoints[i]);
190
191   free_strings (mountpoints);
192 }
193
194 /* Make a LUKS map name from the partition name,
195  * eg "/dev/vda2" => "luksvda2"
196  */
197 static void
198 make_mapname (const char *device, char *mapname, size_t len)
199 {
200   size_t i = 0;
201
202   if (len < 5)
203     abort ();
204   strcpy (mapname, "luks");
205   mapname += 4;
206   len -= 4;
207
208   if (STRPREFIX (device, "/dev/"))
209     i = 5;
210
211   for (; device[i] != '\0' && len >= 1; ++i) {
212     if (c_isalnum (device[i])) {
213       *mapname++ = device[i];
214       len--;
215     }
216   }
217
218   *mapname = '\0';
219 }
220
221 /* Simple implementation of decryption: look for any crypto_LUKS
222  * partitions and decrypt them, then rescan for VGs.  This only works
223  * for Fedora whole-disk encryption.  WIP to make this work for other
224  * encryption schemes.
225  */
226 void
227 inspect_do_decrypt (void)
228 {
229   char **partitions = guestfs_list_partitions (g);
230   if (partitions == NULL)
231     exit (EXIT_FAILURE);
232
233   int need_rescan = 0;
234   size_t i;
235   for (i = 0; partitions[i] != NULL; ++i) {
236     char *type = guestfs_vfs_type (g, partitions[i]);
237     if (type && STREQ (type, "crypto_LUKS")) {
238       char mapname[32];
239       make_mapname (partitions[i], mapname, sizeof mapname);
240
241       char *key = read_key (partitions[i]);
242       /* XXX Should we call guestfs_luks_open_ro if readonly flag
243        * is set?  This might break 'mount_ro'.
244        */
245       if (guestfs_luks_open (g, partitions[i], key, mapname) == -1)
246         exit (EXIT_FAILURE);
247
248       free (key);
249
250       need_rescan = 1;
251     }
252     free (type);
253   }
254
255   free_strings (partitions);
256
257   if (need_rescan) {
258     if (guestfs_vgscan (g) == -1)
259       exit (EXIT_FAILURE);
260     if (guestfs_vg_activate_all (g, 1) == -1)
261       exit (EXIT_FAILURE);
262   }
263 }