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

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

/* 
 * $Log: misc.c,v $
 * Revision 2.3  1993/09/02  23:13:18  surendar
 * Latest Version
 *
 * Revision 2.2  1993/01/29  18:06:10  surendar
 * Disable the old StartTimer so that you can have a fresh start
 *
 * Revision 2.1  1993/01/29  08:09:07  surendar
 * Assignment II release
 *
 * Revision 1.4  1993/01/29  08:06:15  surendar
 * Prepare for assignment II
 *
 * Revision 1.3  1993/01/20  06:18:07  surendar
 * Very wierd bug, if the delay is small enough for Starttimer, the
 * timer goes off before the parent can get control and cause havoc!!
 *
 * 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/misc.c,v 2.3 1993/09/02 23:13:18 surendar Exp $";
#endif /* lint */

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

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

#include	"defs.h"
#include	"extern.h"

static caddr_t layers[] = {	/* Static string for printfs */
    "Hybrid  ",
    "Datalink",
    "Physical",
    NULL,
};

static long lineno = 1;

static caddr_t n_errmsg[] = {	/* Static error message string */
    "Success",
    "Failure",
    "Inappropriate arguments",
    "No data available in channel",
};

/*
 * ====================================================================
 *
 * Function: _fatal
 *
 * Arguments: mesg - Fatal message to be printed before aborting
 * 	   filename - Name of the source file 
 * 	   number - Source line number where this occured
 *
 * Return: void
 *
 * Description: This is the lower level function that prints a appropriate
 * error message which prints the current layer, source line number, source
 * file name etc to the log file before exitting.
 *
 * ===================================================================
 */
void
    _fatal(caddr_t mesg, caddr_t filename, long linenum)
{
    char buf[1024];

    sprintf(buf, "[%c:%-4d]FATAL: %s [%s:%-d]\n",
	    *layers[cur_layer], lineno++, 
	    mesg, filename, linenum); 
    write(logfile, buf, strlen(buf) );
    exit(2);
}

/*
 * ====================================================================
 *
 * Function: _nerror
 *
 * Arguments: mesg - Message to be added to the printout
 * 	   filename - Name of the source file where error occured
 * 	   linenum - Source line number
 *
 * Return: void
 *
 * Description: This is the wrapper around the function which prints a network
 * error message similar to perror() along with the message provided.
 *
 * ===================================================================
 */
void
    _nerror(caddr_t mesg, caddr_t filename, long linenum)
{
    char buf[1024];
    
    sprintf(buf, "[%c:%-4d]ERROR: %s: %s [%s:%-d]\n",
	    *layers[cur_layer], lineno++, mesg, n_errmsg[nerrno],
	    filename, linenum); 
    write(logfile, buf, strlen(buf) );
    exit(2);

}

/*
 * ====================================================================
 *
 * Function: _log
 *
 * Arguments: level - The log output level. This function will print messages
 * 	   over Debug level which could be modified through command line
 * 	   switches. Default is 0 for Debug_level.
 * 	   mesg - The message to be logged
 * 	   filename - Source filename
 * 	   linenum - Source line number
 *
 * Return: void
 *
 * Description: This function adds this message to the log file.
 *
 * ===================================================================
 */
void
    _log(int level, caddr_t mesg, caddr_t filename, long linenum)
{
    char buf[1024];
    
    if (Debug_level >= level)
	{
	    sprintf(buf, "[%c:%-4d]  LOG: %s [%s:%-d]\n",
		    *layers[cur_layer], lineno++,
		    mesg, filename, linenum);
	    write(logfile, buf, strlen(buf) );
	}

}


/*
 * ====================================================================
 *
 * Function: \section{_dump}
 *
 * Arguments: mesg - Message added for debugging. Restrict this to less then 5
 * 	   characters wide.
 * 	   buf  - Actual buffer to be dumped
 * 	   len  - Length of the buffer
 * 	   filename - Source file name
 * 	   linenum - Source file line number
 *
 * Return: void
 *
 * Description: This function dumps the buf in logfile 20 charactes to  a
 * 	     line. It takes care to dump non-printable ASCII characters
 * 	     properly.
 *
 * ===================================================================
 */
