Rewrite febootstrap as a general supermin appliance building tool.
[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, insmod.static, and some
23  * kernel modules.
24  */
25
26 #include <config.h>
27
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <unistd.h>
32 #include <errno.h>
33 #include <sys/types.h>
34 #include <sys/mount.h>
35 #include <sys/stat.h>
36 #include <sys/wait.h>
37
38 /* Leave this enabled for now.  When we get more confident in the boot
39  * process we can turn this off or make it configurable.
40  */
41 #define verbose 1
42
43 static void print_uptime (void);
44 static void insmod (const char *filename);
45
46 static char line[1024];
47
48 int
49 main ()
50 {
51   print_uptime ();
52   fprintf (stderr, "febootstrap: ext2 mini initrd starting up\n");
53
54   /* Create some fixed directories. */
55   mkdir ("/dev", 0755);
56   mkdir ("/root", 0755);
57   mkdir ("/sys", 0755);
58
59   /* Mount /sys. */
60   if (verbose)
61     fprintf (stderr, "febootstrap: mounting /sys\n");
62   if (mount ("sysfs", "/sys", "sysfs", 0, "") == -1) {
63     perror ("mount: /sys");
64     exit (EXIT_FAILURE);
65   }
66
67   FILE *fp = fopen ("/modules", "r");
68   if (fp == NULL) {
69     perror ("fopen: /modules");
70     exit (EXIT_FAILURE);
71   }
72   while (fgets (line, sizeof line, fp)) {
73     size_t n = strlen (line);
74     if (n > 0 && line[n-1] == '\n')
75       line[--n] = '\0';
76     insmod (line);
77   }
78   fclose (fp);
79
80   /* Look for the ext2 filesystem device.  It's always the last
81    * one that was added.
82    * XXX More than 25 devices?
83    */
84   char path[] = "/sys/block/xdx/dev";
85   char class[3] = { 'v', 's', 'h' };
86   size_t i, j;
87   fp = NULL;
88   for (i = 0; i < sizeof class; ++i) {
89     for (j = 'z'; j >= 'a'; --j) {
90       path[11] = class[i];
91       path[13] = j;
92       fp = fopen (path, "r");
93       if (fp != NULL)
94         goto found;
95     }
96   }
97   fprintf (stderr,
98            "febootstrap: no ext2 root device found\n"
99            "Please include FULL verbose output in your bug report.\n");
100   exit (EXIT_FAILURE);
101
102  found:
103   if (verbose)
104     fprintf (stderr, "febootstrap: picked %s as root device\n", path);
105
106   fgets (line, sizeof line, fp);
107   int major = atoi (line);
108   char *p = line + strcspn (line, ":") + 1;
109   int minor = atoi (p);
110
111   fclose (fp);
112   if (umount ("/sys") == -1) {
113     perror ("umount: /sys");
114     exit (EXIT_FAILURE);
115   }
116
117   if (verbose)
118     fprintf (stderr, "febootstrap: creating /dev/root as block special %d:%d\n",
119              major, minor);
120
121   if (mknod ("/dev/root", S_IFBLK|0700, makedev (major, minor)) == -1) {
122     perror ("mknod: /dev/root");
123     exit (EXIT_FAILURE);
124   }
125
126   /* Mount new root and chroot to it. */
127   if (verbose)
128     fprintf (stderr, "febootstrap: mounting new root on /root\n");
129   if (mount ("/dev/root", "/root", "ext2", MS_NOATIME, "") == -1) {
130     perror ("mount: /root");
131     exit (EXIT_FAILURE);
132   }
133
134   /* Note that pivot_root won't work.  See the note in
135    * Documentation/filesystems/ramfs-rootfs-initramfs.txt
136    * We could remove the old initramfs files, but let's not bother.
137    */
138   if (verbose)
139     fprintf (stderr, "febootstrap: chroot\n");
140
141   if (chroot ("/root") == -1) {
142     perror ("chroot: /root");
143     exit (EXIT_FAILURE);
144   }
145
146   chdir ("/");
147
148   /* Run /init from ext2 filesystem. */
149   print_uptime ();
150   execl ("/init", "init", NULL);
151   perror ("execl: /init");
152   exit (EXIT_FAILURE);
153 }
154
155 static void
156 insmod (const char *filename)
157 {
158   if (verbose)
159     fprintf (stderr, "febootstrap: insmod %s\n", filename);
160
161   pid_t pid = fork ();
162   if (pid == -1) {
163     perror ("insmod: fork");
164     exit (EXIT_FAILURE);
165   }
166
167   if (pid == 0) { /* Child. */
168     execl ("/insmod.static", "insmod.static", filename, NULL);
169     perror ("insmod: execl");
170     _exit (EXIT_FAILURE);
171   }
172
173   /* Parent. */
174   int status;
175   if (wait (&status) == -1 ||
176       WEXITSTATUS (status) != 0)
177     perror ("insmod: wait");
178     /* but ignore the error, some will be because the device is not found */
179 }
180
181 /* Print contents of /proc/uptime. */
182 static void
183 print_uptime (void)
184 {
185   FILE *fp = fopen ("/proc/uptime", "r");
186   if (fp == NULL) {
187     perror ("/proc/uptime");
188     return;
189   }
190
191   fgets (line, sizeof line, fp);
192   fclose (fp);
193
194   fprintf (stderr, "febootstrap: uptime: %s", line);
195 }