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