diff --git a/AUTHORS b/AUTHORS index c6843486b2..93fb841271 100644 --- a/AUTHORS +++ b/AUTHORS @@ -29,6 +29,8 @@ AUTHORS (merged projects & commands): setarch: Elliot Lee Jindrich Novy simpleinit: Richard Gooch + switch_root: Peter Jones + Jeremy Katz CONTRIBUTORS: diff --git a/po/POTFILES.in b/po/POTFILES.in index 2ca79c60e4..334b8e5e7f 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -180,6 +180,7 @@ sys-utils/renice.c sys-utils/rtcwake.c sys-utils/setarch.c sys-utils/setsid.c +sys-utils/switch_root.c sys-utils/tunelp.c text-utils/col.c text-utils/colcrt.c diff --git a/sys-utils/.gitignore b/sys-utils/.gitignore index c47cd077a9..6f0be710f6 100644 --- a/sys-utils/.gitignore +++ b/sys-utils/.gitignore @@ -36,6 +36,7 @@ sparc32.8 sparc32bash.8 sparc64.8 sparc.8 +switch_root tunelp vidmode.8 x86_64.8 diff --git a/sys-utils/Makefile.am b/sys-utils/Makefile.am index b4c6ac601a..856d6a1f98 100644 --- a/sys-utils/Makefile.am +++ b/sys-utils/Makefile.am @@ -1,7 +1,7 @@ include $(top_srcdir)/config/include-Makefile.am bin_PROGRAMS = -sbin_PROGRAMS = +sbin_PROGRAMS = switch_root usrbinexec_PROGRAMS = flock ipcrm ipcs ipcmk renice setsid usrsbinexec_PROGRAMS = readprofile diff --git a/sys-utils/switch_root.c b/sys-utils/switch_root.c new file mode 100644 index 0000000000..9188006ebe --- /dev/null +++ b/sys-utils/switch_root.c @@ -0,0 +1,198 @@ +/* + * switchroot.c - switch to new root directory and start init. + * + * Copyright 2002-2008 Red Hat, Inc. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Authors: + * Peter Jones + * Jeremy Katz + */ + +#define _GNU_SOURCE 1 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifndef MS_MOVE +#define MS_MOVE 8192 +#endif + +#ifndef MNT_DETACH +#define MNT_DETACH 0x2 +#endif + +enum { + ok, + err_no_directory, + err_usage, +}; + +/* remove all files/directories below dirName -- don't cross mountpoints */ +static int +recursiveRemove(char * dirName) + { + struct stat sb,rb; + DIR * dir; + struct dirent * d; + char * strBuf = alloca(strlen(dirName) + 1024); + + if (!(dir = opendir(dirName))) { + printf("error opening %s: %m\n", dirName); + return 0; + } + + if (fstat(dirfd(dir),&rb)) { + printf("unable to stat %s: %m\n", dirName); + closedir(dir); + return 0; + } + + errno = 0; + while ((d = readdir(dir))) { + errno = 0; + + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) { + errno = 0; + continue; + } + + strcpy(strBuf, dirName); + strcat(strBuf, "/"); + strcat(strBuf, d->d_name); + + if (lstat(strBuf, &sb)) { + printf("failed to stat %s: %m\n", strBuf); + errno = 0; + continue; + } + + /* only descend into subdirectories if device is same as dir */ + if (S_ISDIR(sb.st_mode)) { + if (sb.st_dev == rb.st_dev) { + recursiveRemove(strBuf); + if (rmdir(strBuf)) + printf("failed to rmdir %s: %m\n", strBuf); + } + errno = 0; + continue; + } + if (unlink(strBuf)) { + printf("failed to remove %s: %m\n", strBuf); + errno = 0; + continue; + } + } + + if (errno) { + closedir(dir); + printf("error reading from %s: %m\n", dirName); + return 1; + } + + closedir(dir); + + return 0; + } + +static int switchroot(const char *newroot) +{ + /* Don't try to unmount the old "/", there's no way to do it. */ + const char *umounts[] = { "/dev", "/proc", "/sys", NULL }; + int errnum; + int i; + + for (i = 0; umounts[i] != NULL; i++) { + char newmount[PATH_MAX]; + strcpy(newmount, newroot); + strcat(newmount, umounts[i]); + if (mount(umounts[i], newmount, NULL, MS_MOVE, NULL) < 0) { + fprintf(stderr, "Error mount moving old %s %s %m\n", + umounts[i], newmount); + fprintf(stderr, "Forcing unmount of %s\n", umounts[i]); + umount2(umounts[i], MNT_FORCE); + } + } + + if (chdir(newroot) < 0) { + errnum=errno; + fprintf(stderr, "switchroot: chdir failed: %m\n"); + errno=errnum; + return -1; + } + recursiveRemove("/"); + if (mount(newroot, "/", NULL, MS_MOVE, NULL) < 0) { + errnum = errno; + fprintf(stderr, "switchroot: mount failed: %m\n"); + errno = errnum; + return -1; + } + + if (chroot(".")) { + errnum = errno; + fprintf(stderr, "switchroot: chroot failed: %m\n"); + errno = errnum; + return -2; + } + return 1; +} + +static void usage(FILE *output) +{ + fprintf(output, "usage: switchroot \n"); + if (output == stderr) + exit(err_usage); + exit(ok); +} + +int main(int argc, char *argv[]) +{ + char *newroot = argv[1]; + char *init = argv[2]; + char **initargs = &argv[2]; + + if (newroot == NULL || newroot[0] == '\0' || + init == NULL || init[0] == '\0' ) { + usage(stderr); + } + + if (switchroot(newroot) < 0) { + fprintf(stderr, "switchroot has failed. Sorry.\n"); + return 1; + } + if (access(initargs[0], X_OK)) + fprintf(stderr, "WARNING: can't access %s\n", initargs[0]); + + /* get session leader */ + setsid(); + /* set controlling terminal */ + ioctl (0, TIOCSCTTY, 1); + + execv(initargs[0], initargs); +} + +/* + * vim:noet:ts=8:sw=8:sts=8 + */