Add -f checksum mode to allow caching of appliances.
[febootstrap.git] / helper / main.c
1 /* febootstrap-supermin-helper reimplementation in C.
2  * Copyright (C) 2009-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 #include <errno.h>
25 #include <unistd.h>
26 #include <getopt.h>
27 #include <limits.h>
28 #include <sys/types.h>
29 #include <sys/time.h>
30 #include <assert.h>
31
32 #include "error.h"
33
34 #include "helper.h"
35
36 struct timeval start_t;
37 int verbose = 0;
38
39 static const char *format = "cpio";
40
41 enum { HELP_OPTION = CHAR_MAX + 1 };
42
43 static const char *options = "f:k:vV";
44 static const struct option long_options[] = {
45   { "help", 0, 0, HELP_OPTION },
46   { "format", required_argument, 0, 'f' },
47   { "kmods", required_argument, 0, 'k' },
48   { "verbose", 0, 0, 'v' },
49   { "version", 0, 0, 'V' },
50   { 0, 0, 0, 0 }
51 };
52
53 static void
54 usage (const char *progname)
55 {
56   printf ("%s: build the supermin appliance on the fly\n"
57           "\n"
58           "Usage:\n"
59           "  %s [-options] inputs [...] host_cpu kernel initrd\n"
60           "  %s -f ext2 inputs [...] host_cpu kernel initrd appliance\n"
61           "  %s -f checksum inputs [...] host_cpu\n"
62           "  %s --help\n"
63           "  %s --version\n"
64           "\n"
65           "This script is used by febootstrap to build the supermin appliance\n"
66           "(kernel and initrd output files).  You should NOT need to run this\n"
67           "program directly except if you are debugging tricky supermin\n"
68           "appliance problems.\n"
69           "\n"
70           "NB: The kernel and initrd parameters are OUTPUT parameters.  If\n"
71           "those files exist, they are overwritten by the output.\n"
72           "\n"
73           "Options:\n"
74           "  --help\n"
75           "       Display this help text and exit.\n"
76           "  -f cpio|ext2|checksum | --format cpio|ext2|checksum\n"
77           "       Specify output format (default: cpio).\n"
78           "  -k file | --kmods file\n"
79           "       Specify kernel module whitelist.\n"
80           "  --verbose | -v\n"
81           "       Enable verbose messages (give multiple times for more verbosity).\n"
82           "  --version | -V\n"
83           "       Display version number and exit.\n",
84           progname, progname, progname, progname, progname, progname);
85 }
86
87 int
88 main (int argc, char *argv[])
89 {
90   /* First thing: start the clock. */
91   gettimeofday (&start_t, NULL);
92
93   const char *whitelist = NULL;
94
95   /* Command line arguments. */
96   for (;;) {
97     int c = getopt_long (argc, argv, options, long_options, NULL);
98     if (c == -1) break;
99
100     switch (c) {
101     case HELP_OPTION:
102       usage (argv[0]);
103       exit (EXIT_SUCCESS);
104
105     case 'f':
106       format = optarg;
107       break;
108
109     case 'k':
110       whitelist = optarg;
111       break;
112
113     case 'v':
114       verbose++;
115       break;
116
117     case 'V':
118       printf (PACKAGE_NAME " " PACKAGE_VERSION "\n");
119       exit (EXIT_SUCCESS);
120
121     default:
122       usage (argv[0]);
123       exit (EXIT_FAILURE);
124     }
125   }
126
127   /* Select the correct writer module. */
128   struct writer *writer;
129   int nr_outputs;
130
131   if (strcmp (format, "cpio") == 0) {
132     writer = &cpio_writer;
133     nr_outputs = 2;             /* kernel and appliance (== initrd) */
134   }
135   else if (strcmp (format, "ext2") == 0) {
136     writer = &ext2_writer;
137     nr_outputs = 3;             /* kernel, initrd, appliance */
138   }
139   else if (strcmp (format, "checksum") == 0) {
140     writer = &checksum_writer;
141     nr_outputs = 0;             /* (none) */
142   }
143   else {
144     fprintf (stderr,
145              "%s: incorrect output format (-f): must be cpio|ext2|checksum\n",
146              argv[0]);
147     exit (EXIT_FAILURE);
148   }
149
150   /* [optind .. optind+nr_inputs-1] hostcpu [argc-nr_outputs-1 .. argc-1]
151    * <----     nr_inputs      ---->    1    <----    nr_outputs     ---->
152    */
153   char **inputs = &argv[optind];
154   int nr_inputs = argc - nr_outputs - 1 - optind;
155   char **outputs = &argv[optind+nr_inputs+1];
156   /*assert (outputs [nr_outputs] == NULL);
157     assert (inputs [nr_inputs + 1 + nr_outputs] == NULL);*/
158
159   if (nr_inputs < 1) {
160     fprintf (stderr, "%s: not enough files specified on the command line\n",
161              argv[0]);
162     exit (EXIT_FAILURE);
163   }
164
165   /* See: https://bugzilla.redhat.com/show_bug.cgi?id=558593 */
166   const char *hostcpu = outputs[-1];
167
168   /* Output files. */
169   const char *kernel = NULL, *initrd = NULL, *appliance = NULL;
170   if (nr_outputs > 0)
171     kernel = outputs[0];
172   if (nr_outputs > 1)
173     initrd = appliance = outputs[1];
174   if (nr_outputs > 2)
175     appliance = outputs[2];
176
177   if (verbose) {
178     print_timestamped_message ("whitelist = %s, "
179                                "host_cpu = %s, "
180                                "kernel = %s, "
181                                "initrd = %s, "
182                                "appliance = %s",
183                                whitelist ? : "(not specified)",
184                                hostcpu, kernel, initrd, appliance);
185     int i;
186     for (i = 0; i < nr_inputs; ++i)
187       print_timestamped_message ("inputs[%d] = %s", i, inputs[i]);
188   }
189
190   /* Remove the output files if they exist. */
191   if (kernel)
192     unlink (kernel);
193   if (initrd)
194     unlink (initrd);
195   if (appliance && initrd != appliance)
196     unlink (appliance);
197
198   /* Create kernel output file. */
199   const char *modpath = create_kernel (hostcpu, kernel);
200
201   if (verbose)
202     print_timestamped_message ("finished creating kernel");
203
204   /* Create the appliance. */
205   create_appliance (hostcpu, inputs, nr_inputs, whitelist, modpath,
206                     initrd, appliance, writer);
207
208   if (verbose)
209     print_timestamped_message ("finished creating appliance");
210
211   exit (EXIT_SUCCESS);
212 }