/* $Header: /home/cobweb3/surendar/networks/RCS/netcp.c,v 2.2 1993/09/02 23:13:18 surendar Exp $ */

/*
 * Module:	This module supports the hybrid layer functions.
 * Author:	$Author: surendar $ 
 * 		Dept. of Computer Sciences 
 * 		Worcester Polytechnic Institute. 
 * Date:	$Date: 1993/09/02 23:13:18 $ 
 */

/* 
 * $Log: netcp.c,v $
 * Revision 2.2  1993/09/02  23:13:18  surendar
 * Latest Version
 *
 * Revision 2.1  1993/01/29  08:09:07  surendar
 * Assignment II release
 *
 * Revision 1.3  1993/01/29  08:06:15  surendar
 * Prepare for assignment II
 *
 * Revision 1.2  1993/01/14  19:07:36  surendar
 * Checked in for Assignment I
 * 
 */

#ifndef lint
static char rcs_ident[] = "$Header: /home/cobweb3/surendar/networks/RCS/netcp.c,v 2.2 1993/09/02 23:13:18 surendar Exp $";
#endif /* lint */

#include	<stdio.h>
#include	<sys/types.h>
#include	<sys/time.h>
#include	<sys/param.h>
#include	<sys/socket.h>
#include	<netdb.h>
#include	<netinet/in.h>
#include	<sys/un.h>
#include	<sys/stat.h>
#include	<fcntl.h>
#include	<signal.h>

#if HAVE_STRING_H
#include	<string.h>
#endif /* HAVE_STRING_H */

#if HAVE_STDARG_H
#include	<stdarg.h>
#endif /* HAVE_STDARG_H */

#include	"defs.h"
#include	"global.h"

extern int optind;
extern char *optarg;

/*
 * ====================================================================
 *
 * Function: \section{usage}
 *
 * Arguments: Print a error message before printing usage informaion
 *
 * Return: None
 *
 * Description: Prit the usage information for netcp
 *
 * ===================================================================
 */
static void
    usage(caddr_t mesg)
{
    if (mesg)
	fprintf(stderr, "%s:%s\n", progname, mesg);

    fprintf(stderr,
	    "Usage: %s [<options>] <source file> <dest. hostname>:<dest file>",
	    progname);
    fprintf(stderr, "\nOptions:\n\t");
    fprintf(stderr, "-v {Prints the version string}\n\t");
    fprintf(stderr, "-d <debug level, default is 0>\n\t");
    fprintf(stderr, "-l <log file, default stderr>\n\t");
    fprintf(stderr, "-s <speed, default 9600 bytes/sec>\n\t");
    fprintf(stderr, "-e <error rate, default 10 [0..10000]>\n\t");
    fprintf(stderr, "-r <specify a seed for random noise generator>\n\t");
    fprintf(stderr, "-D {Prints dump messages}\n\t");
    fprintf(stderr, "-S {Runs as the server. To be run in the dest. machine.\n");
    fflush(stderr);

    exit(0);

}

/*
 * ====================================================================
 *
 * Function: \section{terminate}
 *
 * Arguments: signal that caused this to be called.
 *
 * Return: nothing.
 *
 * Description: Signal handling routine for user defined kill which would be
 * 	     send by the applicationg layer when everything is done!! 
 *
 * ===================================================================
 */
static void
    terminate(int signum)
{
    log(20, "Terminating with User defined symbol");
    print_usage_stat();

    close(logfile);
    exit(0);			/* Close all descriptors for us */
}

/* 
 * ==================================================================== 
 *  The main function (really!!)
 * ==================================================================== 
 */
