#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/syscall.h>
#include <unistd.h>

// FIXME: Make this configurable.
#define LUNA_ROOT_PARTITION "/dev/cd0p2"

static void mount_devfs()
{
    if (mkdir("/dev", 0755) < 0 && errno != EEXIST && errno != EROFS) exit(255);

    if (mount("/dev", "devfs", "devfs") < 0) exit(255);
}

static void open_std_streams()
{
    stdin = fopen("/dev/console", "r");
    stdout = fopen("/dev/console", "w");
    stderr = fopen("/dev/console", "w");
}

static void fail(const char* message)
{
    open_std_streams();
    fprintf(stderr, "preinit: fatal error: %s\n", message);
    exit(255);
}

static void fail_errno(const char* message)
{
    int err = errno;
    open_std_streams();
    fprintf(stderr, "preinit: fatal error: %s (%s)\n", message, strerror(err));
    exit(255);
}

static void mount_rootfs()
{
    if (mkdir("/osroot", 0755) < 0 && errno != EEXIST) exit(255);

    if (mount("/osroot", "ext2", LUNA_ROOT_PARTITION) < 0) fail_errno("Cannot mount the root partition");
}

int main()
{
    if (getpid() != 1)
    {
        fprintf(stderr, "error: preinit must be run as the init process.\n");
        return 1;
    }

    mount_devfs();
    mount_rootfs();

    struct stat st;
    if (stat("/osroot/mnt", &st) < 0 || !S_ISDIR(st.st_mode)) fail("No suitable temporary mountpoint for pivot_root");

    // Now that we have mounted the root file system, remove the /dev mount on the current ramfs root.
    umount("/dev");

    long rc = syscall(SYS_pivot_root, "/osroot", "/osroot/mnt");
    if (rc < 0) exit(255);

    chdir("/");
    umount("/mnt");

    // Now, mount the /dev file system on the new root.
    mount_devfs();

    setenv("PATH", "/sbin:/usr/bin", 1);
    char* argv[] = { "init", nullptr };
    char* envp[] = { nullptr };
    execvpe(argv[0], argv, envp);

    fail_errno("Failed to execute init");

    return 255;
}