summaryrefslogtreecommitdiffstats
path: root/usr/kinit/kinit.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/kinit/kinit.c')
-rw-r--r--usr/kinit/kinit.c331
1 files changed, 331 insertions, 0 deletions
diff --git a/usr/kinit/kinit.c b/usr/kinit/kinit.c
new file mode 100644
index 0000000..28d2953
--- /dev/null
+++ b/usr/kinit/kinit.c
@@ -0,0 +1,331 @@
+#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="), false,
+ false, 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;
+}