06a7aa77308ef9329af635bee5f55887a3589304
[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   /* A perennial problem is that /sbin/insmod.static is not
68    * executable.  Just make it executable.  It's easier than fixing
69    * everyone's distro.
70    */
71   chmod ("/sbin/insmod.static", 0755);
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     insmod (line);
83   }
84   fclose (fp);
85
86   /* Look for the ext2 filesystem device.  It's always the last
87    * one that was added.
88    * XXX More than 25 devices?
89    */
90   char path[] = "/sys/block/xdx/dev";
91   char class[3] = { 'v', 's', 'h' };
92   size_t i, j;
93   fp = NULL;
94   for (i = 0; i < sizeof class; ++i) {
95     for (j = 'z'; j >= 'a'; --j) {
96       path[11] = class[i];
97       path[13] = j;
98       fp = fopen (path, "r");
99       if (fp != NULL)
100         goto found;
101     }
102   }
103   fprintf (stderr,
104            "febootstrap: no ext2 root device found\n"
105            "Please include FULL verbose output in your bug report.\n");
106   exit (EXIT_FAILURE);
107
108  found:
109   if (verbose)
110     fprintf (stderr, "febootstrap: picked %s as root device\n", path);
111
112   fgets (line, sizeof line, fp);
113   int major = atoi (line);
114   char *p = line + strcspn (line, ":") + 1;
115   int minor = atoi (p);
116
117   fclose (fp);
118   if (umount ("/sys") == -1) {
119     perror ("umount: /sys");
120     exit (EXIT_FAILURE);
121   }
122
123   if (verbose)
124     fprintf (stderr, "febootstrap: creating /dev/root as block special %d:%d\n",
125              major, minor);
126
127   if (mknod ("/dev/root", S_IFBLK|0700, makedev (major, minor)) == -1) {
128     perror ("mknod: /dev/root");
129     exit (EXIT_FAILURE);
130   }
131
132   /* Mount new root and chroot to it. */
133   if (verbose)
134     fprintf (stderr, "febootstrap: mounting new root on /root\n");
135   if (mount ("/dev/root", "/root", "ext2", MS_NOATIME, "") == -1) {
136     perror ("mount: /root");
137     exit (EXIT_FAILURE);
138   }
139
140   /* Note that pivot_root won't work.  See the note in
141    * Documentation/filesystems/ramfs-rootfs-initramfs.txt
142    * We could remove the old initramfs files, but let's not bother.
143    */
144   if (verbose)
145     fprintf (stderr, "febootstrap: chroot\n");
146
147   if (chroot ("/root") == -1) {
148     perror ("chroot: /root");
149     exit (EXIT_FAILURE);
150   }
151
152   chdir ("/");
153
154   /* Run /init from ext2 filesystem. */
155   print_uptime ();
156   execl ("/init", "init", NULL);
157   perror ("execl: /init");
158   exit (EXIT_FAILURE);
159 }
160
161 static void
162 insmod (const char *filename)
163 {
164   if (verbose)
165     fprintf (stderr, "febootstrap: insmod %s\n", filename);
166
167   pid_t pid = fork ();
168   if (pid == -1) {
169     perror ("insmod: fork");
170     exit (EXIT_FAILURE);
171   }
172
173   if (pid == 0) { /* Child. */
174     execl ("/insmod.static", "insmod.static", filename, NULL);
175     perror ("insmod: execl");
176     _exit (EXIT_FAILURE);
177   }
178
179   /* Parent. */
180   int status;
181   if (wait (&status) == -1 ||
182       WEXITSTATUS (status) != 0)
183     perror ("insmod: wait");
184     /* but ignore the error, some will be because the device is not found */
185 }
186
187 /* Print contents of /proc/uptime. */
188 static void
189 print_uptime (void)
190 {
191   FILE *fp = fopen ("/proc/uptime", "r");
192   if (fp == NULL) {
193     perror ("/proc/uptime");
194     return;
195   }
196
197   fgets (line, sizeof line, fp);
198   fclose (fp);
199
200   fprintf (stderr, "febootstrap: uptime: %s", line);
201 }