/**
   qemuinit; init for quick boot inside qemu.
   Copyright 2009 Junichi Uekawa

   This init is dedicated for qemubuilder use, with goal of having a
   very fast boot time by only initializing the essential modules
   required for qemubuilder operation.

 */
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/mount.h>
#include <sys/reboot.h>

#include "../parameter.h"
#include "../file.h"

#define PBUILDER_MOUNTPOINT "/var/cache/pbuilder/pbuilder-mnt"
#define ROOT_MOUNTPOINT "/root"

/* global variable to hold /proc/cmdline */
char* proc_cmdline = NULL;

void read_proc_cmdline()
{
  int fd = open("/proc/cmdline", O_RDONLY);
  char buf[BUFSIZ];
  int len=read(fd, buf, sizeof(buf)-1);
  buf[len]=0;			/* make sure it's NULL-terminated */
  proc_cmdline=strdup(buf);
  close(fd);
}

/* parse the command-line option, and return a pointer to
space-delimited option string */
const char* parse_option(const char* option)
{
  const char* s = proc_cmdline;
  do {
    if (!strncmp(option, s, strlen(option)) &&
	s[strlen(option)] == '=')
      return s;
  } while((s=strchr(s, ' ')));
  return NULL;
}

/*
   Return a strdup string, optionally NULL-terminating if it's
   space-delimited.
 */
char* strdup_spacedelimit(const char* str)
{
  char* s = strdup(str);
  char* space = strchr(str, ' ');
  if (space) *space = 0;
  return s;
}

/*
   insert module and display error message when insmod returns an
   error.
 */
int insmod(const char* module)
{
  int ret=forkexeclp("/bin/insmod", "/bin/insmod", module, NULL);
  if (ret)
    {
      fprintf(stderr, "W: insmod of %s returned %i\n",
	      module,
	      ret);
    }
  return ret;

}

int main(int argc, char** argv)
{
  printf(" -> qemu-pbuilder first-stage(initrd version)\n");

  umask(S_IWGRP | S_IWOTH);

  chdir("/");
  setenv("PATH", "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin", 1);
  setenv("PBUILDER_MOUNTPOINT", PBUILDER_MOUNTPOINT, 1);

  mkdir("/root", 0777);		/* mountpoint */
  mkdir("/proc", 0777);
  mkdir("/dev", 0777);
  mkdir("/dev/pts", 0777);

  if (mount("/proc", "/proc",
	    "proc", MS_MGC_VAL, NULL) == -1)
    {
      perror("mount proc");
    }

  if (mount("/dev/pts", "/dev/pts",
	    "devpts", MS_MGC_VAL, NULL) == -1)
    {
      perror("mount devpts");
    }

  read_proc_cmdline();

  /* ext3 driver */
  insmod("lib/modules/linux-live/kernel/drivers/ide/ide-core.ko");
  insmod("lib/modules/linux-live/kernel/drivers/ide/ide-disk.ko");
  insmod("lib/modules/linux-live/kernel/drivers/ide/ide-generic.ko");
  insmod("lib/modules/linux-live/kernel/fs/mbcache.ko");
  insmod("lib/modules/linux-live/kernel/fs/jbd/jbd.ko");
  insmod("lib/modules/linux-live/kernel/fs/ext2/ext2.ko");
  insmod("lib/modules/linux-live/kernel/fs/ext3/ext3.ko");
  sleep(1);

  /* ne2k driver */
  insmod("lib/modules/linux-live/kernel/drivers/net/8390.ko");
  insmod("lib/modules/linux-live/kernel/drivers/net/ne2k-pci.ko");
  /* ARM would need different driver... smc */

  if (copy_file("/proc/mounts", "/etc/mtab"))
    {
      fprintf(stderr, "Cannot copy file /proc/mounts to /etc/mtab.\n");
    }

  mknod_inside_chroot("", "dev/sda", S_IFBLK | 0660, makedev(8, 0));
  mknod_inside_chroot("", "dev/sdb", S_IFBLK | 0660, makedev(8, 16));
  mknod_inside_chroot("", "dev/hda", S_IFBLK | 0660, makedev(3, 0));
  mknod_inside_chroot("", "dev/hdb", S_IFBLK | 0660, makedev(3, 64));

  if (mount("/dev/sda", ROOT_MOUNTPOINT, "ext3",
	     MS_MGC_VAL, NULL) == -1)
    {
      perror("mount root device try sda");
      if (mount("/dev/hda", ROOT_MOUNTPOINT, "ext3",
		MS_MGC_VAL, NULL) == -1)
	{
	  perror("mount root device try hda");
	}
    }

  if (mount("/dev/sdb",
	    ROOT_MOUNTPOINT PBUILDER_MOUNTPOINT,
	    "ext3", MS_MGC_VAL, NULL) == -1)
    {
      perror("mount command device try sdb");
      if (mount("/dev/hdb",
		ROOT_MOUNTPOINT PBUILDER_MOUNTPOINT,
		"ext3", MS_MGC_VAL, NULL) == -1)
	{
	   perror("mount command device try hdb");
	}
    }

  /* debug shell to see what's there. */
  printf("command-line inside initrd, exit to continue\n");
  forkexeclp("/bin/sh", "/bin/sh", NULL);

  if (chroot("root/"))
    {
      perror("chroot");
    }

  if (chdir("/"))
    {
      perror("chdir");
    }

  forkexeclp("bin/sh", "bin/sh", PBUILDER_MOUNTPOINT "/pbuilder-run", NULL);

  /* debug shell inside chroot. */
  printf("command-line inside chroot, exit to continue\n");
  forkexeclp("bin/sh", "bin/sh", NULL);

  /* turn the machine off */
  sync();
  sync();
  reboot(RB_POWER_OFF);
  return 0;
}