void
    _dump(caddr_t mesg, void *databuf, size_t len, caddr_t filename, long linenum)
{
    static int count = 0;	/* Dump number */
    int    width = 0;		/* 20 characters to a line */
    unsigned char ch;		/* Character to print */
	caddr_t buf = databuf;
    static char *list[] = {	/* ASCII name for CTRL characters */
	"\\0 ", "soh", "stx", "etx", "eot", "enq", "ack", "bel",
	"\\b ", "ht ",  "\\n ",  "\\v ", "\\f ",  "\\r ",  "so ",  "si ",
	"dle", "dcl", "dc2", "dc3", "dc4", "nak", "syn", "etb",
	"can",  "em ", "sub", "esc",  "fs ",  "gs ",  "rs ",  "us ",
    };
    int char_count = 0;
    char print_buf[1024], tmp_buf[132];
    
    if (Dump == 0)
	return;
    
    sprintf(print_buf,
	    "[%c:%-4d] DUMP[#%d]: %s [%s:%-d][%d bytes]",
	    *layers[cur_layer], lineno++,
	    count++,
	    mesg,
	    filename,
	    linenum,
	    len);
    
    while (len--)
	{
	    if ((width++ % 15) == 0) /* Only 15 characters to a line */
		{
		    strcat(print_buf,"\n");
		    write(logfile, print_buf, strlen(print_buf));
		    memset(print_buf, '\0', sizeof(print_buf));
		    sprintf(tmp_buf,"[%c:%-4d]%06X:", *layers[cur_layer],
			    lineno++, char_count);
		    strcat(print_buf, tmp_buf);
		}
	    char_count++;

	    ch = *buf++;
	    if (ch == 0x7f)	/* DEL */
		strcat(print_buf, " del");
	    else
		if (ch == ' ')	/* Space character */
		    strcat(print_buf, "  sp");
		else
		    {
			if (iscntrl(ch))	/* Cntrl character? */
			    sprintf(tmp_buf, " %s", list[ch]);
			else
			    if (isprint(ch))
				sprintf(tmp_buf, "   %c", ch);
			    else
				sprintf(tmp_buf, " %03o", (int)ch);
			strcat(print_buf, tmp_buf);
		    }
	    
	}
    strcat(print_buf, "\n");
    write(logfile, print_buf, strlen(print_buf));
}


/*
 * ====================================================================
 *
 * Function: \section{print_usage_stat}
 *
 * Arguments: None
 *
 * Return: None
 *
 * Description: Prints the usage statistics for the current process using
 * log() command.
 *
 * ===================================================================
 */
void
    print_usage_stat(void)
{
#ifndef __hpux
    struct rusage rusage;
    char   buf[132];
    
    if (getrusage( RUSAGE_SELF, &rusage) == -1)
	{
	    log(0, "getrusage() failed");
	    return;
	}

    sprintf(buf, "User time     : %d.%06d secs",
	    rusage.ru_utime.tv_sec, rusage.ru_utime.tv_usec);
    log(0, buf);

    sprintf(buf, "System time   : %d.%06d secs",
	    rusage.ru_stime.tv_sec, rusage.ru_stime.tv_usec);
    log(0, buf);

    sprintf(buf, "Messages sent : %d", rusage.ru_msgsnd);
    log(0, buf);

    sprintf(buf, "Messages recv : %d", rusage.ru_msgrcv);
    log(0, buf);

    sprintf(buf, "Signals recv. : %d", rusage.ru_nsignals);
    log(0, buf);
#endif /* __hpux */
}


/*
 * ====================================================================
 *
 * Function: delay
 *
 * Arguments: time - In secs as a float
 *
 * Return: void
 *
 * Description: Delays execution of the program for the 'time' specified. It
 * takes care to delay for the fractional part too.
 *
 * ===================================================================
 */
void
    delay(float interval)
{
    struct timeval timeout;
    extern char *sys_errlist[];
    char buf[132];

    if (interval > 0)
	{
	    sprintf(buf, "Delaying for %f secs", interval);
	    log(20, buf);
	    
	    timeout.tv_sec = (long) interval;
	    timeout.tv_usec = ((interval - (float) timeout.tv_sec) * 1000000);
	    
	    /* Using select() as a timer, SLEAZY!! */
	    if (select (0, NULL, NULL, NULL, &timeout) == -1)
		fatal(sys_errlist[errno]);
	}
}

