2 /* ----------------------------------------------------------------------- *
4 * Copyright 2004 H. Peter Anvin - All Rights Reserved
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
15 * The above copyright notice and this permission notice shall
16 * be included in all copies or substantial portions of the Software.
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.
27 * ----------------------------------------------------------------------- */
32 * Usage: exec run-init [-c /dev/console] /real-root /sbin/init "$@"
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:
37 * - Delete all files in the initramfs;
38 * - Remounts /real-root onto the root filesystem;
40 * - Opens /dev/console;
41 * - Spawns the specified init program (with arguments.)
53 #include <sys/mount.h>
55 #include <sys/types.h>
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. */
65 # define TMPFS_MAGIC 0x01021994
68 # define RAMFS_MAGIC 0x858458f6
74 static const char *program;
76 static void __attribute__((noreturn)) die(const char *msg)
78 fprintf(stderr, "%s: %s: %s\n", program, msg, strerror(errno));
84 static int nuke(const char *what);
86 static int nuke_dirent(int len, const char *dir, const char *name, dev_t me)
88 int bytes = len+strlen(name)+2;
93 xlen = snprintf(path, bytes, "%s/%s", dir, name);
96 if ( lstat(path, &st) )
97 return ENOENT; /* Return 0 since already gone? */
99 if ( st.st_dev != me )
100 return 0; /* DO NOT recurse down mount points!!!!! */
105 /* Wipe the contents of a directory, but not the directory itself */
106 static int nuke_dir(const char *what)
108 int len = strlen(what);
114 if ( lstat(what, &st) )
117 if ( !S_ISDIR(st.st_mode) )
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;
126 while ( (d = readdir(dir)) ) {
128 if ( d->d_name[0] == '.' &&
129 (d->d_name[1] == '\0' ||
130 (d->d_name[1] == '.' && d->d_name[2] == '\0')) )
133 err = nuke_dirent(len, what, d->d_name, st.st_dev);
145 static int nuke(const char *what)
152 if ( errno == EISDIR ) {
153 /* It's a directory. */
154 err = nuke_dir(what);
155 if ( !err ) err = rmdir(what) ? errno : err;
168 #else /* no ENABLE_NUKE */
170 nuke_dir(const char *what)
174 #endif /* ENABLE_NUKE */
176 static void __attribute__((noreturn)) usage(void)
178 fprintf(stderr, "Usage: exec %s [-c consoledev] /real-root /sbin/init [args]\n", program);
183 int main(int argc, char *argv[])
185 struct stat rst, cst, ist;
190 /* Command-line options and defaults */
191 const char *console = "/dev/console";
192 const char *realroot;
196 /* First, parse the command line */
199 while ( (o = getopt(argc, argv, "c:")) != -1 ) {
207 if ( argc-optind < 2 )
210 realroot = argv[optind];
211 init = argv[optind+1];
212 initargs = argv+optind+1;
214 /* First, change to the new root directory */
215 if ( chdir(realroot) )
216 die("chdir to new root");
218 /* This is a potentially highly destructive program. Take some
219 extra precautions. */
221 /* Make sure the current directory is not on the same filesystem
222 as the root directory */
223 if ( stat("/", &rst) || stat(".", &cst) )
226 if ( rst.st_dev == cst.st_dev )
227 die("current directory on the same filesystem as the root");
229 /* The initramfs should have /init */
230 if ( stat("/init", &ist) || !S_ISREG(ist.st_mode) )
231 die("can't find /init on initramfs");
233 /* Make sure we're on a ramfs */
234 if ( statfs("/", &sfs) )
236 if ( sfs.f_type != RAMFS_MAGIC && sfs.f_type != TMPFS_MAGIC )
237 die("rootfs not a ramfs or tmpfs");
239 /* Okay, I think we should be safe... */
241 /* Delete rootfs contents */
243 die("nuking initramfs contents");
245 /* Overmount the root */
246 if ( mount(".", "/", NULL, MS_MOVE, NULL) )
247 die("overmounting root");
250 if ( chroot(".") || chdir("/") )
253 /* Open /dev/console */
254 if ( (confd = open(console, O_RDWR)) < 0 )
255 die("opening console");
262 execv(init, initargs);
263 die(init); /* Failed to spawn init */