]>
Commit | Line | Data |
---|---|---|
41c0d0ca MT |
1 | /* |
2 | * switchroot.c - switch to new root directory and start init. | |
3 | * | |
4 | * Copyright 2002-2008 Red Hat, Inc. All rights reserved. | |
5 | * | |
6 | * This program is free software; you can redistribute it and/or modify | |
7 | * it under the terms of the GNU General Public License as published by | |
8 | * the Free Software Foundation; either version 2 of the License, or | |
9 | * (at your option) any later version. | |
10 | * | |
11 | * This program is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
14 | * GNU General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public License | |
17 | * along with this program. If not, see <http://www.gnu.org/licenses/>. | |
18 | * | |
19 | * Authors: | |
20 | * Peter Jones <pjones@redhat.com> | |
21 | * Jeremy Katz <katzj@redhat.com> | |
22 | */ | |
23 | #include <sys/mount.h> | |
24 | #include <sys/types.h> | |
25 | #include <sys/stat.h> | |
26 | #include <sys/param.h> | |
27 | #include <fcntl.h> | |
28 | #include <stdio.h> | |
29 | #include <stdlib.h> | |
30 | #include <unistd.h> | |
31 | #include <string.h> | |
32 | #include <errno.h> | |
33 | #include <ctype.h> | |
34 | #include <dirent.h> | |
35 | ||
36 | #ifndef MS_MOVE | |
37 | #define MS_MOVE 8192 | |
38 | #endif | |
39 | ||
40 | enum { | |
41 | ok, | |
42 | err_no_directory, | |
43 | err_usage, | |
44 | }; | |
45 | ||
46 | /* remove all files/directories below dirName -- don't cross mountpoints */ | |
47 | static int | |
48 | recursiveRemove(char * dirName) | |
49 | { | |
50 | struct stat sb,rb; | |
51 | DIR * dir; | |
52 | struct dirent * d; | |
53 | char * strBuf = alloca(strlen(dirName) + 1024); | |
54 | ||
55 | if (!(dir = opendir(dirName))) { | |
56 | printf("error opening %s: %m\n", dirName); | |
57 | return 0; | |
58 | } | |
59 | ||
60 | if (fstat(dirfd(dir),&rb)) { | |
61 | printf("unable to stat %s: %m\n", dirName); | |
62 | closedir(dir); | |
63 | return 0; | |
64 | } | |
65 | ||
66 | errno = 0; | |
67 | ||
68 | while ((d = readdir(dir))) { | |
69 | errno = 0; | |
70 | ||
71 | if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) { | |
72 | errno = 0; | |
73 | continue; | |
74 | } | |
75 | ||
76 | strcpy(strBuf, dirName); | |
77 | strcat(strBuf, "/"); | |
78 | strcat(strBuf, d->d_name); | |
79 | ||
80 | if (lstat(strBuf, &sb)) { | |
81 | printf("failed to stat %s: %m\n", strBuf); | |
82 | errno = 0; | |
83 | continue; | |
84 | } | |
85 | ||
86 | /* only descend into subdirectories if device is same as dir */ | |
87 | if (S_ISDIR(sb.st_mode)) { | |
88 | if (sb.st_dev == rb.st_dev) { | |
89 | recursiveRemove(strBuf); | |
90 | if (rmdir(strBuf)) | |
91 | printf("failed to rmdir %s: %m\n", strBuf); | |
92 | } | |
93 | errno = 0; | |
94 | continue; | |
95 | } | |
96 | if (unlink(strBuf)) { | |
97 | printf("failed to remove %s: %m\n", strBuf); | |
98 | errno = 0; | |
99 | continue; | |
100 | } | |
101 | } | |
102 | ||
103 | if (errno) { | |
104 | closedir(dir); | |
105 | printf("error reading from %s: %m\n", dirName); | |
106 | return 1; | |
107 | } | |
108 | ||
109 | closedir(dir); | |
110 | return 0; | |
111 | } | |
112 | ||
113 | static int switchroot(const char *newroot) | |
114 | { | |
115 | /* Don't try to unmount the old "/", there's no way to do it. */ | |
116 | const char *umounts[] = { "/dev", "/proc", "/sys", NULL }; | |
117 | int errnum; | |
118 | int i; | |
119 | ||
120 | for (i = 0; umounts[i] != NULL; i++) { | |
121 | char newmount[PATH_MAX]; | |
122 | strcpy(newmount, newroot); | |
123 | strcat(newmount, umounts[i]); | |
124 | if (mount(umounts[i], newmount, NULL, MS_MOVE, NULL) < 0) { | |
125 | fprintf(stderr, "Error mount moving old %s %s %m\n", | |
126 | umounts[i], newmount); | |
127 | fprintf(stderr, "Forcing unmount of %s\n", umounts[i]); | |
128 | umount2(umounts[i], MNT_FORCE); | |
129 | } | |
130 | } | |
131 | ||
132 | if (chdir(newroot) < 0) { | |
133 | errnum=errno; | |
134 | fprintf(stderr, "switchroot: chdir failed: %m\n"); | |
135 | errno=errnum; | |
136 | return -1; | |
137 | } | |
138 | recursiveRemove("/"); | |
139 | if (mount(newroot, "/", NULL, MS_MOVE, NULL) < 0) { | |
140 | errnum = errno; | |
141 | fprintf(stderr, "switchroot: mount failed: %m\n"); | |
142 | errno = errnum; | |
143 | return -1; | |
144 | } | |
145 | ||
146 | if (chroot(".")) { | |
147 | errnum = errno; | |
148 | fprintf(stderr, "switchroot: chroot failed: %m\n"); | |
149 | errno = errnum; | |
150 | return -2; | |
151 | } | |
152 | return 1; | |
153 | } | |
154 | ||
155 | static void usage(FILE *output) | |
156 | { | |
157 | fprintf(output, "usage: switchroot <newrootdir> <init> <args to init>\n"); | |
158 | if (output == stderr) | |
159 | exit(err_usage); | |
160 | exit(ok); | |
161 | } | |
162 | ||
163 | int main(int argc, char *argv[]) | |
164 | { | |
165 | char *newroot = argv[1]; | |
166 | char *init = argv[2]; | |
167 | char **initargs = &argv[2]; | |
168 | ||
169 | if (newroot == NULL || newroot[0] == '\0' || | |
170 | init == NULL || init[0] == '\0' ) { | |
171 | usage(stderr); | |
172 | } | |
173 | ||
174 | if (switchroot(newroot) < 0) { | |
175 | fprintf(stderr, "switchroot has failed. Sorry.\n"); | |
176 | return 1; | |
177 | } | |
178 | if (access(initargs[0], X_OK)) | |
179 | fprintf(stderr, "WARNING: can't access %s\n", initargs[0]); | |
180 | ||
181 | /* get session leader */ | |
182 | setsid(); | |
183 | /* set controlling terminal */ | |
184 | ioctl (0, TIOCSCTTY, 1); | |
185 | ||
186 | execv(initargs[0], initargs); | |
187 | } | |
188 |