/*
 * ====================================================================
 * Function: enqueue
 *
 * Arguments: cur_queue - Queue to enqueue to.
 * 	   data	     - Datum to be added to the queue.
 * 	   len	     - Length of data.
 *
 * Return: error status
 *
 * Description: Add data to the queue cur_queue.
 *
 * ===================================================================
 */
err_t
    enqueue(queue_t **cur_queue, void *data, size_t len)
{
    queue_t *new_elem;

    if ((new_elem = (queue_t *) malloc (sizeof (queue_t))) == NULL)
	fatal("Dynamic memory exhausted!!");
    
    /* No need to lock as there is only one thread of execution */
    
    /* Put the data in a queue structure */
    new_elem->elem = (void *) malloc (len);
    memcpy(new_elem->elem, data, len);
    new_elem->len = len;

    if (*cur_queue == NULL)	/* Queue empty */
	{
	    *cur_queue = new_elem;
	    new_elem->prevptr = new_elem->nextptr = new_elem;
	}
    else
	{
	    new_elem->nextptr = *cur_queue;
	    new_elem->prevptr = (*cur_queue)->prevptr;
	    (*cur_queue)->prevptr = new_elem;
	    new_elem->prevptr->nextptr = new_elem;
	    *cur_queue = new_elem;
	}
    return(Success);
}


/*
 * ====================================================================
 *
 * Function: dequeue
 *
 * Arguments: cur_queue - Queue to operate from
 * 	   data - Datum is returned here if found
 * 	   len - Length of the datum
 * 	   error_rate - [ 0 .. 100 ] depending on the error rate packets
 * 	   could be retrieved out of order.
 *
 * Return: error status
 *
 * Description: Remove items from queue cur_queue. 
 *
 * ===================================================================
 */
err_t
    dequeue(queue_t **cur_queue, void **data, size_t *len, long error_rate)
{
    queue_t *elem;
    queue_t *head;
    
    if (*cur_queue == NULL)	/* Nothing in queue */
	{
	    *data = NULL;
	    *len = 0;
	    return(Failure);
	}

    if ((*cur_queue)->nextptr == *cur_queue) /* ie, only one elem */
	{
	    elem = *cur_queue;
	    *cur_queue = NULL;
	}
    else
	{
	    if (error_rate || ((random() % 100) < error_rate)) /* Error creeps in */
		head = elem = (*cur_queue)->prevptr->prevptr;
	    else
		head = elem = (*cur_queue)->prevptr;
	    head->prevptr->nextptr = head->nextptr;
	    head->nextptr->prevptr = head->prevptr;
	}

    *data = elem->elem;
    *len = elem->len;
    free(elem);

    return(Success);
}

/*
 * ====================================================================
 *
 * Function: \section{connect_port}
 *
 * Arguments: name - Name of the UNIX socket to connect to
 *
 * Return: fd
 *
 * Description: Connects to this UNIX socket name
 *
 * ===================================================================
 */
long
    connect_port(caddr_t name)
{
    long fd;
    struct sockaddr_un conn; /* Unix socket structure */

    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) /* Allocate socket */
	fatal("Cannot allocate socket");
    
    conn.sun_family = AF_UNIX;
    strcpy(conn.sun_path, name);

    while (connect(fd,
		   (struct sockaddr *)&conn,
		   sizeof(struct sockaddr_un)) < 0)
	;		/* The other end may not be allocated yet!! */
    
    unlink(name);	/* Can unlink now as the communication */
    			/* channel has already been established */

    return(fd);
}

/*
 * ====================================================================
 *
 * Function: \section{bind_port}
 *
 * Arguments: name - Name of UNIX socket to bind to
 *
 * Return: the fd connected to.
 *
 * Description: Bind to the socket at this name.
 *
 * ===================================================================
 */
long
    bind_port(caddr_t name)
{
    long fd;
    struct sockaddr_un conn; /* Unix socket structure */
    
    if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) /* Allocate socket */
	fatal("Cannot allocate socket");
	    
    conn.sun_family = AF_UNIX;
    strcpy(conn.sun_path, name);
    
    if (bind(fd,
	     (struct sockaddr *)&conn,
	     sizeof(struct sockaddr_un)) < 0)
	fatal("Cannot bind to UNIX file");

    return(fd);
}