void
    main (int argc, char **argv, char **envp)
{
    caddr_t  n_aldl;	/* Temporary UNIX socket for HL and DL */
    caddr_t  n_dlal;	/* Temporary UNIX socket for HL and DL */
    caddr_t  n_dlpl;	/* Temporary UNIX socket for DL and PL */
    caddr_t  n_pldl;	/* Temporary UNIX socket for DL and PL */
    int dtablesize = GETDTABLESIZE;
    char ch;
    pid_t dl_pid, pl_pid, logger_pid, wait_pid;
    struct timeval start, end;
    int sockfd, readfd, len;
    struct sockaddr_in serv_addr, cli_addr;
    fd_set clients, temp;
    char buf[1024];
    int read_fd = -1, sel_count, newsockfd = -1, clilen;
    int ret_value;
    int buf_len;
    int ever_here = 0;
    
    progname = *argv;		/* Global program name used by report */
				/* functions */
    if ((progname = (caddr_t) strrchr(*argv, (int) '/')) == NULL) 
	progname = *argv;
    else
	progname++;

    Verbose = 0;
    desthost[0] = '\0';

    while ((ch = getopt(argc, argv, "Svcd:Dh:r:s:e:l:")) != EOF)
	{
	    switch (ch)
		{
		  case 'h':	/* Host to contact, default is local host */
		    strncpy(desthost, optarg, MAXPATHLEN);
		    desthost[MAXPATHLEN - 1] = '\0';
		    break;

		  case 'D':
		    Dump = 1;	/* Do we need to honor dump()? */
		    break;

		  case 'S':
		    Server = 1;	/* Are we the server? */
		    break;
		    
		  case 'c':
		    client = True;
		    break;
		    
		  case 's':	/* Speed */
		    if ((phys_control.seed = atoi(optarg)) <= 0)
			usage("Positive transfer rate required");
		    break;

		  case 'd':
		    if ((Debug_level = atoi(optarg)) < 0)
			usage("Negative debug level unexpected.");
		    break;
		    
		  case 'r':
		    phys_control.seed = atoi(optarg);/* Change the random */
						     /* seed used by the PL */
		    break;

		  case 'e':	/* Set a error rate */
		    phys_control.error_rate = atoi(optarg);
		    if ((phys_control.error_rate > 10000) ||
			(phys_control.error_rate < 0)) 
			usage("Error rate must be 0 <= e <= 10000");
		    break;

		  case 'l':
		    if ((logfile = open(optarg,
					O_WRONLY | O_CREAT | O_APPEND,
					0622)) == NULL) 
			{
			    logfile = 2;
			    log(0, "Open of logfile failed");
			}
		    break;
	
		  case 'v':	/* Verbose mode */
		    fprintf(stderr, "%s: $Version: $\n", progname);
		    Verbose = 1;
		  case '?':
		  default:
		    usage(NULL);
		    break;
		}
	}
    
    if (! Server)
	if ((argc - optind) == 2)
	    {
		caddr_t ptr;
		
		strcpy(source, *(argv + optind));
		strcpy(desthost, *(argv + optind + 1));
		if ((ptr = (caddr_t) strrchr(desthost, (int) ':')) == NULL)
		    usage("Destination host not specified");
		else
		    {
			strcpy(phys_control.dest, ptr + 1);
			*ptr = '\0';
		    }
	    }
	else
	    usage("Source and Destination files required for copy operation.");
    
    if ((sockfd = socket(AF_INET, SOCK_STREAM, IP)) < 0)
	fatal("Socket open failed");

    bzero((char *) &serv_addr, sizeof(serv_addr));
    serv_addr.sin_family = AF_INET;
    serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
    serv_addr.sin_port = 0;	/* Some port */
    
    if (bind(sockfd,(struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
	fatal("Bind failed");
    
    /* Find the assigned port and print it out */
    len = sizeof(serv_addr);
    if (getsockname(sockfd, (struct sockaddr *)&serv_addr, &len) < 0)
	fatal("Getsockname failed");

    phys_ptr = (phys_ctrl_t *) shmcreate(sizeof(phys_control));
    phys_ptr->valid = 0x00000000;

    logger_port = ntohs(serv_addr.sin_port);

    if ((logger_pid = fork()) == 0) /* Child?? */
	{
	    int max_count = 0;
	    
	    listen(sockfd,5);

	    FD_ZERO(&clients);
	    FD_SET(sockfd, &clients);
	    memcpy(&temp, &clients, sizeof(fd_set));
	    write(logfile, "========================================\n", 41);
	    
	    while (1)
		{
		    if ((sel_count = select(dtablesize, &temp,
					    (fd_set *)0, (fd_set *)0,
					    (struct timeval *)0)) > 0)
			{
			    while(sel_count--)
				{
				    
				    for(read_fd = 0;
					read_fd < dtablesize;
					read_fd++)
					if (FD_ISSET(read_fd, &temp))
					    {
						if (read_fd == sockfd) /* Another connection */
						    			/* coming in */
						    {
							clilen = sizeof(cli_addr);
							
							if ((newsockfd =
							     accept(sockfd,
								    (struct sockaddr *)&cli_addr,       
								    &clilen)) < 0)
							    perror("accept");
							max_count++;
							FD_SET(newsockfd, &clients);
						    }
						else
						    {
							ever_here = 1;

							do
							    {
								if
								    ((buf_len = read(read_fd, buf, 1024)) <= 0)                                    
								    {
									max_count--;
									FD_CLR(read_fd, &clients);
								    }
								else
								    write(logfile,
									  buf,
									  buf_len);
							    } while
								(buf_len == 1024);
						    }
					    }
				}
			    if (ever_here)
				{
				    
				    for(read_fd = 0; read_fd < dtablesize; read_fd++)
					if ((read_fd != sockfd) &&
					    (FD_ISSET(read_fd, &clients)))
					    break;

#ifdef DEBUG
				    printf("max_count: %d read_fd: %d dtablesize: %d\n", max_count, read_fd, dtablesize);
#endif /* DEBUG */
				    if (max_count == 0)
					_exit(0);
				    
				    if (read_fd == dtablesize)
					_exit(0);
				}
			    
			    memcpy((void *)&temp, (void *)& clients, sizeof(fd_set));
			}
		    else
			{
			    perror("select");
			    abort(2);
			}
		}
	}
    else
	{
	    close(sockfd);
	}
    

    /* Generate temporary file names */
#if HAVE_TEMPNAM
    if (((n_aldl = tempnam("/usr/tmp", "aldl")) == NULL) || 
	((n_dlal = tempnam("/usr/tmp", "dlal")) == NULL) || 
	((n_dlpl = tempnam("/usr/tmp", "dlpl")) == NULL) || 
	((n_pldl = tempnam("/usr/tmp", "pldl")) == NULL))
	fatal("Cannot allocate temporary name");
#else /* HAVE_TEMPNAM */
#if HAVE_TMPNAM
	n_aldl = (caddr_t ) malloc (MAXPATHLEN);
	n_dlal = (caddr_t ) malloc (MAXPATHLEN);
	n_dlpl = (caddr_t ) malloc (MAXPATHLEN);
	n_pldl = (caddr_t ) malloc (MAXPATHLEN);
	if ( ! (n_aldl && n_dlal && n_dlpl && n_pldl ))
	fatal("Cannot allocate temporary name");

    if (((n_aldl = tmpnam(n_aldl)) == NULL) || 
	((n_dlal = tmpnam(n_dlal)) == NULL) || 
	((n_dlpl = tmpnam(n_dlpl)) == NULL) || 
	((n_pldl = tmpnam(n_pldl)) == NULL))
	fatal("Cannot allocate temporary name");
#else
#error No way to generate a temporary name
#endif /* HAVE_TMPNAM */
#endif /* HAVE_TEMPNAM */

    if ((dl_pid = fork()) == 0)		/* Child is the datalink layer */
	{
	    signal(SIGUSR1, terminate);
	    
	    cur_layer = Datalink;
	    /* Communication end point for commn between HL and DL */

	    log(10, "Connecting to aldl");
	    aldl_fd = connect_port(n_aldl);

	    log(10, "Connecting to pldl");
	    pldl_fd = connect_port(n_pldl);

	    log(10,"Binding to dlal");
	    dlal_fd = bind_port(n_dlal);
	    dlal_fd = listen_port(dlal_fd);

	    log(10,"Binding to dlpl");
	    dlpl_fd = bind_port(n_dlpl);
	    dlpl_fd = listen_port(dlpl_fd);

	    gethostname(buf, sizeof(buf));
	    if ((ret_value = establish_link(buf, logger_port)) >= 0)
		{
		    close(logfile);
		    logfile = ret_value;
		}
	    else
		log(0,"Cannot establish connection to logger daemon");
	    
#ifdef DOUBLY_CAREFUL
	    if (Server)
		{
		    while(phys_ptr->valid != 0x1010fefe) /* Rudimentary */
							 /* form of */
							 /* synchronization */
							 /* with the */
							 /* physical layer */ 
			;
		    
		    memcpy(&phys_control, phys_ptr, sizeof(phys_control));
		}
#endif				/* DOUBLY_CAREFUL */
	    
	    dl_process();	/* Any initializations required by the data */
				/* link layer */

	    close(aldl_fd);
	    close(dlpl_fd);
	    print_usage_stat();
	    close(logfile);
	    
	    _exit(0);
	}

    if ((pl_pid = fork()) == 0)		/* Child is the physical layer */
	{
	    signal(SIGUSR1, terminate);
    
	    cur_layer = Physical;
	    log(10, "Binding to pldl");
	    pldl_fd = bind_port(n_pldl);
	    pldl_fd = listen_port(pldl_fd);

	    log(10, "Connecting to dlpl");
	    dlpl_fd = connect_port(n_dlpl);

	    gethostname(buf, sizeof(buf));
	    if ((ret_value = establish_link(buf, logger_port)) >= 0)
		{
		    close(logfile);
		    logfile = ret_value;
		}
	    else
		log(0,"Cannot establish connection to logger daemon");

	    pl_process();	/* Any initializations required by the */
				/* physical layer */
	    
	    close(pldl_fd);
	    close(dlpl_fd);
	    close(logfile);

	    _exit(0);
	}

    signal(SIGUSR1, terminate);
    
    cur_layer = Hybrid;
    /* The hybrid layer */

    log(10, "Binding to aldl");
    aldl_fd = bind_port(n_aldl);
    aldl_fd = listen_port(aldl_fd);

    log(10, "Connecting to dlal");
    dlal_fd = connect_port(n_dlal);
    
    gethostname(buf, sizeof(buf));

    log(20, "Trying to rendezous with the logger");
    
    if ((ret_value = establish_link(buf, logger_port)) >= 0)
	{
	    close(logfile);
	    logfile = ret_value;
	}

    else
	log(10, "Cannot rendezous with the logger");

    log(20, "Rendezous with the logger, successful");

    gettimeofday(&start,NULL);
    hl_process();		/* Any initializations required by HL */
    gettimeofday(&end, NULL);

    if (kill(dl_pid, SIGUSR1) == -1)
	perror("KILL_DL");
    if (kill(pl_pid, SIGUSR1) == -1)
	perror("KILL_PL");

#ifdef DEBUG
    fprintf(stdout, "%s: Elapsed time %f secs\n", progname,
	    (float) ((float)(end.tv_sec * 1000000.0 + (float) end.tv_usec) -
	    (float)(start.tv_sec * 1000000.0 + (float)
		    start.tv_usec))/1000000.0); 
#endif				/* DEBUG */
    print_usage_stat();
    
    close(logfile);
    
    while ((wait_pid = wait(NULL)) != -1)
	{
#ifdef DEBUG
	    if (wait_pid == pl_pid)
		printf("PL died\n");
	    else
		if (wait_pid == dl_pid)
		    printf("DL died\n");
		else
		    if (wait_pid == logger_pid)
			printf("LOG died\n");
		    else
			printf("Orphan %d died\n", wait_pid);
#endif /* DEBUG */
	};

    /*
    if (kill(logger_pid, SIGKILL) == -1)
	perror("KILL_LG");
	*/

    delay(1.0);
    
    close(aldl_fd);
    close(dlal_fd);
    
    exit(0);
}

