//-----------------------------------------------------------------------------
// 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
// version	0.8.0
// program	cdinit3 (cd initializer stage 3)
//
// description
//	This program is the stage 3 initializer for the cdinit package.  The
//	stage 2 program will have invoked this program in PID 1.
//
// action
//	1.  Unmount /cdrom
//	2.  Mount /dev/bootcd as /cdrom (same CD as just unmounted)
//	3.  For /opt and /usr:
//		3.1.  If /cdrom/${ARCH}/name exists, bind mount it as /name
//	4.  If /etc/ld.so.cache does not exist, run ldconfig
//	5.  For each program name in init list, try to execve() it
//
//-----------------------------------------------------------------------------
// author	Philip Howard
// email	phil@ipal.org
//-----------------------------------------------------------------------------

#include "cdinit.h"

//-----------------------------------------------------------------------------
// Define list of paths to try to execute.
//-----------------------------------------------------------------------------
static char *	init_list	[] = {
    "/sbin/init",
    "/etc/init",
    "/bin/init",
    "/bin/sh",
    NULL };

//-----------------------------------------------------------------------------
// Define list of directories in / that should be mounted from the CDROM path
// appropriate for this architecture.
//-----------------------------------------------------------------------------
static char *		dirlink_list		[] = {
    "opt",
    "usr",
    NULL };

static char	cdrom_path	[] = "/cdrom/" CDINIT_ARCH_NAME "/xxx";
#define CDROM_PATH_MOUNTPT (cdrom_path+sizeof(cdrom_path)-5)

//-----------------------------------------------------------------------------
// Define argument lists for execve().
//-----------------------------------------------------------------------------
static char *		ldconfig_command	[] = {
    "/sbin/ldconfig",
    NULL };

//-----------------------------------------------------------------------------
// Define an environment for execve().
//-----------------------------------------------------------------------------
static char *		environment		[] = {
    "PATH=/sbin:/bin:/usr/sbin:/usr/bin",
    "HOME=/",
    "TERM=linux",
    NULL };

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

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

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

//-----------------------------------------------------------------------------
// Function setup does setup to be done only once.
//-----------------------------------------------------------------------------
static
int
main1 ()
{
    struct stat			statbuf		;
    char * *			ptr_ptr		;


    //-------------------------------------------------------------------------
    // Remount /cdrom from /dev/bootcd to get the correct name in /proc/mounts.
    //-------------------------------------------------------------------------
    trace2( "umount", "/cdrom" );
    if ( umount( "/cdrom" ) != 0 ) return 2;
    trace4( "umount"+1, "/dev/bootcd", "/cdrom", "iso9660" );
    if ( mount( "/dev/bootcd", "/cdrom", "iso9660", MS_MGC_VAL | MS_RDONLY, 0 ) != 0 ) return 4;

    //----------------------------------------------------------
    // Mount certain CDROM directories onto directories in root.
    //----------------------------------------------------------
    for ( ptr_ptr = dirlink_list; * ptr_ptr; ++ ptr_ptr ) {
	string_copy( CDROM_PATH_MOUNTPT+1, * ptr_ptr );
	if ( stat( cdrom_path, & statbuf ) == 0 ) {
#if 0
	    write_string( 1, "mount -o bind '" );
	    write_string( 1, cdrom_path );
	    write_string( 1, "' '" );
	    write_string( 1, CDROM_PATH_MOUNTPT );
	    write_string( 1, "'\n" );
#endif
	    trace4( "umount"+1, cdrom_path, CDROM_PATH_MOUNTPT, "bind" );
	    if ( mount( cdrom_path, CDROM_PATH_MOUNTPT, "none", MS_BIND | MS_MGC_VAL, 0 ) != 0 ) return 4;
	}
    }

    //----------------------------------------------------
    // Unmount /cdrom because it is no longer needed here.
    //----------------------------------------------------
    trace2( "umount", "/cdrom" );
    if ( umount( "/cdrom" ) != 0 ) return 2;

#if 0
    //----------------------------------------------
    // If no /etc/ld.so.cache then run ldconfig now.
    // This probably should be done in the scripts.
    //----------------------------------------------
    if ( stat( "/etc/ld.so.cache", & statbuf ) != 0 ) {
	int pid, status;
	trace2( "fork", ldconfig_command[0] );
	if ( ( pid = fork() ) < 0 ) return 2;
	if ( pid == 0 ) {
	    trace1( "execve" );
	    execve( ldconfig_command[0], ldconfig_command, environment );
	    return 2;
	}
	trace1( "waitpid" );
	if ( waitpid( pid, & status, 0 ) < 0 ) return 2;
    }
#endif

    //----------------------------------------------------------------------
    // Search for the init program to run in the same order the kernel does.
    // Overload the command name space from ldconfig.
    //----------------------------------------------------------------------
    ptr_ptr = init_list;
    while ( * ptr_ptr ) {
	ldconfig_command[0] = * ptr_ptr;
	execve( ldconfig_command[0], ldconfig_command, environment );
	++ ptr_ptr;
    }
    trace1( "find init program" );
    return 1;
}

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

    errno = 0;

#if 0
    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 (;;);
}

