/*
 * Copyright (c) 2000 Columbia University
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that: (1) source code distributions
 * retain the above copyright notice and this paragraph in its entirety, (2)
 * distributions including binary code include the above copyright notice and
 * this paragraph in its entirety in the documentation or other materials
 * provided with the distribution, and (3) all advertising materials mentioning
 * features or use of this software display the following acknowledgement:
 * ``This product includes software developed by Columbia University
 * and its contributors.'' Neither the name of the University nor the names 
 * of its contributors may be used to endorse or promote products derived 
 * from this software without specific prior written permission.
 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
 *
 * Written by Ping Pan, Columbia University, 2000
 *
 * The author would like to thank Bell Labs for providing time and freedom
 * to complete this work. Please forward bug fixes, enhancements and questions
 * to pingpan@cs.columbia.edu.
 */

/* Actually, all routines here are shamelessly copied from other sources.
 */

#include <stdio.h>
#include <errno.h>
#include <unistd.h>
#include <string.h>
#include <stdarg.h>
#include <syslog.h>
#include <paths.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <net/route.h>
#include <net/if.h>
#define KERNEL
#include <netinet/in.h>

#include "queue.h"
#include "table.h"
#include "yes_var.h"


FILE	*ftrace = stdout;		/* output trace file */
char	inittracename[MAXPATHLEN+1];
int	file_trace;			/* 1=tracing to file, not stdout */


void
set_tracefile(char *filename,
	      char *pat,
	      int dump)			/* -1=no dump, 0=default, 1=force */
{
	struct stat stbuf;
	FILE *n_ftrace;
	char *fn;

	/* Allow a null filename to increase the level if the trace file
	 * is already open or if coming from a trusted source, such as
	 * a signal or the command line.
	 */
	if (filename == 0 || filename[0] == '\0') {
		filename = 0;
		if (ftrace == 0) {
			if (inittracename[0] == '\0') {
				msglog("missing trace file name");
				return;
			}
			fn = inittracename;
		} else {
			fn = 0;
		}
	} else {
		/* Allow the file specified with "-T file" to be reopened,
		 * but require all other names specified over the net to
		 * match the official path.  The path can specify a directory
		 * in which the file is to be created.
		 */
		if (strcmp(filename, inittracename)
		    && (strncmp(filename, YES_LOG, sizeof(YES_LOG)-1)
			|| strstr(filename,"../")
			|| 0 > stat(YES_LOG, &stbuf)) ) {

			msglog("wrong trace file \"%s\"", filename);
			return;
		}

		/* If the new tracefile exists, it must be a regular file.
		 */
		if (stat(filename, &stbuf) >= 0
		    && (stbuf.st_mode & S_IFMT) != S_IFREG) {
			msglog("wrong type (%#x) of trace file \"%s\"",
			       stbuf.st_mode, filename);
			return;
		}
		fn = filename;
	}

	if (fn != 0) {
		n_ftrace = fopen(fn, "a");
		if (n_ftrace == 0) {
			msglog("failed to open trace file \"%s\" %s",
			       fn, strerror(errno));
			if (fn == inittracename)
				inittracename[0] = '\0';
			return;
		}

		tmsg("switch to trace file %s\n", fn);

		file_trace = 1;
		trace_close();
		ftrace = n_ftrace;

		fflush(stdout);
		fflush(stderr);
		dup2(fileno(ftrace), STDOUT_FILENO);
		dup2(fileno(ftrace), STDERR_FILENO);
	}
}


char *
ts(time_t secs) {
	static char s[20];

	secs += yestime->now.tv_sec;
	bcopy(ctime(&secs)+11, s, 8);
	s[8] = '\0';
	return s;
}


void
msglog(char *p, ...)
{
	va_list args;

	trace_flush();

	va_start(args, p);
	vsyslog(LOG_ERR, p, args);

	if (ftrace != 0) {
		if (ftrace == stdout)
			fputs("yessir > ", ftrace);
		vfprintf(ftrace, p, args);
		fputc('\n', ftrace);
	}
}


void
logbad(int dump, char *p, ...)
{
	va_list args;

	trace_flush();

	va_start(args, p);
	vsyslog(LOG_ERR, p, args);

	fputs("error:", stderr);
	vfprintf(stderr, p, args);
	fputs("; giving up\n",stderr);
	fflush(stderr);

	if (dump)
		abort();
	exit(1);
}

 
/* On each event, display a time stamp.
 * This assumes that 'now' is update once for each event, and
 * that at least now.tv_usec changes.
 */
static struct timeval lastlog_time;

void
lastlog()
{
	if (lastlog_time.tv_sec != yestime->now.tv_sec
	    || lastlog_time.tv_usec != yestime->now.tv_usec) {
		fprintf(ftrace, "-- %s --\n", ts(yestime->now.tv_sec));
		lastlog_time = yestime->now;
	}
}


void
tmsg(char *p, ...)
{
	va_list args;

	if (ftrace != 0) {
		lastlog();
		va_start(args, p);
		vfprintf(ftrace, p, args);
		fflush(ftrace);
	}
}


void
trace_close(void)
{
	int fd;

	fflush(stdout);
	fflush(stderr);

	if (ftrace != 0 && file_trace) {
		if (ftrace != stdout)
			fclose(ftrace);
		ftrace = 0;
		fd = open(_PATH_DEVNULL, O_RDWR);
		(void)dup2(fd, STDIN_FILENO);
		(void)dup2(fd, STDOUT_FILENO);
		(void)dup2(fd, STDERR_FILENO);
		(void)close(fd);
	}
	lastlog_time.tv_sec = 0;
}


void
trace_flush(void)
{
	if (ftrace != 0) {
		fflush(ftrace);
		if (ferror(ftrace))
			trace_off("tracing off: ", strerror(ferror(ftrace)));
	}
}


void
trace_off(char *p, ...)
{
	va_list args;

	if (ftrace != 0) {
		lastlog();
		va_start(args, p);
		vfprintf(ftrace, p, args);
	}
	trace_close();
}



/* t1 = t2 - t3
 */
void
timevalsub(struct timeval *t1,
	   struct timeval *t2,
	   struct timeval *t3)
{
	t1->tv_sec = t2->tv_sec - t3->tv_sec;
	if ((t1->tv_usec = t2->tv_usec - t3->tv_usec) < 0) {
		t1->tv_sec--;
		t1->tv_usec += 1000000;
	}
}

