inspect: Add support for Linux Mint and Mandriva.
[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., 675 Mass Ave, Cambridge, MA 02139, 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 static void do_decrypt (void);
32
33 /* Global that saves the root device between inspect_mount and
34  * print_inspect_prompt.
35  */
36 static char *root = NULL;
37
38 static void
39 free_strings (char **argv)
40 {
41   int argc;
42
43   for (argc = 0; argv[argc] != NULL; ++argc)
44     free (argv[argc]);
45   free (argv);
46 }
47
48 static int
49 count_strings (char *const *argv)
50 {
51   int c;
52
53   for (c = 0; argv[c]; ++c)
54     ;
55   return c;
56 }
57
58 static int
59 compare_keys_len (const void *p1, const void *p2)
60 {
61   const char *key1 = * (char * const *) p1;
62   const char *key2 = * (char * const *) p2;
63   return strlen (key1) - strlen (key2);
64 }
65
66 static int
67 compare_keys (const void *p1, const void *p2)
68 {
69   const char *key1 = * (char * const *) p1;
70   const char *key2 = * (char * const *) p2;
71   return strcasecmp (key1, key2);
72 }
73
74 /* This function implements the -i option. */
75 void
76 inspect_mount (void)
77 {
78   do_decrypt ();
79
80   char **roots = guestfs_inspect_os (g);
81   if (roots == NULL)
82     exit (EXIT_FAILURE);
83
84   if (roots[0] == NULL) {
85     fprintf (stderr, _("%s: no operating system was found on this disk\n"),
86              program_name);
87     exit (EXIT_FAILURE);
88   }
89
90   if (roots[1] != NULL) {
91     fprintf (stderr, _("%s: multi-boot operating systems are not supported by the -i option\n"),
92              program_name);
93     exit (EXIT_FAILURE);
94   }
95
96   root = roots[0];
97   free (roots);
98
99   char **mountpoints = guestfs_inspect_get_mountpoints (g, root);
100   if (mountpoints == NULL)
101     exit (EXIT_FAILURE);
102
103   /* Sort by key length, shortest key first, so that we end up
104    * mounting the filesystems in the correct order.
105    */
106   qsort (mountpoints, count_strings (mountpoints) / 2, 2 * sizeof (char *),
107          compare_keys_len);
108
109   size_t i;
110   for (i = 0; mountpoints[i] != NULL; i += 2) {
111     int r;
112     if (!read_only)
113       r = guestfs_mount_options (g, "", mountpoints[i+1], mountpoints[i]);
114     else
115       r = guestfs_mount_ro (g, mountpoints[i+1], mountpoints[i]);
116     if (r == -1)
117       exit (EXIT_FAILURE);
118   }
119
120   free_strings (mountpoints);
121 }
122
123 /* This function is called only if the above function was called,
124  * and only after we've printed the prompt in interactive mode.
125  */
126 void
127 print_inspect_prompt (void)
128 {
129   char *name = guestfs_inspect_get_product_name (g, root);
130   if (STRNEQ (name, "unknown"))
131     printf (_("Operating system: %s\n"), name);
132   free (name);
133
134   char **mountpoints = guestfs_inspect_get_mountpoints (g, root);
135   if (mountpoints == NULL)
136     return;
137
138   /* Sort by key. */
139   qsort (mountpoints, count_strings (mountpoints) / 2, 2 * sizeof (char *),
140          compare_keys);
141
142   size_t i;
143   for (i = 0; mountpoints[i] != NULL; i += 2)
144     printf (_("%s mounted on %s\n"), mountpoints[i+1], mountpoints[i]);
145
146   free_strings (mountpoints);
147 }
148
149 /* Make a LUKS map name from the partition name,
150  * eg "/dev/vda2" => "luksvda2"
151  */
152 static void
153 make_mapname (const char *device, char *mapname, size_t len)
154 {
155   size_t i = 0;
156
157   if (len < 5)
158     abort ();
159   strcpy (mapname, "luks");
160   mapname += 4;
161   len -= 4;
162
163   if (STRPREFIX (device, "/dev/"))
164     i = 5;
165
166   for (; device[i] != '\0' && len >= 1; ++i) {
167     if (c_isalnum (device[i])) {
168       *mapname++ = device[i];
169       len--;
170     }
171   }
172
173   *mapname = '\0';
174 }
175
176 /* Simple implementation of decryption: look for any crypto_LUKS
177  * partitions and decrypt them, then rescan for VGs.  This only works
178  * for Fedora whole-disk encryption.  WIP to make this work for other
179  * encryption schemes.
180  */
181 static void
182 do_decrypt (void)
183 {
184   char **partitions = guestfs_list_partitions (g);
185   if (partitions == NULL)
186     exit (EXIT_FAILURE);
187
188   int need_rescan = 0;
189   size_t i;
190   for (i = 0; partitions[i] != NULL; ++i) {
191     char *type = guestfs_vfs_type (g, partitions[i]);
192     if (type && STREQ (type, "crypto_LUKS")) {
193       char mapname[32];
194       make_mapname (partitions[i], mapname, sizeof mapname);
195
196       char *key = read_key (partitions[i]);
197       /* XXX Should we call guestfs_luks_open_ro if readonly flag
198        * is set?  This might break 'mount_ro'.
199        */
200       if (guestfs_luks_open (g, partitions[i], key, mapname) == -1)
201         exit (EXIT_FAILURE);
202
203       free (key);
204
205       need_rescan = 1;
206     }
207   }
208
209   free_strings (partitions);
210
211   if (need_rescan) {
212     if (guestfs_vgscan (g) == -1)
213       exit (EXIT_FAILURE);
214     if (guestfs_vg_activate_all (g, 1) == -1)
215       exit (EXIT_FAILURE);
216   }
217 }