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/>.
20 * Peter Jones <pjones@redhat.com>
21 * Jeremy Katz <katzj@redhat.com>
26 #include <sys/mount.h>
27 #include <sys/types.h>
29 #include <sys/param.h>
43 #define MNT_DETACH 0x2
52 static int readFD(int fd
, char **buf
)
56 int s
= 0, filesize
= 0;
58 if (!(*buf
= calloc (16384, sizeof (char))))
63 s
= read(fd
, p
, 16384 - s
);
67 /* only exit for empty reads */
70 else if (s
== 16384) {
71 *buf
= realloc(*buf
, size
+ 16384);
72 memset(*buf
+ size
, '\0', 16384);
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
;
92 fd
= open("./proc/cmdline", O_RDONLY
);
95 fprintf(stderr
, "Error: Could not open ./proc/cmdline: %m\n");
100 if (readFD(fd
, &cmdline
) < 0) {
102 fprintf(stderr
, "Error: could not read ./proc/cmdline: %m\n");
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
)
121 cmdline
= start
= getKernelCmdLine();
126 if (isspace(*start
)) {
132 /* don't return if it's a different argument that merely starts
134 if (strncmp(start
, arg
, len
) == 0) {
135 if (start
[len
] == '=')
136 return start
+ len
+ 1;
137 if (!start
[len
] || isspace(start
[len
]))
140 while (*++start
&& !isspace(*start
))
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
;
158 *init
= getKernelArg("init");
162 cmdline
= getKernelCmdLine();
166 for (j
= 0; initprogs
[j
] != NULL
; j
++) {
167 if (!access(initprogs
[j
], X_OK
)) {
168 *init
= strdup(initprogs
[j
]);
174 initargs
= (char **)calloc(MAX_INIT_ARGS
+1, sizeof (char *));
175 if (initargs
== NULL
)
178 if (cmdline
&& *init
) {
179 initargs
[i
++] = *init
;
190 start
= chptr
= cmdline
;
191 for (; (i
< MAX_INIT_ARGS
) && (*start
!= '\0'); i
++) {
192 while (*chptr
&& (*chptr
!= quote
)) {
193 if (isspace(*chptr
) && quote
== '\0')
195 if (*chptr
== '"' || *chptr
== '\'')
200 if (quote
== '"' || quote
== '\'')
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*.
212 for (j
= 0; ignoreargs
[j
] != NULL
; j
++) {
213 if (cmdline
== *init
&& !strncmp(start
, ignoreargs
[j
], strlen(ignoreargs
[j
]))) {
225 if (start
[0] == '\0')
228 initargs
[i
] = strdup(start
);
233 if (initargs
[i
-1] != NULL
)
236 *initargs_out
= initargs
;
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
;
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
);
265 rc
= build_init_args(&init
, &initargs
);
269 if (mount(newroot
, "/", NULL
, MS_MOVE
, NULL
) < 0) {
271 fprintf(stderr
, "switchroot: mount failed: %m\n");
278 fprintf(stderr
, "switchroot: chroot failed: %m\n");
283 if (access(initargs
[0], X_OK
))
284 fprintf(stderr
, "WARNING: can't access %s\n", initargs
[0]);
286 execv(initargs
[0], initargs
);
290 static void usage(FILE *output
)
292 fprintf(output
, "usage: switchroot {-n|--newroot} <newrootdir>\n");
293 if (output
== stderr
)
298 int main(int argc
, char *argv
[])
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")) {
308 } else if (!strcmp(argv
[i
], "-n")
309 || !strcmp(argv
[i
], "--newroot")) {
311 } else if (!strncmp(argv
[i
], "--newroot=", 10)) {
312 newroot
= argv
[i
] + 10;
318 if (newroot
== NULL
|| newroot
[0] == '\0') {
324 fprintf(stderr
, "switchroot has failed. Sorry.\n");
329 * vim:noet:ts=8:sw=8:sts=8