Make 90dmraid load the dm-raid udev rules instead of 95udev-rules
[dracut.git] / switch_root.c
blob638a66379cc2aebd78270487be77f7d67b058e12
1 /*
2 * switchroot.c - switch to new root directory and start init.
4 * Copyright 2002-2008 Red Hat, Inc. All rights reserved.
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.
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.
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/>.
19 * Authors:
20 * Peter Jones <pjones@redhat.com>
21 * Jeremy Katz <katzj@redhat.com>
24 #define _GNU_SOURCE 1
26 #include <sys/mount.h>
27 #include <sys/types.h>
28 #include <sys/stat.h>
29 #include <sys/param.h>
30 #include <fcntl.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <string.h>
35 #include <errno.h>
36 #include <ctype.h>
38 #ifndef MS_MOVE
39 #define MS_MOVE 8192
40 #endif
42 #ifndef MNT_DETACH
43 #define MNT_DETACH 0x2
44 #endif
46 enum {
47 ok,
48 err_no_directory,
49 err_usage,
52 static int readFD(int fd, char **buf)
54 char *p;
55 size_t size = 16384;
56 int s = 0, filesize = 0;
58 if (!(*buf = calloc (16384, sizeof (char))))
59 return -1;
61 do {
62 p = *buf + filesize;
63 s = read(fd, p, 16384 - s);
64 if (s < 0)
65 break;
66 filesize += s;
67 /* only exit for empty reads */
68 if (s == 0)
69 break;
70 else if (s == 16384) {
71 *buf = realloc(*buf, size + 16384);
72 memset(*buf + size, '\0', 16384);
73 size += s;
74 s = 0;
75 } else {
76 size += s;
78 } while (1);
80 *buf = realloc(*buf, filesize+1);
81 (*buf)[filesize] = '\0';
83 return *buf ? filesize : -1;
86 static char *getKernelCmdLine(void)
88 static char *cmdline = NULL;
89 int fd = -1;
90 int errnum;
92 fd = open("./proc/cmdline", O_RDONLY);
93 if (fd < 0) {
94 errnum = errno;
95 fprintf(stderr, "Error: Could not open ./proc/cmdline: %m\n");
96 errno = errnum;
97 return NULL;
100 if (readFD(fd, &cmdline) < 0) {
101 errnum = errno;
102 fprintf(stderr, "Error: could not read ./proc/cmdline: %m\n");
103 close(fd);
104 errno = errnum;
105 return NULL;
107 close(fd);
109 return cmdline;
112 /* get the start of a kernel arg "arg". returns everything after it
113 * (useful for things like getting the args to init=). so if you only
114 * want one arg, you need to terminate it at the n */
115 static char *getKernelArg(char *arg)
117 char *start;
118 char *cmdline;
119 int len;
121 cmdline = start = getKernelCmdLine();
122 if (start == NULL)
123 return NULL;
125 while (*start) {
126 if (isspace(*start)) {
127 start++;
128 continue;
131 len = strlen(arg);
132 /* don't return if it's a different argument that merely starts
133 * like this one. */
134 if (strncmp(start, arg, len) == 0) {
135 if (start[len] == '=')
136 return start + len + 1;
137 if (!start[len] || isspace(start[len]))
138 return start + len;
140 while (*++start && !isspace(*start))
144 return NULL;
147 #define MAX_INIT_ARGS 32
148 static int build_init_args(char **init, char ***initargs_out)
150 const char *initprogs[] = { "./sbin/init", "./etc/init",
151 "./bin/init", "./bin/sh", NULL };
152 const char *ignoreargs[] = { "console=", "BOOT_IMAGE=", NULL };
153 char *cmdline = NULL;
154 char **initargs;
156 int i = 0;
158 *init = getKernelArg("init");
160 if (*init == NULL) {
161 int j;
162 cmdline = getKernelCmdLine();
163 if (cmdline == NULL)
164 return -1;
166 for (j = 0; initprogs[j] != NULL; j++) {
167 if (!access(initprogs[j], X_OK)) {
168 *init = strdup(initprogs[j]);
169 break;
174 initargs = (char **)calloc(MAX_INIT_ARGS+1, sizeof (char *));
175 if (initargs == NULL)
176 return -1;
178 if (cmdline && *init) {
179 initargs[i++] = *init;
180 } else {
181 cmdline = *init;
182 initargs[0] = NULL;
185 if (cmdline) {
186 char quote = '\0';
187 char *chptr;
188 char *start;
190 start = chptr = cmdline;
191 for (; (i < MAX_INIT_ARGS) && (*start != '\0'); i++) {
192 while (*chptr && (*chptr != quote)) {
193 if (isspace(*chptr) && quote == '\0')
194 break;
195 if (*chptr == '"' || *chptr == '\'')
196 quote = *chptr;
197 chptr++;
200 if (quote == '"' || quote == '\'')
201 chptr++;
202 if (*chptr != '\0')
203 *(chptr++) = '\0';
205 /* There are some magic parameters added *after*
206 * everything you pass, including a console= from the
207 * x86_64 kernel and BOOT_IMAGE= by syslinux. Bash
208 * doesn't know what they mean, so it then exits, init
209 * gets killed, desaster ensues. *sigh*.
211 int j;
212 for (j = 0; ignoreargs[j] != NULL; j++) {
213 if (cmdline == *init && !strncmp(start, ignoreargs[j], strlen(ignoreargs[j]))) {
214 if (!*chptr)
215 initargs[i] = NULL;
216 else
217 i--;
218 start = chptr;
219 break;
222 if (start == chptr)
223 continue;
225 if (start[0] == '\0')
226 i--;
227 else
228 initargs[i] = strdup(start);
229 start = chptr;
233 if (initargs[i-1] != NULL)
234 initargs[i] = NULL;
236 *initargs_out = initargs;
239 return 0;
242 static void switchroot(const char *newroot)
244 /* Don't try to unmount the old "/", there's no way to do it. */
245 const char *umounts[] = { "/dev", "/proc", "/sys", NULL };
246 char *init, **initargs;
247 int errnum;
248 int rc;
249 int i;
251 for (i = 0; umounts[i] != NULL; i++) {
252 char newmount[PATH_MAX];
253 strcpy(newmount, newroot);
254 strcat(newmount, umounts[i]);
255 if (mount(umounts[i], newmount, NULL, MS_MOVE, NULL) < 0) {
256 fprintf(stderr, "Error mount moving old %s %s %m\n",
257 umounts[i], newmount);
258 fprintf(stderr, "Forcing unmount of %s\n", umounts[i]);
259 umount2(umounts[i], MNT_FORCE);
263 chdir(newroot);
265 rc = build_init_args(&init, &initargs);
266 if (rc < 0)
267 return;
269 if (mount(newroot, "/", NULL, MS_MOVE, NULL) < 0) {
270 errnum = errno;
271 fprintf(stderr, "switchroot: mount failed: %m\n");
272 errno = errnum;
273 return;
276 if (chroot(".")) {
277 errnum = errno;
278 fprintf(stderr, "switchroot: chroot failed: %m\n");
279 errno = errnum;
280 return;
283 if (access(initargs[0], X_OK))
284 fprintf(stderr, "WARNING: can't access %s\n", initargs[0]);
286 execv(initargs[0], initargs);
287 return;
290 static void usage(FILE *output)
292 fprintf(output, "usage: switchroot {-n|--newroot} <newrootdir>\n");
293 if (output == stderr)
294 exit(err_usage);
295 exit(ok);
298 int main(int argc, char *argv[])
300 int i;
301 char *newroot = NULL;
303 for (i = 1; i < argc; i++) {
304 if (!strcmp(argv[i], "--help")
305 || !strcmp(argv[i], "-h")
306 || !strcmp(argv[i], "--usage")) {
307 usage(stdout);
308 } else if (!strcmp(argv[i], "-n")
309 || !strcmp(argv[i], "--newroot")) {
310 newroot = argv[++i];
311 } else if (!strncmp(argv[i], "--newroot=", 10)) {
312 newroot = argv[i] + 10;
313 } else {
314 usage(stderr);
318 if (newroot == NULL || newroot[0] == '\0') {
319 usage(stderr);
322 switchroot(newroot);
324 fprintf(stderr, "switchroot has failed. Sorry.\n");
325 return 1;
329 * vim:noet:ts=8:sw=8:sts=8