//-----------------------------------------------------------------------------
// Copyright © 2001 - Philip Howard - All rights reserved, released under GPL
//
// 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; either version 2
// of the License, or (at your option) any later version.
//
// 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, write to the Free Software
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//-----------------------------------------------------------------------------
// package	cdinit
// program	cdinit1 (cd initializer stage 1)
//
// description
//	This program is the stage 1 initializer for the cdinit package.	 The
//	initial ram disk will contain this program as /etc/init.  The kernel
//	will unpack and mount the initial ramdisk and execute /etc/init as
//	PID 1 thus invoking cdinit1.
//
//      1.  Make directory /tmpfs
//      2.  Mount a tmpfs filesystem as /tmpfs
//      3.  Make various directories in /tmpfs
//      4.  Make enough directories in / to operate with
//      5.  For each CDROM device number in list:
//              5.1  Make device node /tmpfs/dev/bootcd
//              5.2  Mount /tmpfs/dev/bootcd as /tmpfs/cdrom
//              5.3  If mounted, execve("/tmpfs/cdrom/${ARCH}/boot/cdinit2",...)
//              5.4  Unlink /dev/bootcd
//
//	See description of cdinit2.c for continuation.
//-----------------------------------------------------------------------------
// author	Philip Howard
// email	phil@ipal.org
//-----------------------------------------------------------------------------

#include "cdinit.h"

//-----------------------------------------------------------------------------
// Define list of devices to try to mount to find the CDROM.
// This list is not complete.  More CDROM types should be added.
//-----------------------------------------------------------------------------
static const dev_t	cdrom_devices	[] = {
    makedev(3,0),	// hda
    makedev(3,64),	// hdb
    makedev(22,0),	// hdc
    makedev(22,64),	// hdd
#ifdef CDINIT_MANY_DEVS
    makedev(33,0),	// hde
    makedev(33,64),	// hdf
    makedev(34,0),	// hdg
    makedev(34,64),	// hdh
#endif
    makedev(11,0),	// scd0
    makedev(11,1),	// scd1
#ifdef CDINIT_MANY_DEVS
    makedev(11,2),	// scd2
    makedev(11,3),	// scd3
    makedev(11,4),	// scd4
    makedev(11,5),	// scd5
    makedev(11,6),	// scd6
    makedev(11,7),	// scd7
#endif
    0 };

//-----------------------------------------------------------------------------
// Define list of directories to make in / in the specified order.  These will
// be made via /tmpfs before it is pivoted to root.
//-----------------------------------------------------------------------------
static char *		rootdir_list		[] = {
    "bin",
    "boot",
    "cdrom",
    "dev",
    "etc",
    "home",
    "lib",
    "mnt",
    "opt",
    "proc",
    "root",
    "sbin",
    "tmp",
    "usr",
    "var",
    NULL };

//-----------------------------------------------------------------------------
// Define argument lists and environment for execve().
//-----------------------------------------------------------------------------
static char * const	cdinit2_command	[] = {
    "/tmpfs/cdrom/" CDINIT_ARCH_NAME "/boot/cdinit2",
    NULL
};

//-----------------------------------------------------------------------------
// Define an environment for execve().
//-----------------------------------------------------------------------------
static char *		environment	[] = {
    NULL
};

//-----------------------------------------------------------------------------
// Where to store trace info.
//-----------------------------------------------------------------------------
#ifdef CDINIT_TRACE
static const char *	trace	[4];
#endif

//-----------------------------------------------------------------------------
// Include functions.
//-----------------------------------------------------------------------------
#include "write_string.c"

#include "write_dec_ulong.c"

#ifdef CDINIT_TRACE
#include "write_dec_slong.c"
#endif

//-----------------------------------------------------------------------------
// function	main1
//-----------------------------------------------------------------------------
static
int
main1 ()
{
    char * *			ptr_ptr		;
    const dev_t *		dev_ptr		;

    //--------------------------------------
    // Make directories we need temporarily.
    //--------------------------------------
    trace2( "mkdir", "/tmpfs" );
    if ( mkdir( "/tmpfs", 0755 ) != 0 ) return 2;
    trace2( "mkdir", "/mnt" );
    if ( mkdir( "/mnt", 0755 ) != 0 ) return 2;

    //--------------------------------------------------------
    // Mount the tmpfs filesystem that will later become root.
    //--------------------------------------------------------
    trace4( "umount"+1, "/tmpfs"+1, "/tmpfs", "/tmpfs"+1 );
    if ( mount( "/tmpfs"+1, "/tmpfs", "/tmpfs"+1, MS_MGC_VAL, 0 ) != 0 ) return 4;

    //----------------------------------------------------------------
    // Make the basic set of directories in the future root directory.
    //----------------------------------------------------------------
    trace2( "chdir", "/tmpfs" );
    if ( chdir( "/tmpfs" ) != 0 ) return 2;
    for ( ptr_ptr = rootdir_list; * ptr_ptr; ++ ptr_ptr ) {
        trace2( "mkdir", * ptr_ptr );
        if ( mkdir( * ptr_ptr, 0755 ) != 0 ) return 2;
    }
    trace2( "chdir", "/" );
    if ( chdir( "/" ) != 0 ) return 2;

    //----------------------------------------------------------
    // Search for the boot CD by looking for the first CD that
    // can be mounted for which the stage 2 loader will execute.
    //----------------------------------------------------------
    for ( dev_ptr = cdrom_devices; * dev_ptr; ++ dev_ptr ) {
	trace2( "mknod", "/tmpfs/dev/bootcd" );
	if ( mknod( "/tmpfs/dev/bootcd", S_IFBLK + 0755, * dev_ptr ) != 0 ) return 2;
	if ( mount( "/tmpfs/dev/bootcd", "/tmpfs/cdrom", "iso9660", MS_MGC_VAL | MS_RDONLY, 0 ) == 0 ) {
	    trace2( "execve", cdinit2_command[0] );
	    execve( cdinit2_command[0], cdinit2_command, environment );
	    trace2( "umount", "/tmpfs/cdrom" );
	    if ( umount( "/tmpfs/cdrom" ) != 0 ) return 2;
	}
	trace2( "unlink", "/tmpfs/dev/bootcd" );
	if ( unlink( "/tmpfs/dev/bootcd" ) != 0 ) return 2;
    }
    trace1( "find CDROM and cdinit2" );
    return 1;
}

//-----------------------------------------------------------------------------
// Function _start is really the main function.
//-----------------------------------------------------------------------------
void
_start ()
{
    int		rc		;

    errno = 0;

#if 1
    write_string( 1,
		  "\r"
#ifdef CDINIT_COLOR
		  "\033[33;1m"
#endif
		  "cdinit v" CDINIT_VERSION
		  ", "
		  "Copyright (C) 2001, Philip Howard, All rights reserved, See GPL"
#ifdef CDINIT_COLOR
		  "\033[0m"
#endif
		  "\n" );
#endif

    rc = main1();
#ifdef CDINIT_TRACE
    if ( rc != 0 ) {
	int i;
	write_string( 1, "Error " );
	write_dec_slong( 1, (signed long) ( errno ) );
	write_string( 1, " doing:" );
	for ( i = 0; i < rc; ++ i ) {
	    write_string( 1, " " );
	    write_string( 1, trace[i] );
	}
	write_string( 1, "\n" );
    }
#endif

    write_string( 1, "halting ... \n\n\n\n\n\n" );
    reboot( LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2, LINUX_REBOOT_CMD_HALT, NULL );
    for (;;);
}

