/***********************************************
 *
 * File:
 *	init.c
 *
 * Purpose:
 *	init will be the init process on the
 *	EBSA, it will remount the filesystem
 *	in read/write state, mount the proc
 *	filesystem, run the init script,
 *	it will then launch a shell and
 *	monitor the shell, if it exits init
 * 	will restart the shell.
 *	Added the ability to have the init
 *	process send its debugging and status
 *	info back over a socket to the host.
 *
 * Written by:
 *	Benjamin Stocks
 *	Paul Raisanen
 *
 * Modified by:
 *	Benjamin Stocks 01/30/2000
 *	Added the socket stuff for sending the
 *	errors and status back over a socket
 *	connection.
 *
 **********************************************/

/* System includes */
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 
#include 

/***********************************************
 *
 * Function:
 *	remount
 *
 * Purpose:
 *	remount will fork off a process to 
 *	call the mount command to remount the 
 *	filesystem in read/write state.
 *	All status messages and error messages
 *	will be written to the file descriptor
 *	passed in, it should be socket connection
 *	in this version of init.
 *
 * Calls:
 *	sprintf()
 *	fork()
 *	execlp()
 *	wait()
 *	memset()
 *	write() 
 *
 * Parameters: 
 *	sockfd - A file descriptor to an open
 *		 socket for debugging and error
 *		 messages. 
 *
 * Returns:
 *	An integer containing the error returned
 *	from the execlp if any, zero otherwise.
 *
 * Written by:
 *	Benjamin Stocks
 *	Paul Raisanen
 *
 * Modified by:
 *	Benjamin Stocks 01/30/2000
 *	Added the socket writing code.
 * 
 **********************************************/
int remount(int sockfd)
{
	pid_t childpid;
	int status;
	char message[50];
	int error = 0;
	
	if((childpid = fork()) == 0) 
	{
		memset(message,'\0',sizeof(message));
		sprintf(message,"remounting filesystem\n");
		if(sockfd > 0) {
			write(sockfd,message,strlen(message));
		}

		error = execlp("mount","mount","-o","remount", "-rw","/",0);
		if(error < 0) {
			memset(message,'\0',sizeof(message));
			if(sockfd > 0) {
				sprintf(message,"remount error: %s\n",strerror(error));
				write(sockfd,message,strlen(message));
			}
		}
	}
	wait(&status);
	return error;
}

/***********************************************
 *
 * Function:
 *      runinitscript 
 *
 * Purpose:
 *	runinitscript will run the /etc/rc
 *	init script and capture its output for
 *	writing to the socket passed in.
 *
 * Calls:
 *	memset()
 *	popen()
 *	fread()
 *	pclose()
 *	sprintf()
 *	write()
 *
 * Parameters: 
 *	sockfd - The open socket connection
 *		 for the status and error 
 *		 messages.
 *
 * Returns:
 *      An integer containing the error returned
 *	if any.
 *
 * Written by:
 *      Benjamin Stocks
 *
 * Modified by:
 *      Benjamin Stocks 01/30/2000
 *      Added the socket writing code.
 * 
 **********************************************/
int runinitscript(int sockfd)
{
	FILE *read_fp;
	char buffer[BUFSIZ + 1];
	int chars_read;
	int error = 0;
		
	memset(buffer,'\0',sizeof(buffer));
	sprintf(buffer,"running init scripts\n");
	if(sockfd > 0) {
		write(sockfd,buffer,strlen(buffer));
	}
	memset(buffer,'\0',sizeof(buffer));

	read_fp = popen("/bin/sh /etc/rc 2>&1","r");
	if(read_fp != NULL) 
	{
		chars_read = fread(buffer,sizeof(char),BUFSIZ,read_fp);
		while(chars_read > 0)
		{
			memset(buffer,'\0',sizeof(buffer));
			chars_read = fread(buffer,sizeof(char),BUFSIZ, read_fp);
			if(sockfd > 0) {
				write(sockfd,buffer,strlen(buffer));
			}
		}
		pclose(read_fp);
	}
	return error;
}

