]> git.sur5r.net Git - bacula/rescue/blob - rescue/linux/cdrom/yaird-0.0.5/exec/run_init.c
dbb92c5052d2e223f8d0056c44523bc54c17ed04
[bacula/rescue] / rescue / linux / cdrom / yaird-0.0.5 / exec / run_init.c
1 #ident "$Id$"
2 /* ----------------------------------------------------------------------- *
3  *   
4  *   Copyright 2004 H. Peter Anvin - All Rights Reserved
5  *
6  *   Permission is hereby granted, free of charge, to any person
7  *   obtaining a copy of this software and associated documentation
8  *   files (the "Software"), to deal in the Software without
9  *   restriction, including without limitation the rights to use,
10  *   copy, modify, merge, publish, distribute, sublicense, and/or
11  *   sell copies of the Software, and to permit persons to whom
12  *   the Software is furnished to do so, subject to the following
13  *   conditions:
14  *   
15  *   The above copyright notice and this permission notice shall
16  *   be included in all copies or substantial portions of the Software.
17  *   
18  *   THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19  *   EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
20  *   OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21  *   NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
22  *   HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
23  *   WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24  *   FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25  *   OTHER DEALINGS IN THE SOFTWARE.
26  *
27  * ----------------------------------------------------------------------- */
28
29 /*
30  * run-init.c
31  *
32  * Usage: exec run-init [-c /dev/console] /real-root /sbin/init "$@"
33  *
34  * This program should be called as the last thing in a shell script
35  * acting as /init in an initramfs; it does the following:
36  *
37  * - Delete all files in the initramfs;
38  * - Remounts /real-root onto the root filesystem;
39  * - Chroots;
40  * - Opens /dev/console;
41  * - Spawns the specified init program (with arguments.)
42  */
43
44 #include <alloca.h>
45 #include <assert.h>
46 #include <dirent.h>
47 #include <errno.h>
48 #include <fcntl.h>
49 #include <string.h>
50 #include <stdlib.h>
51 #include <stdio.h>
52 #include <unistd.h>
53 #include <sys/mount.h>
54 #include <sys/stat.h>
55 #include <sys/types.h>
56 #include <sys/vfs.h>
57
58 #include <config.h>
59
60
61 /* Make it possible to compile on glibc by including constants that the
62    always-behind shipped glibc headers may not include.  Classic example
63    on why the lack of ABI headers screw us up. */
64 #ifndef TMPFS_MAGIC
65 # define TMPFS_MAGIC    0x01021994
66 #endif
67 #ifndef RAMFS_MAGIC
68 # define RAMFS_MAGIC    0x858458f6
69 #endif
70 #ifndef MS_MOVE
71 # define MS_MOVE        8192
72 #endif
73
74 static const char *program;
75
76 static void __attribute__((noreturn)) die(const char *msg)
77 {
78   fprintf(stderr, "%s: %s: %s\n", program, msg, strerror(errno));
79   exit(1);
80 }
81
82
83 #ifdef ENABLE_NUKE
84 static int nuke(const char *what);
85
86 static int nuke_dirent(int len, const char *dir, const char *name, dev_t me)
87 {
88   int bytes = len+strlen(name)+2;
89   char path[bytes];
90   int xlen;
91   struct stat st;
92
93   xlen = snprintf(path, bytes, "%s/%s", dir, name);
94   assert(xlen < bytes);
95
96   if ( lstat(path, &st) )
97     return ENOENT;              /* Return 0 since already gone? */
98
99   if ( st.st_dev != me )
100     return 0;                   /* DO NOT recurse down mount points!!!!! */
101
102   return nuke(path);
103 }
104
105 /* Wipe the contents of a directory, but not the directory itself */
106 static int nuke_dir(const char *what)
107 {
108   int len = strlen(what);
109   DIR *dir;
110   struct dirent *d;
111   int err = 0;
112   struct stat st;
113
114   if ( lstat(what, &st) )
115     return errno;
116   
117   if ( !S_ISDIR(st.st_mode) )
118     return ENOTDIR;
119
120   if ( !(dir = opendir(what)) ) {
121     /* EACCES means we can't read it.  Might be empty and removable;
122        if not, the rmdir() in nuke() will trigger an error. */
123     return (errno == EACCES) ? 0 : errno;
124   }
125   
126   while ( (d = readdir(dir)) ) {
127     /* Skip . and .. */
128     if ( d->d_name[0] == '.' &&
129          (d->d_name[1] == '\0' ||
130           (d->d_name[1] == '.' && d->d_name[2] == '\0')) )
131       continue;
132     
133     err = nuke_dirent(len, what, d->d_name, st.st_dev);
134     if ( err ) {
135       closedir(dir);
136       return err;
137     }
138   }
139   
140   closedir(dir);
141
142   return 0;
143 }
144
145 static int nuke(const char *what)
146 {
147   int rv;
148   int err = 0;
149
150   rv = unlink(what);
151   if ( rv < 0 ) {
152     if ( errno == EISDIR ) {
153       /* It's a directory. */
154       err = nuke_dir(what);
155       if ( !err ) err = rmdir(what) ? errno : err;
156     } else {
157       err = errno;
158     }
159   }
160
161   if ( err ) {
162     errno = err;
163     die(what);
164   } else {
165     return 0;
166   }
167 }
168 #else /* no ENABLE_NUKE */
169 static inline int
170 nuke_dir(const char *what)
171 {
172         return 0;
173 }
174 #endif /* ENABLE_NUKE */
175
176 static void __attribute__((noreturn)) usage(void)
177 {
178   fprintf(stderr, "Usage: exec %s [-c consoledev] /real-root /sbin/init [args]\n", program);
179   exit(1);
180 }
181
182
183 int main(int argc, char *argv[])
184 {
185   struct stat   rst, cst, ist;
186   struct statfs sfs;
187   int o;
188   int confd;
189
190   /* Command-line options and defaults */
191   const char *console = "/dev/console";
192   const char *realroot;
193   const char *init;
194   char **initargs;
195
196   /* First, parse the command line */
197   program = argv[0];
198
199   while ( (o = getopt(argc, argv, "c:")) != -1 ) {
200     if ( o == 'c' ) {
201       console = optarg;
202     } else {
203       usage();
204     }
205   }
206
207   if ( argc-optind < 2 )
208     usage();
209
210   realroot = argv[optind];
211   init     = argv[optind+1];
212   initargs = argv+optind+1;
213
214   /* First, change to the new root directory */
215   if ( chdir(realroot) )
216     die("chdir to new root");
217
218   /* This is a potentially highly destructive program.  Take some
219      extra precautions. */
220
221   /* Make sure the current directory is not on the same filesystem
222      as the root directory */
223   if ( stat("/", &rst) || stat(".", &cst) )
224     die("stat");
225
226   if ( rst.st_dev == cst.st_dev )
227     die("current directory on the same filesystem as the root");
228   
229   /* The initramfs should have /init */
230   if ( stat("/init", &ist) || !S_ISREG(ist.st_mode) )
231     die("can't find /init on initramfs");
232
233   /* Make sure we're on a ramfs */
234   if ( statfs("/", &sfs) )
235     die("statfs /");
236   if ( sfs.f_type != RAMFS_MAGIC && sfs.f_type != TMPFS_MAGIC )
237     die("rootfs not a ramfs or tmpfs");
238
239   /* Okay, I think we should be safe... */
240
241   /* Delete rootfs contents */
242   if ( nuke_dir("/") )
243     die("nuking initramfs contents");
244
245   /* Overmount the root */
246   if ( mount(".", "/", NULL, MS_MOVE, NULL) )
247     die("overmounting root");
248   
249   /* chroot, chdir */
250   if ( chroot(".") || chdir("/") )
251     die("chroot");
252
253   /* Open /dev/console */
254   if ( (confd = open(console, O_RDWR)) < 0 )
255     die("opening console");
256   dup2(confd, 0);
257   dup2(confd, 1);
258   dup2(confd, 2);
259   close(confd);
260
261   /* Spawn init */
262   execv(init, initargs);
263   die(init);                    /* Failed to spawn init */
264 }