| #include <sys/mount.h> |
| #include <sys/stat.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| #include <stdio.h> |
| #include <stdlib.h> |
| #include <string.h> |
| #include <alloca.h> |
| #include <limits.h> |
| #include <ctype.h> |
| #include <termios.h> |
| |
| #include "kinit.h" |
| #include "ipconfig.h" |
| #include "run-init.h" |
| #include "resume.h" |
| |
| const char *progname = "kinit"; |
| int mnt_procfs; |
| int mnt_sysfs; |
| |
| #ifdef DEBUG |
| void dump_args(int argc, char *argv[]) |
| { |
| int i; |
| |
| printf(" argc == %d\n", argc); |
| |
| for (i = 0; i < argc; i++) |
| printf(" argv[%d]: \"%s\"\n", i, argv[i]); |
| |
| if (argv[argc] != NULL) |
| printf(" argv[%d]: \"%s\" (SHOULD BE NULL)\n", |
| argc, argv[argc]); |
| } |
| #endif /* DEBUG */ |
| |
| |
| static int do_ipconfig(int argc, char *argv[]) |
| { |
| int i, a = 0; |
| char **args = alloca((argc + 3) * sizeof(char *)); |
| |
| if (!args) |
| return -1; |
| |
| args[a++] = (char *)"IP-Config"; |
| args[a++] = (char *)"-i"; |
| args[a++] = (char *)"Linux kinit"; |
| |
| dprintf("Running ipconfig\n"); |
| |
| for (i = 1; i < argc; i++) { |
| if (strncmp(argv[i], "ip=", 3) == 0 || |
| strncmp(argv[i], "nfsaddrs=", 9) == 0) { |
| args[a++] = argv[i]; |
| } |
| } |
| |
| if (a > 1) { |
| args[a] = NULL; |
| dump_args(a, args); |
| return ipconfig_main(a, args); |
| } |
| |
| return 0; |
| } |
| |
| static int split_cmdline(int cmdcmax, char *cmdv[], char *argv0, |
| char *cmdlines[], char *args[]) |
| { |
| int was_space; |
| char c, *p; |
| int vmax = cmdcmax; |
| int v = 1; |
| int space; |
| |
| if (cmdv) |
| cmdv[0] = argv0; |
| |
| /* First, add the parsable command lines */ |
| |
| while (*cmdlines) { |
| p = *cmdlines++; |
| was_space = 1; |
| while (v < vmax) { |
| c = *p; |
| space = isspace(c); |
| if ((space || !c) && !was_space) { |
| if (cmdv) |
| *p = '\0'; |
| v++; |
| } else if (was_space) { |
| if (cmdv) |
| cmdv[v] = p; |
| } |
| |
| if (!c) |
| break; |
| |
| was_space = space; |
| p++; |
| } |
| } |
| |
| /* Second, add the explicit command line arguments */ |
| |
| while (*args && v < vmax) { |
| if (cmdv) |
| cmdv[v] = *args; |
| v++; |
| args++; |
| } |
| |
| if (cmdv) |
| cmdv[v] = NULL; |
| |
| return v; |
| } |
| |
| static int mount_sys_fs(const char *check, const char *fsname, |
| const char *fstype) |
| { |
| struct stat st; |
| |
| if (stat(check, &st) == 0) |
| return 0; |
| |
| mkdir(fsname, 0555); |
| |
| if (mount("none", fsname, fstype, 0, NULL) == -1) { |
| fprintf(stderr, "%s: could not mount %s as %s\n", |
| progname, fsname, fstype); |
| return -1; |
| } |
| |
| return 1; |
| } |
| |
| static void check_path(const char *path) |
| { |
| struct stat st; |
| |
| if (stat(path, &st) == -1) { |
| if (errno != ENOENT) { |
| perror("stat"); |
| exit(1); |
| } |
| if (mkdir(path, 0755) == -1) { |
| perror("mkdir"); |
| exit(1); |
| } |
| } else if (!S_ISDIR(st.st_mode)) { |
| fprintf(stderr, "%s: '%s' not a directory\n", progname, path); |
| exit(1); |
| } |
| } |
| |
| static const char *find_init(const char *root, const char *user) |
| { |
| const char *init_paths[] = { |
| "/sbin/init", "/bin/init", "/etc/init", "/bin/sh", NULL |
| }; |
| const char **p; |
| const char *path; |
| |
| if (chdir(root)) { |
| perror("chdir"); |
| exit(1); |
| } |
| |
| if (user) |
| dprintf("Checking for init: %s\n", user); |
| |
| if (user && user[0] == '/' && !access(user+1, X_OK)) { |
| path = user; |
| } else { |
| for (p = init_paths; *p; p++) { |
| dprintf("Checking for init: %s\n", *p); |
| if (!access(*p+1, X_OK)) |
| break; |
| } |
| path = *p; |
| } |
| chdir("/"); |
| return path; |
| } |
| |
| /* This is the argc and argv we pass to init */ |
| const char *init_path; |
| int init_argc; |
| char **init_argv; |
| |
| extern ssize_t readfile(const char *, char **); |
| |
| int main(int argc, char *argv[]) |
| { |
| char **cmdv, **args; |
| char *cmdlines[3]; |
| int i; |
| const char *errmsg; |
| int ret = 0; |
| int cmdc; |
| int fd; |
| struct timeval now; |
| |
| gettimeofday(&now, NULL); |
| srand48(now.tv_usec ^ (now.tv_sec << 24)); |
| |
| /* Default parameters for anything init-like we execute */ |
| init_argc = argc; |
| init_argv = alloca((argc+1)*sizeof(char *)); |
| memcpy(init_argv, argv, (argc+1)*sizeof(char *)); |
| |
| if ((fd = open("/dev/console", O_RDWR)) != -1) { |
| dup2(fd, STDIN_FILENO); |
| dup2(fd, STDOUT_FILENO); |
| dup2(fd, STDERR_FILENO); |
| |
| if (fd > STDERR_FILENO) |
| close(fd); |
| } |
| |
| mnt_procfs = mount_sys_fs("/proc/cmdline", "/proc", "proc") >= 0; |
| if (!mnt_procfs) { |
| ret = 1; |
| goto bail; |
| } |
| |
| mnt_sysfs = mount_sys_fs("/sys/bus", "/sys", "sysfs") >= 0; |
| if (!mnt_sysfs) { |
| ret = 1; |
| goto bail; |
| } |
| |
| /* Construct the effective kernel command line. The |
| effective kernel command line consists of /arch.cmd, if |
| it exists, /proc/cmdline, plus any arguments after an -- |
| argument on the proper command line, in that order. */ |
| |
| ret = readfile("/arch.cmd", &cmdlines[0]); |
| if (ret < 0) |
| cmdlines[0] = ""; |
| |
| ret = readfile("/proc/cmdline", &cmdlines[1]); |
| if (ret < 0) { |
| fprintf(stderr, "%s: cannot read /proc/cmdline\n", progname); |
| ret = 1; |
| goto bail; |
| } |
| |
| cmdlines[2] = NULL; |
| |
| /* Find an -- argument, and if so append to the command line */ |
| for (i = 1; i < argc; i++) { |
| if (!strcmp(argv[i], "--")) { |
| i++; |
| break; |
| } |
| } |
| args = &argv[i]; /* Points either to first argument past -- or |
| to the final NULL */ |
| |
| /* Count the number of arguments */ |
| cmdc = split_cmdline(INT_MAX, NULL, argv[0], cmdlines, args); |
| |
| /* Actually generate the cmdline array */ |
| cmdv = (char **)alloca((cmdc+1)*sizeof(char *)); |
| if (split_cmdline(cmdc, cmdv, argv[0], cmdlines, args) != cmdc) { |
| ret = 1; |
| goto bail; |
| } |
| |
| /* Debugging... */ |
| dump_args(cmdc, cmdv); |
| |
| /* Resume from suspend-to-disk, if appropriate */ |
| /* If successful, does not return */ |
| do_resume(cmdc, cmdv); |
| |
| /* Initialize networking, if applicable */ |
| do_ipconfig(cmdc, cmdv); |
| |
| check_path("/root"); |
| do_mounts(cmdc, cmdv); |
| |
| if (mnt_procfs) { |
| umount2("/proc", 0); |
| mnt_procfs = 0; |
| } |
| |
| if (mnt_sysfs) { |
| umount2("/sys", 0); |
| mnt_sysfs = 0; |
| } |
| |
| init_path = find_init("/root", get_arg(cmdc, cmdv, "init=")); |
| if (!init_path) { |
| fprintf(stderr, "%s: init not found!\n", progname); |
| ret = 2; |
| goto bail; |
| } |
| |
| init_argv[0] = strrchr(init_path, '/') + 1; |
| |
| errmsg = run_init("/root", "/dev/console", |
| get_arg(cmdc, cmdv, "drop_capabilities="), |
| init_path, init_argv); |
| |
| /* If run_init returned, something went bad */ |
| fprintf(stderr, "%s: %s: %s\n", progname, errmsg, strerror(errno)); |
| ret = 2; |
| goto bail; |
| |
| bail: |
| if (mnt_procfs) |
| umount2("/proc", 0); |
| |
| if (mnt_sysfs) |
| umount2("/sys", 0); |
| |
| /* |
| * If we get here, something bad probably happened, and the kernel |
| * will most likely panic. Drain console output so the user can |
| * figure out what happened. |
| */ |
| tcdrain(2); |
| tcdrain(1); |
| |
| return ret; |
| } |