/*
 * ====================================================================
 *
 * Function: \section{listen_port}
 *
 * Arguments: sock_fd - Fd to listen on
 *
 * Return: the listened (sp?) socket
 *
 * Description: Listen on this socket.
 *
 * ===================================================================
 */
long
    listen_port(long sock_fd)
{
    long fd;
    
    listen(sock_fd, 1);		/* One queue is fine because only one other proces connects to this!! */   
    
    if ((fd = accept(sock_fd, (struct sockaddr *) 0, (int *)
		     0)) == -1)
	fatal("Cannot accept connections");

    return(fd);
}


long timer_reset = 0;
static long timer_child = -1;

/*
 * ====================================================================
 *
 * Function: \section{timer_sighandler}
 *
 * Arguments: signal number
 *
 * Return: void
 *
 * Description: Signal handler for the timer
 *
 * ===================================================================
 */
static void
    timer_sighandler(int signum)
{
    long dead_child;
    
    timer_reset = 1;		/* Actual thing that is being processed */

}

/*
 * ====================================================================
 *
 * Function: \section{StopTimer}
 *
 * Arguments: timer to stop
 *
 * Return: err_t
 *
 * Description: Tries to Stop the timer, returns an error(E_Notimer) if there
 * is no timer already running.
 *
 * ===================================================================
 */
err_t
    StopTimer(timer_t timer)
{

}

/*
 * ====================================================================
 *
 * Function: \section{StartTimer}
 *
 * Arguments: timer to start, interval in microseconds
 *
 * Return: error status
 *
 * Description: Starts a interval timer with signal handler as
 * timer_sighandler().
 *
 * ===================================================================
 */
err_t
    StartTimer(timer_t timer, long interval)
{

}


/*
 * ====================================================================
 *
 * Function: \section{EnableHybridLayer}
 *
 * Arguments: void
 *
 * Return: void
 *
 * Description: Enables the Hybrid layer for more activity
 *
 * ===================================================================
 */
void
    EnableHybridLayer(void)
{
				/* Make no sense in our program as we just */
				/* let the OS  buffer stuff for us. */

}


/*
 * ====================================================================
 *
 * Function: \section{DisableHybridLayer}
 *
 * Arguments: void
 *
 * Return: void
 *
 * Description: Disables the hybrid layer
 *
 * ===================================================================
 */
void
    DisableHybridLayer(void)
{
    				/* Makes no sense as we let the OS buffer */
				/* stuff for us */
}


/*
 * ====================================================================
 * establish_link - Try to establish a contact with the hostname specified.
 * ====================================================================
 */
int
    establish_link(char *hostname,long port)
{
    int soc;
    struct hostent *host;
    struct sockaddr_in name;
    struct servent *server;
	char buf[132];

	sprintf(buf, "Connect to Hostname: %s:%d", hostname, port);
	log(10, buf);

    bzero( &name,sizeof(name));

    name.sin_family = AF_INET;
    if ((host = gethostbyname (hostname)) == NULL)
	{
	    log(0, "Cannot connect to log host");
	    return(-1);
	}
    
    bcopy( host->h_addr, &name.sin_addr, 4);
    name.sin_port = htons(port);

    if ((soc = socket( AF_INET, SOCK_STREAM, IP)) < 0)
	{
	    log(0, "socket open for logger failed");
	    return(-1);
	}

    if ((connect(soc, &name, sizeof(name))) < 0)
	{
	    log(0, "Socket connect failed");
	    return(-1);
	}

    return(soc);
	
}


/*
 * ====================================================================
 * hostname_str - Returns the hostname as a string for the address
 * ====================================================================
 */
char *
    hostname_str(struct in_addr addr)
{
    struct hostent *host = gethostbyaddr(&addr, sizeof(addr), AF_INET);
    long   addr_buf;
    
    if (host)
	return(host->h_name);

    memcpy(&addr_buf, &addr, sizeof (addr_buf));
    addr_buf = ntohl(addr_buf);
    memcpy(&addr, &addr_buf, sizeof(struct in_addr));

    host = gethostbyaddr(&addr, sizeof(addr), AF_INET);
    if (host)
	return(host->h_name);
    else
	return((char *)inet_ntoa(addr));
}
