Don't require external insmod.static.
[febootstrap.git] / helper / init.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 /* This very minimal init "script" goes in the mini-initrd used to
20  * boot the ext2-based appliance.  Note we have no shell, so we cannot
21  * use system(3) to run external commands.  In fact, we don't have
22  * very much at all, except this program, and some kernel modules.
23  */
24
25 #include <config.h>
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 #include <unistd.h>
31 #include <errno.h>
32 #include <fcntl.h>
33 #include <dirent.h>
34 #include <sys/types.h>
35 #include <sys/mount.h>
36 #include <sys/stat.h>
37 #include <sys/wait.h>
38
39 #include <asm/unistd.h>
40
41 extern long init_module (void *, unsigned long, const char *);
42
43 /* Leave this enabled for now.  When we get more confident in the boot
44  * process we can turn this off or make it configurable.
45  */
46 #define verbose 1
47
48 static void print_uptime (void);
49 static void insmod (const char *filename);
50 static void show_directory (const char *dir);
51
52 static char line[1024];
53
54 int
55 main ()
56 {
57   print_uptime ();
58   fprintf (stderr, "febootstrap: ext2 mini initrd starting up\n");
59
60   /* Create some fixed directories. */
61   mkdir ("/dev", 0755);
62   mkdir ("/root", 0755);
63   mkdir ("/sys", 0755);
64
65   /* Mount /sys. */
66   if (verbose)
67     fprintf (stderr, "febootstrap: mounting /sys\n");
68   if (mount ("sysfs", "/sys", "sysfs", 0, "") == -1) {
69     perror ("mount: /sys");
70     exit (EXIT_FAILURE);
71   }
72
73   FILE *fp = fopen ("/modules", "r");
74   if (fp == NULL) {
75     perror ("fopen: /modules");
76     exit (EXIT_FAILURE);
77   }
78   while (fgets (line, sizeof line, fp)) {
79     size_t n = strlen (line);
80     if (n > 0 && line[n-1] == '\n')
81       line[--n] = '\0';
82
83     /* XXX Because of the way we construct the module list, the
84      * "modules" file can contain non-existent modules.  Ignore those
85      * for now.  Really we should add them as missing dependencies.
86      * See ext2initrd.c:ext2_make_initrd().
87      */
88     if (access (line, R_OK) == 0)
89       insmod (line);
90     else
91       fprintf (stderr, "skipped %s, module is missing\n", line);
92   }
93   fclose (fp);
94
95   /* Look for the ext2 filesystem device.  It's always the last
96    * one that was added.
97    * XXX More than 25 devices?
98    */
99   char path[] = "/sys/block/xdx/dev";
100   char class[3] = { 'v', 's', 'h' };
101   size_t i, j;
102   fp = NULL;
103   for (i = 0; i < sizeof class; ++i) {
104     for (j = 'z'; j >= 'a'; --j) {
105       path[11] = class[i];
106       path[13] = j;
107       fp = fopen (path, "r");
108       if (fp != NULL)
109         goto found;
110     }
111   }
112   fprintf (stderr,
113            "febootstrap: no ext2 root device found\n"
114            "Please include FULL verbose output in your bug report.\n");
115   exit (EXIT_FAILURE);
116
117  found:
118   if (verbose)
119     fprintf (stderr, "febootstrap: picked %s as root device\n", path);
120
121   fgets (line, sizeof line, fp);
122   int major = atoi (line);
123   char *p = line + strcspn (line, ":") + 1;
124   int minor = atoi (p);
125
126   fclose (fp);
127   if (umount ("/sys") == -1) {
128     perror ("umount: /sys");
129     exit (EXIT_FAILURE);
130   }
131
132   if (verbose)
133     fprintf (stderr, "febootstrap: creating /dev/root as block special %d:%d\n",
134              major, minor);
135
136   if (mknod ("/dev/root", S_IFBLK|0700, makedev (major, minor)) == -1) {
137     perror ("mknod: /dev/root");
138     exit (EXIT_FAILURE);
139   }
140
141   /* Mount new root and chroot to it. */
142   if (verbose)
143     fprintf (stderr, "febootstrap: mounting new root on /root\n");
144   if (mount ("/dev/root", "/root", "ext2", MS_NOATIME, "") == -1) {
145     perror ("mount: /root");
146     exit (EXIT_FAILURE);
147   }
148
149   /* Note that pivot_root won't work.  See the note in
150    * Documentation/filesystems/ramfs-rootfs-initramfs.txt
151    * We could remove the old initramfs files, but let's not bother.
152    */
153   if (verbose)
154     fprintf (stderr, "febootstrap: chroot\n");
155
156   if (chroot ("/root") == -1) {
157     perror ("chroot: /root");
158     exit (EXIT_FAILURE);
159   }
160
161   chdir ("/");
162
163   /* Run /init from ext2 filesystem. */
164   print_uptime ();
165   execl ("/init", "init", NULL);
166   perror ("execl: /init");
167
168   /* /init failed to execute, but why?  Before we ditch, print some
169    * debug.  Although we have a full appliance, the fact that /init
170    * failed to run means we may not be able to run any commands.
171    */
172   show_directory ("/");
173   show_directory ("/bin");
174   show_directory ("/lib");
175   show_directory ("/lib64");
176   fflush (stderr);
177
178   exit (EXIT_FAILURE);
179 }
180
181 static void
182 insmod (const char *filename)
183 {
184   if (verbose)
185     fprintf (stderr, "febootstrap: internal insmod %s\n", filename);
186
187   int fd = open (filename, O_RDONLY);
188   if (fd == -1) {
189     fprintf (stderr, "insmod: open: %s: %m\n", filename);
190     exit (EXIT_FAILURE);
191   }
192   struct stat st;
193   if (fstat (fd, &st) == -1) {
194     perror ("insmod: fstat");
195     exit (EXIT_FAILURE);
196   }
197   char buf[st.st_size];
198   long offset = 0;
199   do {
200     long rc = read (fd, buf + offset, st.st_size - offset);
201     if (rc == -1) {
202       perror ("insmod: read");
203       exit (EXIT_FAILURE);
204     }
205     offset += rc;
206   } while (offset < st.st_size);
207   close (fd);
208
209   if (init_module (buf, st.st_size, "") != 0) {
210     fprintf (stderr, "insmod: init_module: %s: %m\n", filename);
211     /* However ignore the error because this can just happen because
212      * of a missing device.
213      */
214   }
215 }
216
217 /* Print contents of /proc/uptime. */
218 static void
219 print_uptime (void)
220 {
221   FILE *fp = fopen ("/proc/uptime", "r");
222   if (fp == NULL) {
223     perror ("/proc/uptime");
224     return;
225   }
226
227   fgets (line, sizeof line, fp);
228   fclose (fp);
229
230   fprintf (stderr, "febootstrap: uptime: %s", line);
231 }
232
233 /* Display a directory on stderr.  This is used for debugging only. */
234 static char
235 dirtype (int dt)
236 {
237   switch (dt) {
238   case DT_BLK: return 'b';
239   case DT_CHR: return 'c';
240   case DT_DIR: return 'd';
241   case DT_FIFO: return 'p';
242   case DT_LNK: return 'l';
243   case DT_REG: return '-';
244   case DT_SOCK: return 's';
245   case DT_UNKNOWN: return 'u';
246   default: return '?';
247   }
248 }
249
250 static void
251 show_directory (const char *dirname)
252 {
253   DIR *dir;
254   struct dirent *d;
255   struct stat statbuf;
256   char link[PATH_MAX+1];
257   ssize_t n;
258
259   fprintf (stderr, "febootstrap: debug: listing directory %s\n", dirname);
260
261   if (chdir (dirname) == -1) {
262     perror (dirname);
263     return;
264   }
265
266   dir = opendir (".");
267   if (!dir) {
268     perror (dirname);
269     chdir ("/");
270     return;
271   }
272
273   while ((d = readdir (dir)) != NULL) {
274     fprintf (stderr, "%5d %c %-16s", d->d_ino, dirtype (d->d_type), d->d_name);
275     if (lstat (d->d_name, &statbuf) >= 0) {
276       fprintf (stderr, " %06o %d %d:%d",
277                statbuf.st_mode, statbuf.st_size,
278                statbuf.st_uid, statbuf.st_gid);
279       if (S_ISLNK (statbuf.st_mode)) {
280         n = readlink (d->d_name, link, PATH_MAX);
281         if (n >= 0) {
282           link[n] = '\0';
283           fprintf (stderr, " -> %s", link);
284         }
285       }
286     }
287     fprintf (stderr, "\n");
288   }
289
290   closedir (dir);
291   chdir ("/");
292 }