Skip to content
This repository

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Newer
Older
100644 1024 lines (819 sloc) 24.518 kb
e5a8d5b7 »
2011-05-01 standardize file headers and add MIT licensee
1 /*
2 * init.c
3 *
4 * This file is part of geninit.
5 *
6 * PID 1 for early userspace.
7 *
8 */
9
75052c2e »
2011-04-26 initial commit
10 #define _GNU_SOURCE
11 #include <ctype.h>
12 #include <dirent.h>
13 #include <errno.h>
14 #include <fcntl.h>
15 #include <limits.h>
16 #include <linux/magic.h>
17 #include <signal.h>
18 #include <stdio.h>
19 #include <stdlib.h>
20 #include <string.h>
90dfe5f8 »
2011-05-18 dmesg: remove builder/hook. add support for loglevel=N.
21 #include <sys/klog.h>
75052c2e »
2011-04-26 initial commit
22 #include <sys/mount.h>
23 #include <sys/stat.h>
24 #include <sys/time.h>
25 #include <sys/types.h>
26 #include <sys/vfs.h>
27 #include <sys/wait.h>
28 #include <unistd.h>
29
30 /* util-linux */
1d8224ac »
2011-04-28 provide the proper path for blkid.h
31 #include <blkid/blkid.h>
75052c2e »
2011-04-26 initial commit
32
821dd7e2 »
2011-05-10 replace busybox's rm -rf implementation with util-linux's
33 #define msg(...) {if (!quiet) fprintf(stderr, ":: " __VA_ARGS__);}
34 #define err(...) {fprintf(stderr, "error: " __VA_ARGS__);}
35 #define warn(...) {fprintf(stderr, "warning: " __VA_ARGS__);}
36 #define die(...) {err(__VA_ARGS__); _exit(1);}
75052c2e »
2011-04-26 initial commit
37
9e335d4a »
2011-05-06 STRING => QUOTE
38 #define QUOTE(x) #x
482d4d2f »
2011-05-07 fix CHILD_WRITE_FD declaration
39 #define TOSTRING(x) QUOTE(x)
a001934d »
2011-05-06 fully abstract write pipe from child to parent
40
165842a8 »
2011-05-06 rename CHILD_READ_FD as CHILD_WRITE_FD, since this is way more accurate
41 #define CMDLINE_SIZE 257 /* 256 max cmdline len + NULL */
42 #define TMPFS_FLAGS MS_NOEXEC|MS_NODEV|MS_NOSUID
75052c2e »
2011-04-26 initial commit
43
165842a8 »
2011-05-06 rename CHILD_READ_FD as CHILD_WRITE_FD, since this is way more accurate
44 #define CHILD_WRITE_FD 6
354f0b06 »
2011-05-05 use FD 6 instead of 3. It's less likely to conflict.
45
165842a8 »
2011-05-06 rename CHILD_READ_FD as CHILD_WRITE_FD, since this is way more accurate
46 #define NEWROOT "/new_root"
47 #define BUSYBOX "/bin/busybox"
48 #define UDEVD "/sbin/udevd"
49 #define UDEVADM "/sbin/udevadm"
50 #define MODPROBE "/sbin/modprobe"
75052c2e »
2011-04-26 initial commit
51
52 int rootflags = 0;
53 int quiet = 0;
be506134 »
2011-05-06 Install busybox symlinks prior to running hooks, if needed.
54 int bbox_installed = 0;
3b17a574 »
2011-05-02 refactor handling of the udev daemon
55 pid_t udevpid = 0;
75052c2e »
2011-04-26 initial commit
56
57 /* utility */
58 static dev_t hex2dev(char *hexstring) { /* {{{ */
59 char *endptr;
60 char hexmajor[3], hexminor[3];
61 long major, minor;
62 size_t len;
63
64 len = strlen(hexstring);
65 if (len > 4) {
66 return 1;
67 }
68
69 /* 2 less than the length, plus a NULL */
70 snprintf(hexmajor, len - 2 + 1, "%s", hexstring);
71
72 /* leave off after the major, 2 chars plus a NULL */
73 snprintf(hexminor, 3, "%s", hexstring + len - 2);
74
75 major = strtol(hexmajor, &endptr, 16);
76 if (!endptr) {
77 return makedev(0, 0);
78 }
79
80 minor = strtol(hexminor, &endptr, 16);
81 if (!endptr) {
82 return makedev(0, 0);
83 }
84
85 return makedev(major, minor);
86 } /* }}} */
87
88 static int forkexecwait(char **argv) { /* {{{ */
89 pid_t pid;
90 int statloc;
91
92 pid = vfork();
93 if (pid == -1) {
94 perror("fork");
95 return errno;
96 }
97
98 if (pid == 0) {
99 execv(argv[0], argv);
100 fprintf(stderr, "exec: %s: %s\n", argv[0], strerror(errno));
101 _exit(errno); /* avoid flushing streams */
102 }
103
104 /* block for process exit */
105 waitpid(pid, &statloc, 0);
106
107 if (WIFEXITED(statloc) > 0) {
108 return WEXITSTATUS(statloc);
b3832b57 »
2011-05-02 improve return values of forkexecwait
109 } else if (WIFSIGNALED(statloc)) {
110 return WTERMSIG(statloc) + 128;
75052c2e »
2011-04-26 initial commit
111 }
112
b3832b57 »
2011-05-02 improve return values of forkexecwait
113 /* we really shouldn't get here */
114 return 255;
75052c2e »
2011-04-26 initial commit
115 } /* }}} */
116
117 static char *sanitize_var(char *var) { /* {{{ */
118 char *p;
119
120 /* special attention to first letter */
121 p = var;
122 if (!(isalpha(*p) || *p == '_')) {
123 /* invalid var name, can't use this */
124 return NULL;
125 }
126
127 while (*++p) {
128 if (isalnum((unsigned char)*p) || *p == '_') {
129 /* valid character */
130 continue;
131 }
132
133 if (*p == '=') {
134 /* stop here, don't mangle the values */
135 return var;
136 }
137
138 if (*p == '.' || *p == '-') {
139 /* sanitizable */
140 *p = '_';
141 } else {
142 /* gfy */
143 return NULL;
144 }
145 }
146
147 return var;
148 } /* }}} */
149
821dd7e2 »
2011-05-10 replace busybox's rm -rf implementation with util-linux's
150 static int delete_contents(char *dirname) { /* {{{ */
151 struct stat rb; /* rootdir buffer */
152 int dfd, rc = -1;
153 DIR *dp;
75052c2e »
2011-04-26 initial commit
154
821dd7e2 »
2011-05-10 replace busybox's rm -rf implementation with util-linux's
155 if (!(dp = opendir(dirname))) {
156 warn("failed to open %s", dirname);
157 goto done;
75052c2e »
2011-04-26 initial commit
158 }
159
821dd7e2 »
2011-05-10 replace busybox's rm -rf implementation with util-linux's
160 dfd = dirfd(dp);
161
162 if (fstat(dfd, &rb)) {
163 warn("failed to stat %s", dirname);
164 goto done;
165 }
166
167 while(1) {
168 struct dirent *d;
169
170 errno = 0;
171 if (!(d = readdir(dp))) {
172 if (errno) {
173 warn("failed to read %s", dirname);
174 goto done;
75052c2e »
2011-04-26 initial commit
175 }
821dd7e2 »
2011-05-10 replace busybox's rm -rf implementation with util-linux's
176 break; /* end of directory */
177 }
75052c2e »
2011-04-26 initial commit
178
821dd7e2 »
2011-05-10 replace busybox's rm -rf implementation with util-linux's
179 if (strcmp(d->d_name, ".") == 0 || strcmp(d->d_name, "..") == 0) {
180 continue;
75052c2e »
2011-04-26 initial commit
181 }
821dd7e2 »
2011-05-10 replace busybox's rm -rf implementation with util-linux's
182
183 if (d->d_type == DT_DIR) {
184 struct stat sb; /* subdir buffer */
185
186 if (fstatat(dfd, d->d_name, &sb, AT_SYMLINK_NOFOLLOW)) {
187 warn("failed to stat %s/%s", dirname, d->d_name);
188 continue;
189 }
190
191 /* don't descend into other filesystems */
192 if (sb.st_dev == rb.st_dev) {
193 char subdir[strlen(dirname) + strlen(d->d_name) + 2];
194
195 sprintf(subdir, "%s/%s", dirname, d->d_name);
196 delete_contents(subdir);
197 } else {
198 continue;
199 }
200 }
201
202 if (unlinkat(dfd, d->d_name, d->d_type == DT_DIR ? AT_REMOVEDIR : 0)) {
203 warn("failed to unlink %s/%s", dirname, d->d_name);
204 }
205 }
206
207 rc = 0; /* success */
208
209 done:
210 if (dp) {
211 closedir(dp);
75052c2e »
2011-04-26 initial commit
212 }
821dd7e2 »
2011-05-10 replace busybox's rm -rf implementation with util-linux's
213 return rc;
75052c2e »
2011-04-26 initial commit
214 } /* }}} */
215
fae54790 »
2011-05-08 expose FDINIT for rescue shells
216 static ssize_t read_child_response(char **argv, char *buffer) { /* {{{ */
217 int statloc, pfds[2];
af47f6ef »
2011-05-25 initial support for custom mount handlers
218 ssize_t len, total = 0;
aa2c1aed »
2011-05-08 loop to read from child processes
219 char readbuf[BUFSIZ];
fae54790 »
2011-05-08 expose FDINIT for rescue shells
220 pid_t pid;
75052c2e »
2011-04-26 initial commit
221
fae54790 »
2011-05-08 expose FDINIT for rescue shells
222 if (pipe(pfds) != 0) {
223 perror("pipe");
283da7ed »
2011-05-11 return negative errno to avoid returning a valid len
224 return -errno;
75052c2e »
2011-04-26 initial commit
225 }
226
fae54790 »
2011-05-08 expose FDINIT for rescue shells
227 pid = fork();
228 if (pid < 0) {
229 perror("fork");
283da7ed »
2011-05-11 return negative errno to avoid returning a valid len
230 return -errno;
be506134 »
2011-05-06 Install busybox symlinks prior to running hooks, if needed.
231 }
75052c2e »
2011-04-26 initial commit
232
fae54790 »
2011-05-08 expose FDINIT for rescue shells
233 if (pid == 0) {
234 close(pfds[0]); /* unused by child */
75052c2e »
2011-04-26 initial commit
235
fae54790 »
2011-05-08 expose FDINIT for rescue shells
236 /* child writes on CHILD_WRITE_FD will be received by the parent */
237 if (dup2(pfds[1], CHILD_WRITE_FD) == -1) {
238 perror("dup2");
239 _exit(errno);
240 }
75052c2e »
2011-04-26 initial commit
241
fae54790 »
2011-05-08 expose FDINIT for rescue shells
242 /* now redundant */
243 close(pfds[1]);
75052c2e »
2011-04-26 initial commit
244
fae54790 »
2011-05-08 expose FDINIT for rescue shells
245 execv(argv[0], argv);
af47f6ef »
2011-05-25 initial support for custom mount handlers
246 fprintf(stderr, "exec: %s: %s\n", argv[0], strerror(errno));
fae54790 »
2011-05-08 expose FDINIT for rescue shells
247 _exit(errno);
75052c2e »
2011-04-26 initial commit
248 }
249
fae54790 »
2011-05-08 expose FDINIT for rescue shells
250 close(pfds[1]); /* unused by parent */
75052c2e »
2011-04-26 initial commit
251
aa2c1aed »
2011-05-08 loop to read from child processes
252 memset(buffer, 0, BUFSIZ);
253 while (1) {
254 len = read(pfds[0], readbuf, BUFSIZ);
255 if (len <= 0 && errno != EINTR) {
256 break;
257 }
258
259 if (total + len > BUFSIZ) { /* overflow! */
260 /* this is a ridiculous condition. the user just tried to write an absurd
261 * amount of data to init. if this wasn't an accident, i either messed up,
262 * or i hate the user. */
263 err("buffer overflow detected while writing to init! input may be truncated!");
264
265 /* write what we can to the buffer and get out */
266 memcpy(&buffer[total], readbuf, BUFSIZ - total - 1);
267 break;
268 }
269
270 memcpy(&buffer[total], readbuf, len);
271 total += len;
272 }
af47f6ef »
2011-05-25 initial support for custom mount handlers
273
fae54790 »
2011-05-08 expose FDINIT for rescue shells
274 close(pfds[0]);
75052c2e »
2011-04-26 initial commit
275
fae54790 »
2011-05-08 expose FDINIT for rescue shells
276 waitpid(pid, &statloc, 0);
277 if (WIFEXITED(statloc) && WEXITSTATUS(statloc) != 0) {
af47f6ef »
2011-05-25 initial support for custom mount handlers
278 err("`%s' exited with status %d\n", argv[0], WEXITSTATUS(statloc));
283da7ed »
2011-05-11 return negative errno to avoid returning a valid len
279 return -(WEXITSTATUS(statloc));
75052c2e »
2011-04-26 initial commit
280 }
fae54790 »
2011-05-08 expose FDINIT for rescue shells
281
aa2c1aed »
2011-05-08 loop to read from child processes
282 return total;
75052c2e »
2011-04-26 initial commit
283 } /* }}} */
284
71fb815f »
2011-05-01 move environment string parsing to separate function
285 static void parse_envstring(char *envstring) { /* {{{ */
75052c2e »
2011-04-26 initial commit
286 char *c, *tp;
71fb815f »
2011-05-01 move environment string parsing to separate function
287 char token[CMDLINE_SIZE];
288 char quoted = '\0'; /* flag for inside/outside quoted region */
75052c2e »
2011-04-26 initial commit
289 int isvar = 0;
71fb815f »
2011-05-01 move environment string parsing to separate function
290
291 if (!envstring) {
292 return;
293 }
75052c2e »
2011-04-26 initial commit
294
295 /* a bit of pointer/var hell going on...
71fb815f »
2011-05-01 move environment string parsing to separate function
296 * c = pointer along contents of envstring
75052c2e »
2011-04-26 initial commit
297 * token = container for current token being parsed
298 * tp = pointer along contents of token
299 */
300
301 tp = token;
71fb815f »
2011-05-01 move environment string parsing to separate function
302 for (c = envstring; *c; c++) {
75052c2e »
2011-04-26 initial commit
303 if (*c == '#') { /* full stop! */
304 break;
305 }
306
307 if (isspace((unsigned char)*c)) {
308 /* don't break inside a quoted region */
309 if (!quoted && tp != token) {
310 *tp = '\0';
311 if (sanitize_var(token)) {
312 if (isvar) {
313 putenv(strdup(token));
314 } else {
315 setenv(strdup(token), "y", 1);
316 }
317 if (strcmp(token, "ro") == 0) {
318 rootflags |= MS_RDONLY;
319 } else if (strcmp(token, "quiet") == 0) {
320 quiet = 1;
321 }
322 }
323 isvar = 0;
324 tp = token;
325 }
326 continue;
327 } else if (*c == '\'' || *c == '"') {
328 if (quoted) {
329 if (quoted == *c) {
330 quoted = '\0';
331 continue;
332 }
333 } else {
334 quoted = *c;
335 continue;
336 }
337 }
338
339 if (*c == '=') {
340 isvar = 1;
341 }
342
343 *tp++ = *c;
344 }
71fb815f »
2011-05-01 move environment string parsing to separate function
345
346 } /* }}} */
347
fae54790 »
2011-05-08 expose FDINIT for rescue shells
348 static void start_rescue_shell(void) { /* {{{ */
349 char *bboxinstall[] = { BUSYBOX, "--install", NULL };
350 char *bboxlaunch[] = { BUSYBOX, "ash", NULL };
aa2c1aed »
2011-05-08 loop to read from child processes
351 char buffer[BUFSIZ];
e51793fc »
2011-05-01 allow hooks to write to FD3 to communicate back to init
352
fae54790 »
2011-05-08 expose FDINIT for rescue shells
353 if (access(BUSYBOX, X_OK) != 0) {
354 return;
e51793fc »
2011-05-01 allow hooks to write to FD3 to communicate back to init
355 }
356
fae54790 »
2011-05-08 expose FDINIT for rescue shells
357 if (!bbox_installed) {
358 forkexecwait(bboxinstall);
e51793fc »
2011-05-01 allow hooks to write to FD3 to communicate back to init
359 }
360
fae54790 »
2011-05-08 expose FDINIT for rescue shells
361 /* set a prompt */
362 putenv("PS1=[ramfs \\W]\\$ ");
e51793fc »
2011-05-01 allow hooks to write to FD3 to communicate back to init
363
fae54790 »
2011-05-08 expose FDINIT for rescue shells
364 /* start the shell, allow writes on FDINIT */
365 if (read_child_response(bboxlaunch, buffer) > 0) {
366 parse_envstring(buffer);
367 }
e51793fc »
2011-05-01 allow hooks to write to FD3 to communicate back to init
368
fae54790 »
2011-05-08 expose FDINIT for rescue shells
369 } /* }}} */
e51793fc »
2011-05-01 allow hooks to write to FD3 to communicate back to init
370
fae54790 »
2011-05-08 expose FDINIT for rescue shells
371 static char *probe_fstype(const char *devname) { /* {{{ */
372 int ret;
91f7aa06 »
2011-05-12 always iterate over the returned values from blkid
373 char *fstype = NULL;
fae54790 »
2011-05-08 expose FDINIT for rescue shells
374 blkid_probe pr;
e51793fc »
2011-05-01 allow hooks to write to FD3 to communicate back to init
375
fae54790 »
2011-05-08 expose FDINIT for rescue shells
376 pr = blkid_new_probe_from_filename(devname);
377 if (!pr) {
378 err("%s: failed to create a new libblkid probe\n", devname);
379 return NULL;
380 }
e51793fc »
2011-05-01 allow hooks to write to FD3 to communicate back to init
381
fae54790 »
2011-05-08 expose FDINIT for rescue shells
382 blkid_probe_enable_superblocks(pr, 1);
383 blkid_probe_set_superblocks_flags(pr, BLKID_SUBLKS_TYPE);
e51793fc »
2011-05-01 allow hooks to write to FD3 to communicate back to init
384
fae54790 »
2011-05-08 expose FDINIT for rescue shells
385 ret = blkid_do_safeprobe(pr);
386 if (ret == -1) {
387 return NULL;
388 } else if (ret == 1) {
389 err("failed to probe device %s\n", devname);
390 return NULL;
391 } else {
392 const char *name, *data;
91f7aa06 »
2011-05-12 always iterate over the returned values from blkid
393 int i, nvals = blkid_probe_numof_values(pr);
394
395 /* btrfs (maybe others) returns more than just its fstype here so we're
396 * forced to iterate over the data to find the one true 'TYPE' */
397 for (i = 0; i < nvals; i++) {
398 blkid_probe_get_value(pr, i, &name, &data, NULL);
399 if (strcmp(name, "TYPE") == 0) {
400 fstype = strdup(data);
401 break;
402 }
403 }
4d09a5d1 »
2011-05-05 throw an error when a hook exits with non-zero
404 }
405
fae54790 »
2011-05-08 expose FDINIT for rescue shells
406 blkid_free_probe(pr);
407
408 return fstype;
409 } /* }}} */
410
411 static void movemount(const char *src, const char *dest) { /* {{{ */
412 /* move the mount if it exists on the real root, otherwise get rid of it */
413 if (access(dest, F_OK) == 0) {
414 mount(src, dest, NULL, MS_MOVE, NULL);
415 } else {
416 umount2(src, MNT_DETACH);
417 }
e51793fc »
2011-05-01 allow hooks to write to FD3 to communicate back to init
418 } /* }}} */
419
137997d9 »
2011-05-02 implement udevadm as a helper function
420 static int udevadm(char *action, char *arg1, char *arg2) { /* {{{ */
421 char *argv[] = { UDEVADM, action, arg1, arg2, NULL };
422 return forkexecwait(argv);
423 } /* }}} */
424
71fb815f »
2011-05-01 move environment string parsing to separate function
425 /* meat */
426 static void mount_setup(void) { /* {{{ */
427 int ret;
428
429 /* setup basic filesystems */
430 mount("proc", "/proc", "proc", TMPFS_FLAGS, NULL);
431 mount("sys", "/sys", "sysfs", TMPFS_FLAGS, NULL);
4f6199ec »
2011-05-10 name /run's source as run
432 mount("run", "/run", "tmpfs", TMPFS_FLAGS, "mode=0755,size=10M");
71fb815f »
2011-05-01 move environment string parsing to separate function
433
434 /* ENODEV returned on non-existant FS */
df5ae8d9 »
2011-05-28 init: limit /dev to 1024k
435 ret = mount("udev", "/dev", "devtmpfs", MS_NOSUID, "mode=0755");
71fb815f »
2011-05-01 move environment string parsing to separate function
436 if (ret == -1 && errno == ENODEV) {
437 /* devtmpfs not available, use standard tmpfs */
df5ae8d9 »
2011-05-28 init: limit /dev to 1024k
438 mount("udev", "/dev", "tmpfs", MS_NOSUID, "mode=0755,size=1024k");
71fb815f »
2011-05-01 move environment string parsing to separate function
439
2c0b9398 »
2011-05-20 init: shorten comment about nodes
440 /* create necessary nodes */
71fb815f »
2011-05-01 move environment string parsing to separate function
441 mknod("/dev/console", S_IFCHR|0600, makedev(5, 1));
442 mknod("/dev/null", S_IFCHR|0666, makedev(1, 3));
443 mknod("/dev/zero", S_IFCHR|0666, makedev(1, 5));
444 mknod("/dev/mem", S_IFCHR|0640, makedev(1, 1));
445 }
446 } /* }}} */
447
448 static void put_cmdline(void) { /* {{{ */
449 char cmdline[CMDLINE_SIZE];
450 FILE *fp;
451
452 fp = fopen("/proc/cmdline", "r");
453 if (!fp) {
454 return;
455 }
456
457 if (fgets(cmdline, CMDLINE_SIZE, fp) != NULL) {
458 parse_envstring(cmdline);
459 }
460
461 fclose(fp);
75052c2e »
2011-04-26 initial commit
462 } /* }}} */
463
464 static void disable_modules(void) { /* {{{ */
465 char *tok, *var;
466 FILE *fp;
467
468 if (getenv("disablemodules") == NULL) {
469 return;
470 }
471
472 /* ensure parent dirs exist */
473 mkdir("/etc", 0755);
474 mkdir("/etc/modprobe.d", 0755);
475
476 fp = fopen("/etc/modprobe.d/initcpio.conf", "w");
477 if (!fp) {
478 perror("error: /etc/modprobe.d/initcpio.conf");
479 return;
480 }
481
482 var = strdup(getenv("disablemodules"));
483 for (tok = strtok(var, ","); tok; tok = strtok(NULL, ",")) {
484 fprintf(fp, "install %s /bin/false\n", tok);
485 }
486
487 fclose(fp);
488 free(var);
489 } /* }}} */
490
3b17a574 »
2011-05-02 refactor handling of the udev daemon
491 static void launch_udev(void) { /* {{{ */
be506134 »
2011-05-06 Install busybox symlinks prior to running hooks, if needed.
492 char *argv[] = { UDEVD, "--resolve-names=never", NULL };
75052c2e »
2011-04-26 initial commit
493
494 if (access(UDEVD, X_OK) != 0) {
3b17a574 »
2011-05-02 refactor handling of the udev daemon
495 return;
75052c2e »
2011-04-26 initial commit
496 }
497
498 msg("Starting udev...\n");
499
3b17a574 »
2011-05-02 refactor handling of the udev daemon
500 udevpid = vfork();
501 if (udevpid == -1) {
75052c2e »
2011-04-26 initial commit
502 perror("fork");
3b17a574 »
2011-05-02 refactor handling of the udev daemon
503 return;
75052c2e »
2011-04-26 initial commit
504 }
505
3b17a574 »
2011-05-02 refactor handling of the udev daemon
506 if (udevpid == 0) {
75052c2e »
2011-04-26 initial commit
507 execv(argv[0], argv);
508 perror("exec: " UDEVD);
509 _exit(errno);
510 }
511
3b17a574 »
2011-05-02 refactor handling of the udev daemon
512 /* we assume here that udevd started correctly, but we won't trust this. */
75052c2e »
2011-04-26 initial commit
513 } /* }}} */
514
515 static void load_extra_modules(void) { /* {{{ */
516 FILE *fp;
517 char *tok, *var;
518 char **argv;
519 char line[PATH_MAX];
8af7bccd »
2011-04-30 cleanup parsing earlymodules. fix a memory leak and wrong allocation
520 int modcount = 3;
75052c2e »
2011-04-26 initial commit
521
522 /* load early modules */
523 if (getenv("earlymodules") != NULL) {
e605090c »
2011-04-30 tighten up modprobe execution
524 argv = calloc(3, sizeof(argv));
8af7bccd »
2011-04-30 cleanup parsing earlymodules. fix a memory leak and wrong allocation
525 argv[0] = MODPROBE;
526 argv[1] = "-qab";
527 argv[2] = "--";
75052c2e »
2011-04-26 initial commit
528
529 var = strdup(getenv("earlymodules"));
530 for (tok = strtok(var, ","); tok; tok = strtok(NULL, ",")) {
531 argv = realloc(argv, sizeof(argv) * ++modcount);
8af7bccd »
2011-04-30 cleanup parsing earlymodules. fix a memory leak and wrong allocation
532 argv[modcount - 1] = tok;
75052c2e »
2011-04-26 initial commit
533 }
534
e605090c »
2011-04-30 tighten up modprobe execution
535 if (modcount > 3) {
75052c2e »
2011-04-26 initial commit
536 argv = realloc(argv, sizeof(argv) * ++modcount);
537 *(argv + (modcount - 1)) = NULL;
8d72afc3 »
2011-04-30 Revert "run modprobe in load_extra_modules asynchronously"
538 forkexecwait(argv);
75052c2e »
2011-04-26 initial commit
539 }
540 free(argv);
8af7bccd »
2011-04-30 cleanup parsing earlymodules. fix a memory leak and wrong allocation
541 free(var);
75052c2e »
2011-04-26 initial commit
542 }
543
544 /* load modules from /config */
545 fp = fopen("/config", "r");
7c8ab969 »
2011-04-27 fix possible null dereferences
546 if (!fp) {
547 return;
548 }
549
29fdef9d »
2011-04-27 fixup init for new config style
550 while ((fgets(line, 1024, fp))) {
551 if (strncmp(line, "%MODULES%", 9) == 0) {
552 strtok(line, " \n"); /* ditch the fieldname */
553 tok = strtok(NULL, " \n");
75052c2e »
2011-04-26 initial commit
554
29fdef9d »
2011-04-27 fixup init for new config style
555 modcount = atoi(tok);
556 if (!modcount) {
8af7bccd »
2011-04-30 cleanup parsing earlymodules. fix a memory leak and wrong allocation
557 break;
29fdef9d »
2011-04-27 fixup init for new config style
558 }
559
560 /* commands + number of modules + NULL */
8af7bccd »
2011-04-30 cleanup parsing earlymodules. fix a memory leak and wrong allocation
561 argv = calloc(3 + modcount + 1, sizeof argv);
e605090c »
2011-04-30 tighten up modprobe execution
562 *argv++ = MODPROBE;
563 *argv++ = "-qab";
564 *argv++ = "--";
8af7bccd »
2011-04-30 cleanup parsing earlymodules. fix a memory leak and wrong allocation
565
29fdef9d »
2011-04-27 fixup init for new config style
566 while ((tok = strtok(NULL, " \n"))) {
567 *argv++ = tok;
75052c2e »
2011-04-26 initial commit
568 }
29fdef9d »
2011-04-27 fixup init for new config style
569
570 /* rewind */
e605090c »
2011-04-30 tighten up modprobe execution
571 argv -= (modcount + 3);
29fdef9d »
2011-04-27 fixup init for new config style
572
573 /* run modprobe */
8d72afc3 »
2011-04-30 Revert "run modprobe in load_extra_modules asynchronously"
574 forkexecwait(argv);
29fdef9d »
2011-04-27 fixup init for new config style
575
576 free(argv);
577 break;
75052c2e »
2011-04-26 initial commit
578 }
579 }
7c8ab969 »
2011-04-27 fix possible null dereferences
580
581 fclose(fp);
75052c2e »
2011-04-26 initial commit
582 } /* }}} */
583
584 static void trigger_udev_events(void) { /* {{{ */
2427be98 »
2011-05-06 export UDEVPID for children, so they know if udev is running
585 char buffer[8];
75052c2e »
2011-04-26 initial commit
586 struct timeval tv[2];
587 long time_ms = 0; /* processing time in ms */
588
239441c6 »
2011-05-06 if running, export udev's pid to the environment
589 /* is udev alive? */
590 if (udevpid <= 0 || kill(udevpid, 0) != 0) {
75052c2e »
2011-04-26 initial commit
591 return;
592 }
593
2427be98 »
2011-05-06 export UDEVPID for children, so they know if udev is running
594 /* drop udev's pid into the environment for children to use */
595 snprintf(buffer, 8, "%d", udevpid);
596 setenv("UDEVPID", buffer, 1);
597
a3534b68 »
2011-04-30 trigger udev events in two stages
598 msg("triggering uevents...\n");
599
137997d9 »
2011-05-02 implement udevadm as a helper function
600 gettimeofday(&tv[0], NULL);
601 udevadm("trigger", "--action=add", "--type=subsystems");
602 udevadm("trigger", "--action=add", "--type=devices");
603 udevadm("settle", "--timeout=30", NULL);
75052c2e »
2011-04-26 initial commit
604 gettimeofday(&tv[1], NULL);
605
606 time_ms += (tv[1].tv_sec - tv[0].tv_sec) * 1000; /* s => ms */
607 time_ms += (tv[1].tv_usec - tv[0].tv_usec) / 1000; /* us => ms */
608
609 msg("finished udev processing in %ldms\n", time_ms);
610 } /* }}} */
611
612 static void disable_hooks(void) { /* {{{ */
613 char *hook, *list, *disable;
614
615 disable = getenv("disablehooks");
616 if (!disable) {
617 return;
618 }
619
620 list = strdup(disable);
621 for (hook = strtok(list, ", "); hook; hook = strtok(NULL, ", ")) {
622 char path[PATH_MAX];
623 snprintf(path, PATH_MAX, "/hooks/%s", hook);
624
625 /* mark as non-executable so run_hooks skips over it */
626 chmod(path, 0644);
627 }
628
629 free(list);
630 } /* }}} */
631
90dfe5f8 »
2011-05-18 dmesg: remove builder/hook. add support for loglevel=N.
632 static void set_kloglevel(void) { /* {{{ */
633 char *level;
634 int ret;
635
636 level = getenv("loglevel");
637 if (!level) {
638 return;
639 }
640
641 if (strlen(level) > 1 || *level < '1' || *level > '8') {
642 err("invalid log level: %s\n", level);
643 return;
644 }
645
646 ret = klogctl(8, NULL, *level - 48);
647 if (ret != 0) {
648 perror("klogctl");
649 }
650 } /* }}} */
651
75052c2e »
2011-04-26 initial commit
652 static void run_hooks(void) { /* {{{ */
be506134 »
2011-05-06 Install busybox symlinks prior to running hooks, if needed.
653 char *bboxinstall[] = { BUSYBOX, "--install", NULL };
75052c2e »
2011-04-26 initial commit
654 char line[PATH_MAX];
655 char *hook;
b15ea26b »
2011-05-12 avoid keeping the FD for /config open through hooks
656 FILE *fp;
75052c2e »
2011-04-26 initial commit
657
22b0f4c9 »
2011-05-24 init: export a default PATH
658 putenv("PATH=/usr/sbin:/usr/bin:/sbin:/bin");
fae54790 »
2011-05-08 expose FDINIT for rescue shells
659 setenv("FDINIT", TOSTRING(CHILD_WRITE_FD), 1);
b15ea26b »
2011-05-12 avoid keeping the FD for /config open through hooks
660 line[0] = '\0';
fae54790 »
2011-05-08 expose FDINIT for rescue shells
661
90dfe5f8 »
2011-05-18 dmesg: remove builder/hook. add support for loglevel=N.
662 fp = fopen("/config", "r");
b15ea26b »
2011-05-12 avoid keeping the FD for /config open through hooks
663 if (fp) {
664 while (fgets(line, PATH_MAX, fp) != NULL) {
665 if (strncmp(line, "%HOOKS%", 7) == 0) {
666 break;
667 }
668 line[0] = '\0';
669 }
670 fclose(fp);
75052c2e »
2011-04-26 initial commit
671 }
672
b15ea26b »
2011-05-12 avoid keeping the FD for /config open through hooks
673 if (!line[0]) { /* never found a %HOOKS% line */
674 return;
675 }
75052c2e »
2011-04-26 initial commit
676
b15ea26b »
2011-05-12 avoid keeping the FD for /config open through hooks
677 strtok(line, " \n"); /* ditch the fieldname */
678 while ((hook = strtok(NULL, " \n"))) {
679 char *argv[] = { NULL, NULL };
680 char response[BUFSIZ], path[PATH_MAX];
75052c2e »
2011-04-26 initial commit
681
b15ea26b »
2011-05-12 avoid keeping the FD for /config open through hooks
682 snprintf(path, PATH_MAX, "/hooks/%s", hook);
683 if (access(path, X_OK) != 0) {
684 continue;
685 }
be506134 »
2011-05-06 Install busybox symlinks prior to running hooks, if needed.
686
b15ea26b »
2011-05-12 avoid keeping the FD for /config open through hooks
687 /* lazily install symlinks */
688 if (!bbox_installed) {
689 forkexecwait(bboxinstall);
690 bbox_installed = 1;
691 }
e51793fc »
2011-05-01 allow hooks to write to FD3 to communicate back to init
692
b15ea26b »
2011-05-12 avoid keeping the FD for /config open through hooks
693 argv[0] = path;
75052c2e »
2011-04-26 initial commit
694
b15ea26b »
2011-05-12 avoid keeping the FD for /config open through hooks
695 /* writes to CHILD_WRITE_FD will read back and parsed as environment
696 * variables. the return value is that of read(3). */
697 if (read_child_response(argv, response) > 0) {
698 parse_envstring(response);
75052c2e »
2011-04-26 initial commit
699 }
700 }
701
702 } /* }}} */
703
704 static void check_for_break(void) { /* {{{ */
705 if (getenv("break") == NULL) {
706 return;
707 }
708
709 msg("break requested. type 'exit' or 'logout' to resume\n");
710 start_rescue_shell();
711 } /* }}} */
712
713 static int wait_for_root(void) { /* {{{ */
714 char *rootdelay, *root;
715 int delay = 0;
716
717 root = getenv("root");
718 rootdelay = getenv("rootdelay");
719
aedbf72a »
2011-05-11 add extra checking for wait_for_root slightly
720 if (access(root, F_OK) == 0) {
721 return 0; /* already exists */
722 }
723
75052c2e »
2011-04-26 initial commit
724 if (strncmp(root, "/dev/", 5) != 0) {
725 return 1; /* not a path, so it won't be found */
726 }
727
728 if (rootdelay) {
729 /* atoi is "safe" here because delay<=0 is invalid */
730 delay = atoi(rootdelay);
731 }
732
733 if (delay <= 0) {
734 delay = 10;
735 }
736
737 msg("waiting up to %d seconds for %s ...\n", delay, root);
738
739 delay *= 10; /* we sleep in centiseconds */
740 while (delay--) {
741 if (access(root, F_OK) == 0) {
742 struct stat st;
aedbf72a »
2011-05-11 add extra checking for wait_for_root slightly
743 if (stat(root, &st) == 0) {
744 if (S_ISBLK(st.st_mode)) {
745 return 0; /* found */
746 } else {
747 warn("%s showed up, but it isn't a block device!\n", root);
748 return 1; /* not found? */
749 }
75052c2e »
2011-04-26 initial commit
750 }
751 }
752 usleep(100000); /* .1 seconds */
753 }
754
755 return 1; /* not found */
756 } /* }}} */
757
758 static void try_create_root(void) { /* {{{ */
759 dev_t rootdev;
760 char *root;
761
762 root = getenv("root");
763
2c404400 »
2011-05-08 allow specifying UUID= or LABEL= syntax for root
764 if (strncmp(root, "UUID=", 5) == 0 ||
765 strncmp(root, "LABEL=", 6) == 0) {
766 /* resolve UUID= or LABEL= syntax */
767 char *key, *val, *res;
768
769 key = val = root;
770 strsep(&val, "=");
771
772 res = blkid_evaluate_tag(key, val, NULL);
773 if (!res) {
774 err("failed to resolve %s to a root device", root);
775 return;
776 }
777 root = res;
778
779 /* it may already exist */
780 if (access(root, F_OK) == 0) {
781 setenv("root", root, 1);
782 return;
783 }
784 }
785
786 /* intentional fallthrough from above */
75052c2e »
2011-04-26 initial commit
787 if (strncmp(root, "/dev/", 5) == 0) {
2c404400 »
2011-05-08 allow specifying UUID= or LABEL= syntax for root
788 /* regular block device */
75052c2e »
2011-04-26 initial commit
789 FILE *fp;
790 char path[PATH_MAX], majmin[8];
791
792 snprintf(path, PATH_MAX, "/sys/class/block/%s", root + 6);
793 if (*path && access(path, R_OK) == 0) {
794 fp = fopen(path, "r"); /* this will not fail */
795 fgets(majmin, 8, fp);
796 fclose(fp);
797 setenv("root", majmin, 1);
798 }
799 }
800
801 /* intentional fallthrough from above */
802 if (strchr(root, ':')) {
803 /* major/minor encoding */
804 char *major, *minor;
805
806 major = minor = root;
807 strsep(&minor, ":");
808 rootdev = makedev(atoi(major), atoi(minor));
809 } else if (strtol(root, NULL, 16) > 0) {
810 rootdev = hex2dev(root);
811 } else {
812 /* uhhhhhhhhhhhhh .... ?? */
813 err("unknown device: '%s'. You can try to create "
814 "/dev/root yourself!\n", root);
815 start_rescue_shell();
816 printf("continuing... chance of failure = high\n");
817 return;
818 }
819
aa35bf96 »
2011-05-08 catch invalid root device return from hex2dev
820 if (!major(rootdev) && !minor(rootdev)) {
821 err("invalid root specifier: %s\n", root);
822 return;
823 }
824
75052c2e »
2011-04-26 initial commit
825 if (mknod("/dev/root", 0660|S_IFBLK, rootdev) != 0) {
826 perror("failed to create root device");
827 return;
828 }
829
830 /* only set this now that /dev/root was created successfully */
831 setenv("root", "/dev/root", 1);
832
833 } /* }}} */
834
835 static int mount_root(void) { /* {{{ */
af47f6ef »
2011-05-25 initial support for custom mount handlers
836 char *mount_handler, *root, *fstype, *data;
75052c2e »
2011-04-26 initial commit
837 int ret = 1;
838
af47f6ef »
2011-05-25 initial support for custom mount handlers
839 mount_handler = getenv("mount_handler");
840 if (mount_handler != NULL) {
f4109bbe »
2011-05-26 init: draw mount handlers from /mount. avoid statvfs.
841 struct stat rootdev, newrootdev;
842 char response[BUFSIZ], handlerpath[PATH_MAX];
843 char *argv[] = { handlerpath, NULL };
844
845 snprintf(handlerpath, PATH_MAX, "/mount/%s", mount_handler);
af47f6ef »
2011-05-25 initial support for custom mount handlers
846
847 if (!bbox_installed) { /* unlikely */
848 char *bboxinstall[] = { BUSYBOX, "--install", NULL };
849 forkexecwait(bboxinstall);
850 bbox_installed = 1;
851 }
852
853 if (read_child_response(argv, response) > 0) {
854 parse_envstring(response);
855 }
856
f4109bbe »
2011-05-26 init: draw mount handlers from /mount. avoid statvfs.
857 stat("/", &rootdev);
858 stat(NEWROOT, &newrootdev);
af47f6ef »
2011-05-25 initial support for custom mount handlers
859
f4109bbe »
2011-05-26 init: draw mount handlers from /mount. avoid statvfs.
860 return !(rootdev.st_dev = newrootdev.st_dev);
af47f6ef »
2011-05-25 initial support for custom mount handlers
861 }
862
75052c2e »
2011-04-26 initial commit
863 root = getenv("root");
2a82ab5d »
2011-05-18 init: add support for rootflags var
864 data = getenv("rootflags");
75052c2e »
2011-04-26 initial commit
865 fstype = getenv("rootfstype");
2a82ab5d »
2011-05-18 init: add support for rootflags var
866
75052c2e »
2011-04-26 initial commit
867 if (fstype) {
2a82ab5d »
2011-05-18 init: add support for rootflags var
868 return mount(root, NEWROOT, fstype, rootflags, data);
75052c2e »
2011-04-26 initial commit
869 }
870
871 fstype = probe_fstype(root);
872 if (!fstype) { /* still no fstype, we're out of ideas */
873 /* should hopefully never reach this */
874 err("the filesystem of the root device could not be determined!\n");
875 fprintf(stderr, "Try adding the rootfstype= parameter to the"
876 "kernel command line\n");
877 return ret;
878 }
879
2a82ab5d »
2011-05-18 init: add support for rootflags var
880 ret = mount(root, NEWROOT, fstype, rootflags, data);
75052c2e »
2011-04-26 initial commit
881 free(fstype);
882
883 return ret;
884 } /* }}} */
885
886 static int set_init(void) { /* {{{ */
887 char path[PATH_MAX];
888
889 /* don't overwrite, but make sure something is set */
890 setenv("init", "/sbin/init", 0);
891
892 /* existance check */
893 snprintf(path, PATH_MAX, NEWROOT "%s", getenv("init"));
894 return access(path, F_OK);
895 } /* }}} */
896
3b17a574 »
2011-05-02 refactor handling of the udev daemon
897 static void kill_udev(void) { /* {{{ */
898 /* pid = 0 : we never attempted to start udev
899 * pid = -1 : udev fork failed
fc034a41 »
2011-05-07 comment the 'edge case' where udevd is alive
900 * pid = 1 : udev died at some point
901 * pid > 1 : udevd is alive! */
3b17a574 »
2011-05-02 refactor handling of the udev daemon
902
239441c6 »
2011-05-06 if running, export udev's pid to the environment
903 if (getenv("UDEVPID") == NULL) {
75052c2e »
2011-04-26 initial commit
904 return;
905 }
906
137997d9 »
2011-05-02 implement udevadm as a helper function
907 /* As per upstream, this is the proper way to shut down udev>=168:
908 *
909 * udevadm control --exit
910 * udevadm info --cleanup-db
911 *
912 * What happens on the initramfs is not supposed to make it into later
913 * userspace. These are completely separate environments with different
914 * rules both due to the nature of initramfs as well as the fact that we're
915 * running with a non-standard udev ruleset. The only exception here is
916 * dm/lvm, which requires their udev rules to have OPTIONS+=db_persist added
917 * in order to keep a persistent state through to later userspace. Ideally,
918 * this will someday change and state will be kept in /run/device-mapper
919 * instead. */
920
3b17a574 »
2011-05-02 refactor handling of the udev daemon
921 udevadm("control", "--exit", NULL); /* waits up to 60 seconds */
137997d9 »
2011-05-02 implement udevadm as a helper function
922 udevadm("info", "--cleanup-db", NULL);
75052c2e »
2011-04-26 initial commit
923 } /*}}}*/
924
925 static int switch_root(char *argv[]) { /* {{{ */
926 struct stat st;
927 struct statfs stfs;
928
929 /* this is mostly taken from busybox's util_linux/switch_root.c */
930
931 chdir(NEWROOT);
932 stat("/", &st);
933
934 /* sanity checks: we're about to rm -rf / ! */
935 if (stat("/init", &st) != 0 || !S_ISREG(st.st_mode)) {
936 die("/init not found or not a regular file\n");
937 }
938
939 statfs("/", &stfs); /* this never fails */
940 if ((unsigned)stfs.f_type != RAMFS_MAGIC &&
941 (unsigned)stfs.f_type != TMPFS_MAGIC) {
942 die("root filesystem is not ramfs/tmpfs!\n");
943 }
944
821dd7e2 »
2011-05-10 replace busybox's rm -rf implementation with util-linux's
945 /* zap everything out of root */
946 delete_contents("/");
75052c2e »
2011-04-26 initial commit
947
948 /* mount $PWD over / and chroot into it */
949 if (mount(".", "/", NULL, MS_MOVE, NULL) != 0) {
950 /* fails when newroot is not a mountpoint */
951 die("error moving root\n");
952 }
953 chroot(".");
954
955 /* The chdir is needed to recalculate "." and ".." links */
956 chdir("/");
957
958 /* redirect stdin/stdout/stderr to new console */
e605090c »
2011-04-30 tighten up modprobe execution
959 close(STDIN_FILENO);
75052c2e »
2011-04-26 initial commit
960 open("/dev/console", O_RDWR);
e605090c »
2011-04-30 tighten up modprobe execution
961 dup2(STDIN_FILENO, STDOUT_FILENO);
962 dup2(STDIN_FILENO, STDERR_FILENO);
75052c2e »
2011-04-26 initial commit
963
964 /* exec real pid shady */
965 execv(argv[0], argv);
966 err("failed to execute '%s'\n", argv[0]);
967 fprintf(stderr, ":: This is the end. Something has gone terribly wrong.\n"
968 ":: Please file a detailed bug report.\n");
969 exit(EXIT_FAILURE);
970 } /* }}} */
971
972 int main(int argc, char *argv[]) {
94c37121 »
2011-05-06 cleanup the environment before calling switch_root
973 char *term;
974
75052c2e »
2011-04-26 initial commit
975 (void)argc; /* poor unloved argc */
976
977 mount_setup(); /* create early tmpfs mountpoints */
978 put_cmdline(); /* parse cmdline and set environment */
979 disable_modules(); /* blacklist modules passed in on cmdline */
3b17a574 »
2011-05-02 refactor handling of the udev daemon
980 launch_udev(); /* try to launch udev */
75052c2e »
2011-04-26 initial commit
981 load_extra_modules(); /* load modules passed in on cmdline */
982 trigger_udev_events(); /* read and process uevent queue */
983 disable_hooks(); /* delete hooks specified on cmdline */
90dfe5f8 »
2011-05-18 dmesg: remove builder/hook. add support for loglevel=N.
984 set_kloglevel(); /* set user specified loglevel */
75052c2e »
2011-04-26 initial commit
985 run_hooks(); /* run remaining hooks */
986 check_for_break(); /* did the user request a shell? */
987
988 if (wait_for_root() != 0) {
989 try_create_root(); /* ensure that root shows up */
990 }
991
992 if (mount_root() != 0) { /* this is what we're here for */
993 err("failed to mount the root device: %s\n", strerror(errno));
994 start_rescue_shell();
995 }
996
997 if (set_init() != 0) { /* mounted something, now find init */
998 err("root device was mounted, but %s does not exist!\n", getenv("init"));
999 start_rescue_shell();
1000 }
1001
3b17a574 »
2011-05-02 refactor handling of the udev daemon
1002 kill_udev(); /* shutdown udev in prep switch_root */
75052c2e »
2011-04-26 initial commit
1003
1004 /* migrate to the new root */
1005 movemount("/proc", NEWROOT "/proc");
1006 movemount("/sys", NEWROOT "/sys");
1007 movemount("/run", NEWROOT "/run");
70a60ad2 »
2011-05-01 move /dev to /new_root/dev
1008 movemount("/dev", NEWROOT "/dev");
75052c2e »
2011-04-26 initial commit
1009
94c37121 »
2011-05-06 cleanup the environment before calling switch_root
1010 /* save these... */
75052c2e »
2011-04-26 initial commit
1011 argv[0] = getenv("init");
94c37121 »
2011-05-06 cleanup the environment before calling switch_root
1012 term = getenv("TERM");
1013
1014 /* purge the environment */
1015 clearenv();
1016 setenv("TERM", term, 1);
1017
75052c2e »
2011-04-26 initial commit
1018 switch_root(argv);
1019 /* unreached */
1020 return 0;
1021 }
1022
abdcbb6e »
2011-05-22 init: fix broken vim modeline
1023 /* vim: set et ts=2 sw=2: */
Something went wrong with that request. Please try again.