/***********************************************
 *
 * Function:
 *      mountproc
 *
 * Purpose:
 *	mountproc will mount the proc file-
 *	system and pass off any status or
 *	error messages to the open socket
 *	passed into it.	
 *
 * Calls:
 *	mount()
 *	memset()
 *	write()
 *
 * Parameters: 
 *	sockfd - An open file descriptor to 
 *		 the socket to write to.
 *
 * Returns:
 *      An integer containing the error returned
 *      from the mount if any, zero otherwise.
 *
 * Written by:
 *      Benjamin Stocks
 *
 * Modified by:
 *      Benjamin Stocks 01/30/2000
 *      Added the socket writing code.
 * 
 **********************************************/
int mountproc(int sockfd)
{
	char message[50];
	int error = 0;

	memset(message,'\0',sizeof(message));
	sprintf(message,"mounting proc filesystem\n");
	if(sockfd > 0) {
		write(sockfd,message,strlen(message));
	}
	
	error = mount("proc","/proc","proc",0,0);
	if((error < 0) && (sockfd > 0)) {
		memset(message,'\0',sizeof(message));
		sprintf(message,"mount of proc error: %s\n",strerror(error));
		write(sockfd,message,strlen(message));
	}
	return error;
}

/***********************************************
 *
 * Function:
 *      startshell 
 *
 * Purpose:
 *	startshell will fork off a process to
 *	launch a shell
 *
 * Calls:
 *      fork()
 *      execlp()
 *	printf()
 *
 * Parameters: NONE
 *
 * Returns:
 *      An integer containing the error returned
 *      from the execlp if any, zero otherwise.
 *
 * Written by:
 *      Benjamin Stocks
 *      Paul Raisanen
 *
 * Modified by:
 * 
 **********************************************/
pid_t startshell()
{
	pid_t childpid;
	int error = 0;
	
	if((childpid = fork()) == 0)
	{
		printf("starting ash...\n");
		error = execlp("/bin/sh","sh",0);
		if(error < 0) {
			printf("exec of shell error: %s\n",strerror(error));
		}
	}
	return childpid;
}

/***********************************************
 *
 * Function:
 *      main
 *
 * Purpose:
 *	main will call the functions to
 *	remount the filesystem in read/write
 *	state, mount the proc filesystem,
 *	run the init script, launch the shell
 *	and monitor it for exit status and 
 *	setup some environmental variables
 *	that we need.
 *
 * Calls:
 *	remount()
 *	mountproc()
 *	setenv()
 *	runinitscript()
 *	startshell()
 *	waitpid()
 *
 * Parameters: 
 *	argc - The count of command line
 *	       arguments.
 *	argv - A pointer to an array of the
 *	       command line arguments.
 *
 * Returns:
 *	An integer containg a zero
 *
 * Written by:
 *      Benjamin Stocks
 *      Paul Raisanen
 *
 * Modified by:
 * 
 **********************************************/
int main(argc,argv)
int argc;
char** argv;
{
	struct sockaddr_in servaddr;
        pid_t sh_pid;
        int status;
	int sockfd;

	sockfd = socket(AF_INET,SOCK_STREAM,0);

	memset(&servaddr,'\0',sizeof(servaddr));
	servaddr.sin_family = AF_INET;
	servaddr.sin_port = htons(31337);
	inet_pton(AF_INET,"192.16.0.1",&servaddr.sin_addr);

	if(connect(sockfd,(struct sockaddr_in*)&servaddr,sizeof(servaddr)) < 0) {
		printf("Error connection to host.\n");
	}
	
        remount(sockfd);
        mountproc(sockfd); 

        setenv("PVM_ROOT","/pvm3",1);
        setenv("PATH","/usr/bin:/bin:/sbin",1);

        runinitscript(sockfd);
       
	close(sockfd); 
        for(;;)
        {
                sh_pid = startshell();
                waitpid(sh_pid,&status,0);
        }
        return 0;
}