fish: Add -N option for making prepared disk images.
[libguestfs.git] / fish / prep.c
1 /* guestfish - the filesystem interactive shell
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 <stdarg.h>
24 #include <string.h>
25 #include <unistd.h>
26
27 #include "fish.h"
28
29 static prep_data *parse_type_string (const char *type_string);
30 static void prep_error (prep_data *data, const char *filename, const char *fs, ...) __attribute__((noreturn, format (printf,3,4)));
31
32 struct prep {
33   const char *name;             /* eg. "fs" */
34
35   size_t nr_params;             /* optional parameters */
36   struct param *params;
37
38   const char *shortdesc;        /* short description */
39   const char *longdesc;         /* long description */
40
41                                 /* functions to implement it */
42   void (*prelaunch) (const char *filename, prep_data *);
43   void (*postlaunch) (const char *filename, prep_data *, const char *device);
44 };
45
46 struct param {
47   const char *pname;            /* parameter name */
48   const char *pdefault;         /* parameter default */
49   const char *pdesc;            /* parameter description */
50 };
51
52 static void prelaunch_disk (const char *filename, prep_data *data);
53 static struct param disk_params[] = {
54   { "size", "100M", "the size of the disk image" },
55 };
56
57 static void prelaunch_part (const char *filename, prep_data *data);
58 static void postlaunch_part (const char *filename, prep_data *data, const char *device);
59 static struct param part_params[] = {
60   { "size", "100M", "the size of the disk image" },
61   { "partition", "mbr", "partition table type" },
62 };
63
64 static void prelaunch_fs (const char *filename, prep_data *data);
65 static void postlaunch_fs (const char *filename, prep_data *data, const char *device);
66 static struct param fs_params[] = {
67   { "filesystem", "ext2", "the type of filesystem to use" },
68   { "size", "100M", "the size of the disk image" },
69   { "partition", "mbr", "partition table type" },
70 };
71
72 static const struct prep preps[] = {
73   { "disk",
74     1, disk_params,
75     "create a blank disk",
76     "\
77   Create a blank disk, size 100MB (by default).\n\
78 \n\
79   The default size can be changed by supplying an optional parameter.",
80     prelaunch_disk, NULL
81   },
82   { "part",
83     2, part_params,
84     "create a partitioned disk",
85     "\
86   Create a disk with a single partition.  By default the size of the disk\n\
87   is 100MB (the available space in the partition will be a tiny bit smaller)\n\
88   and the partition table will be MBR (old DOS-style).\n\
89 \n\
90   These defaults can be changed by supplying optional parameters.",
91     prelaunch_part, postlaunch_part
92   },
93   { "fs",
94     3, fs_params,
95     "create a filesystem",
96     "\
97   Create a disk with a single partition, with the partition containing\n\
98   an empty filesystem.  This defaults to creating a 100MB disk (the available\n\
99   space in the filesystem will be a tiny bit smaller) with an MBR (old\n\
100   DOS-style) partition table and an ext2 filesystem.\n\
101 \n\
102   These defaults can be changed by supplying optional parameters.",
103     prelaunch_fs, postlaunch_fs
104   },
105 };
106
107 void
108 list_prepared_drives (void)
109 {
110   size_t i, j;
111
112   printf (_("List of available prepared disk images:\n\n"));
113
114   for (i = 0; i < sizeof preps / sizeof preps[0]; ++i) {
115     printf (_("\
116 guestfish -N %-16s %s\n\
117 \n\
118 %s\n"),
119             preps[i].name, preps[i].shortdesc, preps[i].longdesc);
120
121     if (preps[i].nr_params > 0) {
122       printf ("\n");
123       printf (_("  Optional parameters:\n"));
124       printf ("    -N %s", preps[i].name);
125       for (j = 0; j < preps[i].nr_params; ++j)
126         printf (":<%s>", preps[i].params[j].pname);
127       printf ("\n");
128       for (j = 0; j < preps[i].nr_params; ++j) {
129         printf ("      ");
130         printf (_("<%s> %s (default: %s)\n"),
131                 preps[i].params[j].pname,
132                 preps[i].params[j].pdesc,
133                 preps[i].params[j].pdefault);
134       }
135     }
136
137     printf ("\n");
138   }
139
140   printf (_("\
141 Prepared disk images are written to file \"test1.img\" in the local\n\
142 directory.  (\"test2.img\" etc if -N option is given multiple times).\n\
143 For more information see the guestfish(1) manual.\n"));
144 }
145
146 struct prep_data {
147   const struct prep *prep;
148   const char *orig_type_string;
149   const char **params;
150 };
151
152 /* Parse the type string (from the command line) and create the output
153  * file 'filename'.  This is called before launch.  Return the opaque
154  * prep_data which will be passed back to us in prepare_drive below.
155  */
156 prep_data *
157 create_prepared_file (const char *type_string, const char *filename)
158 {
159   if (access (filename, F_OK) == 0) {
160     fprintf (stderr, _("guestfish: file '%s' exists and the '-N' option will not overwrite it\n"),
161              filename);
162     exit (EXIT_FAILURE);
163   }
164
165   prep_data *data = parse_type_string (type_string);
166   if (data->prep->prelaunch)
167     data->prep->prelaunch (filename, data);
168   return data;
169 }
170
171 static prep_data *
172 parse_type_string (const char *type_string)
173 {
174   size_t i;
175
176   /* Match on the type part (without parameters). */
177   size_t len = strcspn (type_string, ":");
178   for (i = 0; i < sizeof preps / sizeof preps[0]; ++i)
179     if (STRCASEEQLEN (type_string, preps[i].name, len))
180       break;
181
182   if (preps[i].name == NULL) {
183     fprintf (stderr, _("\
184 guestfish: -N parameter '%s': no such prepared disk image known.\n\
185 Use 'guestfish -N list' to list possible values for the -N parameter.\n"),
186              type_string);
187     exit (EXIT_FAILURE);
188   }
189
190   prep_data *data = malloc (sizeof *data);
191   if (data == NULL) {
192     perror ("malloc");
193     exit (EXIT_FAILURE);
194   }
195   data->prep = &preps[i];
196   data->orig_type_string = type_string;
197
198   /* Set up the optional parameters to all-defaults. */
199   data->params = malloc (data->prep->nr_params * sizeof (char *));
200   if (data->params == NULL) {
201     perror ("malloc");
202     exit (EXIT_FAILURE);
203   }
204
205   for (i = 0; i < data->prep->nr_params; ++i)
206     data->params[i] = data->prep->params[i].pdefault;
207
208   /* Parse the optional parameters. */
209   const char *p = type_string + len;
210   if (*p) p++; /* skip colon char */
211
212   i = 0;
213   while (*p) {
214     len = strcspn (p, ":");
215     data->params[i] = strndup (p, len);
216     if (data->params[i] == NULL) {
217       perror ("strndup");
218       exit (EXIT_FAILURE);
219     }
220
221     p += len;
222     if (*p) p++; /* skip colon char */
223     i++;
224   }
225
226   return data;
227 }
228
229 /* Prepare a drive.  The appliance has been launched, and 'device' is
230  * the libguestfs device.  'data' is the requested type.  'filename'
231  * is just used for error messages.
232  */
233 void
234 prepare_drive (const char *filename, prep_data *data,
235                const char *device)
236 {
237   if (data->prep->postlaunch)
238     data->prep->postlaunch (filename, data, device);
239 }
240
241 static void
242 prep_error (prep_data *data, const char *filename, const char *fs, ...)
243 {
244   fprintf (stderr,
245            _("guestfish: error creating prepared disk image '%s' on '%s': "),
246            data->orig_type_string, filename);
247
248   va_list args;
249   va_start (args, fs);
250   vfprintf (stderr, fs, args);
251   va_end (args);
252
253   fprintf (stderr, "\n");
254
255   exit (EXIT_FAILURE);
256 }
257
258 static void
259 prelaunch_disk (const char *filename, prep_data *data)
260 {
261   if (alloc_disk (filename, data->params[0], 0, 1) == -1)
262     prep_error (data, filename, _("failed to allocate disk"));
263 }
264
265 static void
266 prelaunch_part (const char *filename, prep_data *data)
267 {
268   if (alloc_disk (filename, data->params[0], 0, 1) == -1)
269     prep_error (data, filename, _("failed to allocate disk"));
270 }
271
272 static void
273 postlaunch_part (const char *filename, prep_data *data, const char *device)
274 {
275   if (guestfs_part_disk (g, device, data->params[2]) == -1)
276     prep_error (data, filename, _("failed to partition disk: %s"),
277                 guestfs_last_error (g));
278 }
279
280 static void
281 prelaunch_fs (const char *filename, prep_data *data)
282 {
283   if (alloc_disk (filename, data->params[1], 0, 1) == -1)
284     prep_error (data, filename, _("failed to allocate disk"));
285 }
286
287 static void
288 postlaunch_fs (const char *filename, prep_data *data, const char *device)
289 {
290   if (guestfs_part_disk (g, device, data->params[2]) == -1)
291     prep_error (data, filename, _("failed to partition disk: %s"),
292                 guestfs_last_error (g));
293
294   char *part;
295   if (asprintf (&part, "%s1", device) == -1) {
296     perror ("asprintf");
297     exit (EXIT_FAILURE);
298   }
299
300   if (guestfs_mkfs (g, data->params[0], part) == -1)
301     prep_error (data, filename, _("failed to create filesystem (%s): %s"),
302                 data->params[0], guestfs_last_error (g));
303
304   free (part);
305 }