blob: b4e1f2d5689611a74ae3cb5a3f2624efc8ecce53 [file] [log] [blame]
#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 "kinit.h"
#include "ipconfig.h"
#include "../../utils/run-init.h"
const char *progname = "kinit";
int mnt_procfs;
int mnt_sysfs;
void __attribute__((noreturn)) die(const char *msg)
{
fprintf(stderr, "%s: %s: %s\n", progname, msg, strerror(errno));
exit(1);
}
void dump_args(int argc, char *argv[])
{
(void) argc;
(void) argv;
#ifdef INI_DEBUG
int i;
printf("%s: argc == %d\n", argv[0], argc);
for (i = 1; i < argc; i++) {
printf(" argv[%d]: '%s'\n", i, argv[i]);
}
#endif
}
static int do_ipconfig(int argc, char *argv[])
{
int i, a = 0;
char **args = alloca((argc+1) * sizeof(char *));
if ( !args )
return -1;
args[a++] = (char *) "IP-Config";
DEBUG(("Running ipconfig\n"));
#ifdef INI_DEBUG
args[a++] = (char *) "-n";
#endif
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 *cmdc, char *cmdv[],
char *cmdline,
int argc, char *argv[])
{
char was_space = 1;
char *i = cmdline;
int vmax = *cmdc;
int v = 1, a;
if ( cmdv )
cmdv[0] = argv[0];
while (i && *i && v < vmax) {
if ((*i == ' ' || *i == '\t') && !was_space) {
if ( cmdv )
*i = '\0';
was_space = 1;
} else if (was_space) {
if ( cmdv )
cmdv[v] = i;
v++;
was_space = 0;
}
i++;
}
for (a = 1; a < argc && v < vmax; a++) {
if ( cmdv )
cmdv[v] = argv[a];
v++;
}
if ( cmdv )
cmdv[v] = NULL;
return *cmdc = 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 char *get_kernel_cmdline(char *buf, int len)
{
FILE *fp;
if ((fp = fopen("/proc/cmdline", "r")) == NULL) {
fprintf(stderr, "%s: could not open kernel command line\n",
progname);
buf = NULL;
goto bail;
}
if (fgets(buf, len, fp) == NULL) {
buf = NULL;
goto bail;
}
len = strlen(buf);
if (buf[len - 1] == '\n') {
buf[--len] = '\0';
}
bail:
if (fp) {
fclose(fp);
}
return buf;
}
/* Was this argument passed? */
char *get_arg(int argc, char *argv[], const char *name)
{
int len = strlen(name);
char *ret = NULL;
int i;
for (i = 1; i < argc; i++) {
if (argv[i] && strncmp(argv[i], name, len) == 0 &&
(argv[i][len] != '\0')) {
ret = argv[i] + len;
break;
}
}
return ret;
}
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, "NFS-Root: '%s' not a directory\n", path);
exit(1);
}
}
static const char *find_init(const char *root)
{
const char *init_paths[] = {
"/sbin/init", "/bin/init", "/etc/init", "/bin/sh", NULL
};
const char **p;
if ( chdir(root) ) {
perror("chdir");
exit(1);
}
for ( p = init_paths ; *p ; p++ ) {
if ( !access(*p+1, X_OK) )
break;
}
chdir("/");
return *p;
}
/* 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;
char buf[1024];
char *cmdline;
int ret = 0;
int cmdc;
int fd;
/* 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);
}
}
if ((mnt_procfs = mount_sys_fs("/proc/cmdline", "/proc", "proc")) == -1) {
ret = 1;
goto bail;
}
ret = readfile("/proc/cmdline", &cmdline);
if ((mnt_sysfs = mount_sys_fs("/sys/bus", "/sys", "sysfs")) == -1) {
ret = 1;
goto bail;
}
if ((cmdline = get_kernel_cmdline(buf, sizeof(buf))) == NULL) {
ret = 1;
goto bail;
}
cmdc = INT_MAX;
cmdv = (char **) alloca(sizeof(char *) *
(split_cmdline(&cmdc, NULL, cmdline, argc, argv+1)));
if (split_cmdline(&cmdc, cmdv, cmdline, argc, argv) == 0) {
ret = 1;
goto bail;
}
do_ipconfig(cmdc, cmdv);
check_path("/root");
do_mounts(cmdc, cmdv);
/* do_mounts cd's to /root so below tests /root/old_root */
check_path("old_root");
if (mnt_procfs == 1)
umount2("/proc", 0);
if (mnt_sysfs == 1)
umount2("/sys", 0);
init_path = find_init("/root");
if ( !init_path ) {
fprintf(stderr, "%s: init not found!\n", progname);
ret = 2;
goto done;
}
init_argv[0] = strrchr(init_path, '/')+1;
run_init("/root", "/dev/console", init_path, init_argv);
/* run_init shouldn't fail; it rather calls die(). */
fprintf(stderr, "%s: failed to run init for unknown reason...\n", progname);
ret = 2;
goto done;
bail:
if (mnt_procfs == 1)
umount2("/proc", 0);
if (mnt_sysfs == 1)
umount2("/sys", 0);
done:
exit(ret);
}