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