/* $Id$ */
/* Copyright (c) 2008-2014 Pierre Pronchery <khorben@defora.org> */
/* This file is part of DeforaOS Unix others */
/* This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>. */



#include <sys/types.h>
#include <sys/param.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>

/* portability */
#if defined(MNT_RDONLY)	/* FreeBSD, NetBSD */
# include <isofs/cd9660/cd9660_mount.h>
# ifndef MOUNT_CD9660
#  define MT_ISO9660		"cd9660"
# else
#  define MT_ISO9660		MOUNT_CD9660
# endif
# ifndef MOUNT_PROCFS
#  define MT_PROCFS		"procfs"
# else
#  define MT_PROCFS		MOUNT_PROCFS
# endif
# ifndef MNT_NODEV
#  define MF_NODEV		NODEV
# else
#  define MF_NODEV		MNT_NODEV
# endif
# define MF_NOEXEC		MNT_NOEXEC
# define MF_NOSUID		MNT_NOSUID
# define MF_RDONLY		MNT_RDONLY
# if defined(__APPLE__) || defined(__FreeBSD__) || (defined(__NetBSD__) \
			&& (!defined(__NetBSD_Version__) \
				|| __NetBSD_Version__ < 499000000)) /* XXX check */
#  define mount(type, dir, flags, data, data_len) \
	mount(type, dir, flags, data)
# endif
#elif !defined(MT_ISO9660)
# define MF_ASYNC		MS_ASYNC
# define MF_NODEV		MS_NODEV
# define MF_NOEXEC		MS_NOEXEC
# define MF_NOSUID		MS_NOSUID
# define MF_RDONLY		MS_RDONLY
# define MT_ISO9660		"iso9660"
# define MT_PROCFS		"proc"
struct iso_args
{
	char const * fspec;
};
# define mount(type, dir, flags, data, data_len) \
	mount(data ? ((struct iso_args *)data)->fspec : NULL, dir, type, \
				flags, NULL)
# define unmount(dir, flags)	umount(dir)
#endif

/* constants */
#define PACKAGE			"linuxrc"

#define CDROM_PATH		"/mnt/cdrom"
#define DEV_CDROMX		"/dev/cdroms/cdromX"
#define INIT_PATH		CDROM_PATH "/sbin/init"
#define PROC_REAL_ROOT_DEV	"/proc/sys/kernel/real-root-dev"


/* linuxrc_error */
static int _linuxrc_error(char const * message, int ret)
{
	fputs(PACKAGE ": ", stderr);
	perror(message);
	return ret;
}


/* linuxrc_mount_cdrom */
static int _linuxrc_mount_cdrom(char * source, char const * dir)
{
	struct iso_args ia;

	memset(&ia, 0, sizeof(ia));
	ia.fspec = source;
#ifdef DEBUG
	fprintf(stderr, "DEBUG: mount -t %s %s %s\n", MT_ISO9660,
			((struct iso_args *)&ia)->fspec, dir);
#endif
	return mount(MT_ISO9660, dir, MF_NOSUID | MF_NODEV | MF_RDONLY, &ia,
			sizeof(ia));
}


/* main */
int main(void)
{
	size_t i;
	char dev_cdrom[] = DEV_CDROMX;
	const char dev_root[] = "0x100\n";
	struct stat st;
	int found = 0;
	FILE * fp;

	/* mount /proc */
	if(mount(MT_PROCFS, "/proc", MF_NOEXEC | MF_NOSUID | MF_NODEV, NULL, 0)
			!= 0)
		_linuxrc_error("/proc", 0);
	/* look for the installation CD-ROM */
	for(i = 0; i < 4; i++)
	{
		dev_cdrom[sizeof(dev_cdrom) - 2] = i + '0';
		if(_linuxrc_mount_cdrom(dev_cdrom, CDROM_PATH) != 0)
		{
			_linuxrc_error(dev_cdrom, 0);
			continue;
		}
		if(stat(INIT_PATH, &st) != 0)
		{
			found = 1;
			break;
		}
		_linuxrc_error(INIT_PATH, 0);
		unmount(dev_cdrom, 0); /* FIXME check the flags */
	}
	if(!found)
	{
		fputs(PACKAGE ": Could not find the bootable CD-ROM\n", stderr);
		return 2;
	}
	if((fp = fopen(PROC_REAL_ROOT_DEV, "w")) == NULL)
		return _linuxrc_error(PROC_REAL_ROOT_DEV, 0);
	if(fwrite(dev_root, sizeof(*dev_root), sizeof(dev_root) - 1, fp)
			!= sizeof(dev_root) - 1)
		_linuxrc_error(PROC_REAL_ROOT_DEV, 0);
	if(fclose(fp) != 0)
		_linuxrc_error(PROC_REAL_ROOT_DEV, 0);
	return 0;
}
