helper: Print /modules when verbose >= 2
[febootstrap.git] / helper / ext2initrd.c
1 /* febootstrap-supermin-helper reimplementation in C.
2  * Copyright (C) 2009-2011 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 /* ext2 requires a small initrd in order to boot.  This builds it. */
20
21 #include <config.h>
22
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <fcntl.h>
26 #include <unistd.h>
27 #include <dirent.h>
28 #include <errno.h>
29 #include <assert.h>
30 #include <fnmatch.h>
31
32 #include "error.h"
33 #include "full-write.h"
34 #include "xalloc.h"
35 #include "xvasprintf.h"
36
37 #include "helper.h"
38 #include "ext2internal.h"
39
40 static void read_module_deps (const char *modpath);
41 static void free_module_deps (void);
42 static void add_module_dep (const char *name, const char *dep);
43 static struct module * add_module (const char *name);
44 static struct module * find_module (const char *name);
45 static void print_module_load_order (FILE *f, FILE *pp, struct module *m);
46
47 /* The init binary. */
48 extern char _binary_init_start, _binary_init_end, _binary_init_size;
49
50 /* The list of modules (wildcards) we consider for inclusion in the
51  * mini initrd.  Only what is needed in order to find a device with an
52  * ext2 filesystem on it.
53  */
54 static const char *kmods[] = {
55   "ext2.ko*",
56   "ext4.ko*", /* CONFIG_EXT4_USE_FOR_EXT23=y option might be set */
57   "virtio*.ko*",
58   "ide*.ko*",
59   "libata*.ko*",
60   "piix*.ko*",
61   "scsi_transport_spi.ko*",
62   "scsi_mod.ko*",
63   "sd_mod.ko*",
64   "sym53c8xx.ko*",
65   "ata_piix.ko*",
66   "sr_mod.ko*",
67   "mbcache.ko*",
68   "crc*.ko*",
69   "libcrc*.ko*",
70   NULL
71 };
72
73 /* Module dependencies. */
74 struct module {
75   struct module *next;
76   struct moddep *deps;
77   char *name;
78   int visited;
79 };
80 struct module *modules = NULL;
81
82 struct moddep {
83   struct moddep *next;
84   struct module *dep;
85 };
86
87 void
88 ext2_make_initrd (const char *modpath, const char *initrd)
89 {
90   char dir[] = "/tmp/ext2initrdXXXXXX";
91   if (mkdtemp (dir) == NULL)
92     error (EXIT_FAILURE, errno, "mkdtemp");
93
94   read_module_deps (modpath);
95   add_module ("");
96   for (int i = 0; kmods[i] != NULL; ++i) {
97     for (struct module *m = modules; m; m = m->next) {
98       char *n = strrchr (m->name, '/');
99       if (n)
100         n += 1;
101       else
102         n = m->name;
103       if (fnmatch (kmods[i], n, FNM_PATHNAME) == 0) {
104         if (verbose >= 2)
105           fprintf (stderr, "Adding top-level dependency %s (%s)\n", m->name, kmods[i]);
106         add_module_dep ("", m->name);
107       }
108     }
109   }
110
111   char *cmd = xasprintf ("cd %s; xargs cp -t %s", modpath, dir);
112   char *outfile = xasprintf ("%s/modules", dir);
113   if (verbose >= 2) fprintf (stderr, "writing to %s\n", cmd);
114
115   FILE *f = fopen (outfile, "w");
116   if (f == NULL)
117     error (EXIT_FAILURE, errno, "failed to create modules list (%s)", outfile);
118   FILE *pp = popen (cmd, "w");
119   if (pp == NULL)
120     error (EXIT_FAILURE, errno, "failed to create pipe (%s)", cmd);
121
122   /* The "pseudo" module depends on all modules matched by the contents of kmods */
123   struct module *pseudo = find_module ("");
124   print_module_load_order (pp, f, pseudo);
125   fclose (pp);
126   pclose (f);
127
128   free (cmd);
129   free_module_deps ();
130
131   /* Copy in the init program, linked into this program as a data blob. */
132   char *init = xasprintf ("%s/init", dir);
133   int fd = open (init, O_WRONLY|O_TRUNC|O_CREAT|O_NOCTTY, 0755);
134   if (fd == -1)
135     error (EXIT_FAILURE, errno, "open: %s", init);
136
137   size_t n = (size_t) &_binary_init_size;
138   if (full_write (fd, &_binary_init_start, n) != n)
139     error (EXIT_FAILURE, errno, "write: %s", init);
140
141   if (close (fd) == -1)
142     error (EXIT_FAILURE, errno, "close: %s", init);
143
144   free (init);
145
146   /* Build the cpio file. */
147   cmd = xasprintf ("(cd %s && (echo . ; ls -1)"
148                    " | cpio --quiet -o -H newc) > '%s'",
149                    dir, initrd);
150   if (verbose >= 2) fprintf (stderr, "%s\n", cmd);
151   int r = system (cmd);
152   if (r == -1 || WEXITSTATUS (r) != 0)
153     error (EXIT_FAILURE, 0, "ext2_make_initrd: cpio failed");
154   free (cmd);
155
156   /* Construction of 'dir' above ensures this is safe. */
157   cmd = xasprintf ("rm -rf %s", dir);
158   if (verbose >= 2) fprintf (stderr, "%s\n", cmd);
159   system (cmd);
160   free (cmd);
161 }
162
163 static void
164 free_module_deps (void)
165 {
166   /* Short-lived program, don't bother to free it. */
167   modules = NULL;
168 }
169
170 /* Read modules.dep into internal structure. */
171 static void
172 read_module_deps (const char *modpath)
173 {
174   free_module_deps ();
175
176   char *filename = xasprintf ("%s/modules.dep", modpath);
177   FILE *fp = fopen (filename, "r");
178   if (fp == NULL)
179     error (EXIT_FAILURE, errno, "open: %s/modules.dep", modpath);
180
181   char *line = NULL;
182   size_t llen = 0;
183   ssize_t len;
184   while ((len = getline (&line, &llen, fp)) != -1) {
185     if (len > 0 && line[len-1] == '\n')
186       line[--len] = '\0';
187
188     char *name = strtok (line, ": ");
189     if (!name) continue;
190
191     add_module (name);
192     char *dep;
193     while ((dep = strtok (NULL, " ")) != NULL) {
194       add_module_dep (name, dep);
195     }
196   }
197
198   free (line);
199   fclose (fp);
200 }
201
202 static struct module *
203 add_module (const char *name)
204 {
205   struct module *m = find_module (name);
206   if (m)
207     return m;
208   m = xmalloc (sizeof *m);
209   m->name = xstrdup (name);
210   m->deps = NULL;
211   m->next = modules;
212   m->visited = 0;
213   modules = m;
214   return m;
215 }
216
217 static struct module *
218 find_module (const char *name)
219 {
220   struct module *m;
221   for (m = modules; m; m = m->next) {
222     if (strcmp (name, m->name) == 0)
223       break;
224   }
225   return m;
226 }
227
228 /* Module 'name' requires 'dep' to be loaded first. */
229 static void
230 add_module_dep (const char *name, const char *dep)
231 {
232   if (verbose >= 2) fprintf (stderr, "add_module_dep %s: %s\n", name, dep);
233   struct module *m1 = add_module (name);
234   struct module *m2 = add_module (dep);
235   struct moddep *d;
236   for (d = m1->deps; d; d = d->next) {
237     if (d->dep == m2)
238       return;
239   }
240   d = xmalloc (sizeof *d);
241   d->next = m1->deps;
242   d->dep = m2;
243   m1->deps = d;
244   return;
245 }
246
247 /* DFS on the dependency graph */
248 static void
249 print_module_load_order (FILE *pipe, FILE *list, struct module *m)
250 {
251   if (m->visited)
252     return;
253
254   for (struct moddep *d = m->deps; d; d = d->next)
255     print_module_load_order (pipe, list, d->dep);
256
257   if (m->name[0] == 0)
258     return;
259
260   char *basename = strrchr (m->name, '/');
261   if (basename)
262     ++basename;
263   else
264     basename = m->name;
265
266   fputs (m->name, pipe);
267   fputc ('\n', pipe);
268   fputs (basename, list);
269   fputc ('\n', list);
270   m->visited = 1;
271
272   if (verbose >= 2)
273     fprintf (stderr, "print_module_load_order: %s %s\n", m->name, basename);
274 }