/*
 * Copyright (c) 2002-2006 Endace Technology Ltd, Hamilton, New Zealand.
 * All rights reserved.
 *
 * This source code is proprietary to Endace Technology and no part
 * of it may be redistributed, published or disclosed except as outlined in
 * the written contract supplied with this product.
 *
 * $Id$
 */

/* Endace headers. */
#include "dagapi.h"
#include "dagutil.h"
#include "dagclarg.h"

#define LANDER_ADDITION

#ifdef LANDER_ADDITION
#include <unistd.h>
#include <sys/socket.h>
#endif /* LANDER_ADDITION */

/* CVS Header. */
static const char* const kCvsHeader __attribute__ ((unused)) = "$Id$";
static const char* const kRevisionString = "$Revision: 1.45.10.1 $";


#if defined(_WIN32) 
static int outfile;
#elif defined (__FreeBSD__) || defined(__linux__) || defined(__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))
static FILE *outfile;
#endif /* Platform-specific code. */

#ifdef LANDER_ADDITION
/* lander addition: timestamp handling macro: they are in little-endian b.o. */
#if __BYTE_ORDER == __BIG_ENDIAN
#define TS(x)                   __bswap_64(x)
#endif
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define TS(x)                   (x)
#endif
#ifndef TS
#error __BYTE_ORDER is neither __BIG_ENDIAN nor __LITTLE_ENDI
#endif
/* lander addition: sequence number limits */
#define SEQNUM_FMT	"%08lu"
#define SEQNUM_MAX	99999999
#define LANDER_DEFAULT_EXT ".erf"
#endif /* LANDER_ADDITION */

#define OPTBUFSIZE ONE_KIBI
#define BUFSIZE 256
static int mbytes = 0;
static int rtime = 0;
static int waittime = 0;
static char buffer[OPTBUFSIZE];

static char dagname_buf[DAGNAME_BUFSIZE] = "dag0";
static char dagname[DAGNAME_BUFSIZE];
static char fname_buf[BUFSIZE];
static char dlay_buf[BUFSIZE];

static int dagstream;
static int dagfd;
static char* ofname;
static int doreport = 1;
static int maximize = 0;
static uint64_t diff;
static int64_t fileoff = 0;
static time_t seconds = 0;
static time_t waitseconds = 0;
//static int maxwrite = 256 * ONE_MEBI; /* Lander default: 256MiB default */
static int maxwrite = 4 * ONE_MEBI; /* 4MiB default */
static int delay_msecs=0;

static int exit_now = 0;	/* exit loop flag set by signal handler */

#ifdef LANDER_ADDITION
static uint64_t ofilesize = (1<<30);	/* approximate file size */
static int64_t  ofilesize_over = -1;    /* max MB over filesize  */
static char   basename_buf[BUFSIZE]="";
static char   *obasename = basename_buf;/* output file base name */
static FILE   *ofd = NULL;
static int    odaemonize = 0;		/* start in the background */
static int    ouse_utc_time = 0;	/* use UTC time in timestamping files */
static char   *cur_filename = NULL;	/* current output file name */
static char   watchfile_buf[BUFSIZE]="";
static char   *owatchfile = NULL;	/* watch if this file exists, then suspend writing until it's gone */
static char   compressor_cmd[BUFSIZE]="";  /* cmd to pipe output through */
static char   *compressor_arg[BUFSIZE];
static char   file_ext_buf[BUFSIZE] = LANDER_DEFAULT_EXT;
static int    compressor_enabled = 0;
static int    orecsize = 0;		/* fixed record size (to make sure we write on rec boundary */
static char   seqnumfile_buf[BUFSIZE];	/* filename of the last seq number */
static char   *oseqnumfile = NULL;
static uint8_t *bottom = NULL;
static uint8_t *top = NULL;
static ulong   _remainder = 0;
static ulong   _seqnum = 0;
static int mbytes_fsize = 0;
static int mbytes_fsize_over = 0;
#endif /* LANDER_ADDITION */

/* Commandline argument codes. */
enum
{
#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))
	CLA_PERFORMANCE,
#endif
	CLA_DEVICE,
	CLA_HELP,
	CLA_VERBOSE,
	CLA_VERSION,
	CLA_MAXDATA,
	CLA_FNAME,
	CLA_RTIME,
	CLA_DELAY,
	CLA_WAITTIME,
#ifdef LANDER_ADDITION
	CLA_BASENAME,
	CLA_MAXFSZ,
	CLA_MAXFSZ_OVER,
	CLA_DAEMONIZE,
	CLA_USEUTC,
	CLA_WATCHFILE,
	CLA_RECSZ,
	CLA_SEQNUMFILE,
	CLA_COMPRESSOR,
    CLA_FILE_EXT,
#endif  /* LANDER_ADDITION */
};


/* Internal routines. */
static void anysig(int sig);
static void alarmsig(int sig);
static void report(void);
#ifdef LANDER_ADDITION
static void	open_new_file(void);
static size_t   sleep_on_watchfile(const char *watchfile);
static void	init_seqnum(void);
static void	save_seqnum(void);
static void	init_statrec(void);
static void	write_statrec(FILE *);
static void release_child(int sig);
#endif /* LANDER_ADDITION */

#if defined(_WIN32)
static DWORD WINAPI ReportThread(LPVOID lpParameter);
static void SetTimerFn(void);
#endif /* _WIN32 */



#if defined(_WIN32)
/* In Windows we use a separate thread for timeout scheduling. */

static DWORD WINAPI
ReportThread(LPVOID lpParameter)
{
	while(TRUE)
	{
		Sleep(1000);
		doreport++;
	}
	
	return 0;
}

static void
SetTimerFn(void)
{
	if(CreateThread(
		NULL,
		0,
		ReportThread,
		NULL,
		0,
		NULL) == NULL)
		panic("unable to start the report function\n");
}
#endif /* _WIN32 */

static void
print_version(void)
{
	printf("dagsnap (DAG %s) %s\n", kDagReleaseVersion, kRevisionString);
}


static void
print_usage(ClArgPtr clarg)
{
	print_version();
	printf("dagsnap - Endace DAG card capture utility.\n");
	printf("Usage: dagsnap [options]\n");
	dagclarg_display_usage(clarg, stdout);
	printf("With -v three columns are printed per second.\n");
	printf("    1. The cumulative total of data written out.\n");
	printf("    2. The buffer occupancy. Small values indicate no packet loss.\n");
	printf("    3. The rate at which data is currently being written.\n");
}


int
dagsnap_main(int argc, char *argv[])
{
	void* bounce_buffer = NULL;
	FILE* errorfile = NULL;
	uint32_t written;
	struct timeval maxwait;
	struct timeval poll;
	//uint8_t *bottom = NULL; //lander made global
	//uint8_t *top = NULL; //lander made global
	ClArgPtr clarg = NULL;
	int code;
	int result;
	int argindex;
	int index;
	//int fd; //lander removed
	uint32_t written_clip = 0; //lander

#if defined(_WIN32) 

	HANDLE hFile;
	outfile = -1;

#elif defined (__FreeBSD__) || defined(__linux__) || defined(__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))

	outfile = stdout;

#endif /* Platform-specific code. */

	/* lander modification */
	dagutil_set_progname("dagsnap_lander");

	/* Set up default DAG device. */
	if (-1 == dag_parse_name(dagname_buf, dagname, DAGNAME_BUFSIZE, &dagstream))
	{
		dagutil_panic("dag_parse_name(%s): %s\n", dagname_buf, strerror(errno));
	}

	/* Set up the command line options. */
	clarg = dagclarg_init(argc, (const char * const *)argv);

	dagclarg_add(clarg, "this page.", "--help", 'h', CLA_HELP);
	dagclarg_add_long_option(clarg, CLA_HELP, "--usage");
	dagclarg_add_short_option(clarg, CLA_HELP, '?');
	dagclarg_add_string(clarg, "DAG device to use.", "--device", 'd', "device", dagname_buf, DAGNAME_BUFSIZE, CLA_DEVICE);
	dagclarg_add(clarg, "increase verbosity.", "--verbose", 'v', CLA_VERBOSE);
	dagclarg_add(clarg, "display version information.", "--version", 'V', CLA_VERSION);
#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))	
	dagclarg_add(clarg, "maximize disk write performance - will only write in chunks", "--maxwrite", 'j', CLA_PERFORMANCE);
#endif
	dagclarg_add_int(clarg, "maximum amount of data to write per call in MiB (default 4).", "--maxdata", 'm', "mebibytes", &mbytes, CLA_MAXDATA);
	dagclarg_add_string(clarg, "output file name (default is stdout).", "--fname", 'o', "filename", fname_buf, BUFSIZE, CLA_FNAME);
	dagclarg_add_int(clarg, "runtime in seconds.", "--runtime", 's', "seconds", &rtime, CLA_RTIME);
	dagclarg_add_int(clarg, "delay(wait) in seconds before capture and aftre the stream is initialized.", "--wait", 'w', "waitseconds", &waittime, CLA_WAITTIME);
	dagclarg_add_string(clarg, "delay processing by an extra n milliseconds on each burst", "--delay", 'D', "n", dlay_buf, BUFSIZE, CLA_DELAY);

#ifdef LANDER_ADDITION
	dagclarg_add_string(clarg, "output file base name", "--basename", 'B', "basename", 
                        basename_buf, BUFSIZE, CLA_BASENAME);
	dagclarg_add_int(clarg, "maximum (soft) file size in mb.", "--maxfsize", 'S', 
                     "maxfsize", &mbytes_fsize, CLA_MAXFSZ);
	dagclarg_add_int(clarg, "maximum (hard, over soft) file size in mb.", 
                     "--maxfsize-overflow", 0, "maxfsize-overflow", &mbytes_fsize_over, CLA_MAXFSZ_OVER);
	dagclarg_add(clarg, "daemonize", "--daemon", 'F', CLA_DAEMONIZE);
	dagclarg_add(clarg, "use UTC", "--useutc", 'U', CLA_USEUTC);
	dagclarg_add_string(clarg, "watchfile name", "--watchfile", 'W', "watchfile", 
                        watchfile_buf, BUFSIZE, CLA_WATCHFILE);
	dagclarg_add_int(clarg, "fixed record size in bytes", "--recsize", 'f', "recsize", &orecsize, CLA_RECSZ);
	dagclarg_add_string(clarg, "sequence number", "--seqfile", 'N', 
                        "filename", seqnumfile_buf, BUFSIZE, CLA_SEQNUMFILE);
	dagclarg_add_string(clarg, "Command to run to compress the output", "--compressor", 0, 
                        "command", compressor_cmd, sizeof(compressor_cmd), CLA_COMPRESSOR);
	dagclarg_add_string(clarg, 
                        "Give this extension to files (default: "
                        LANDER_DEFAULT_EXT
                        ")", "--filext", 0, "extention", file_ext_buf, 
                        sizeof(file_ext_buf), CLA_FILE_EXT);

#endif /* LANDER_ADDITION */

	/* Parse the command line options. */
	result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	while (1 == result)
	{
		switch (code)
		{
			case CLA_HELP:
				print_usage(clarg);
				return EXIT_SUCCESS;
				break;

			case CLA_DEVICE:
				if (-1 == dag_parse_name(dagname_buf, dagname, DAGNAME_BUFSIZE, &dagstream))
				{
					dagutil_panic("dag_parse_name(%s): %s\n", dagname_buf, strerror(errno));
				}
				break;

			case CLA_VERBOSE:
				dagutil_inc_verbosity();
				break;

			case CLA_VERSION:
				print_version();
				return EXIT_SUCCESS;
				break;

#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))
			case CLA_PERFORMANCE:
				maximize = 1;
				break;
#endif

			case CLA_MAXDATA:
				maxwrite = mbytes * ONE_MEBI;
				break;

			case CLA_FNAME:
				ofname = fname_buf;
				outfile = 0;
				break;

			case CLA_RTIME:
				seconds = rtime;
				break;
			case CLA_DELAY:
				delay_msecs = strtoul(dlay_buf, NULL, 0);
				break;

			case CLA_WAITTIME:
				waitseconds = waittime;
				break;

#ifdef LANDER_ADDITION
			case CLA_MAXFSZ:
				ofilesize = mbytes_fsize * ONE_MEBI;
				break;
            case CLA_MAXFSZ_OVER:
                ofilesize_over = mbytes_fsize_over * ONE_MEBI;
				break;
		        case CLA_BASENAME:
				break;
			case CLA_DAEMONIZE:
				++odaemonize;
				break;
			case CLA_USEUTC:
				++ouse_utc_time;
				break;
			case CLA_WATCHFILE:
				owatchfile = watchfile_buf;
				break;
            case CLA_COMPRESSOR:
                {
                    ++compressor_enabled;
                    /* break into cmd and args */
                    int i=0, argc=0;
                    compressor_arg[argc] = &compressor_cmd[i];
                    for ( ; compressor_cmd[i] != '\0'; ++i) {
                        if (compressor_cmd[i] == ' ') {
                            /* new arg */
                            compressor_cmd[i] = '\0';
                            compressor_arg[++argc] = &compressor_cmd[i+1];
                        }
                    }
                }
				break;
            case CLA_FILE_EXT:
                break;
            case CLA_RECSZ:
				break;
			case CLA_SEQNUMFILE:
				oseqnumfile = seqnumfile_buf;
				break;
#endif /* LANDER_ADDITION */

			default:
				
					/* Unknown option. */
					dagutil_error("unknown option %s\n", argv[argindex]); 
					print_usage(clarg);
					return EXIT_FAILURE;
				
		}
		result = dagclarg_parse(clarg, errorfile, &argindex, &code);
	}

	if (-1 == result)
	{
		if (argindex < argc)
		{
			dagutil_error("while processing option %s\n", argv[argindex]);
		}
		dagclarg_display_usage(clarg, stderr);
		return EXIT_FAILURE;
	}
    
#if defined(_WIN32)
	if(outfile < 0)
		dagutil_panic("Stdout can not be used with Windows. Please specify the filename to Write to\n");
#endif

	 /* Display unprocessed arguments if verbose flag was given. */
	argv = dagclarg_get_unprocessed_args(clarg, &argc);
	if ((NULL != argv) && (0 < argc) && (1 < dagutil_get_verbosity()))
	{
		for (index = 0; index < argc; index++)
		{
			dagutil_verbose_level(2, "unprocessed argument: '%s'\n", argv[index]);
		}
		dagutil_panic("Too many options!\n");
	}

#if defined(_WIN32)
	/* If we don't set stdout to binary mode, redirections will not work correctly */
	_setmode(_fileno (stdout), _O_BINARY);
#endif /* _WIN32 */
	if(ofname)
	{
		int flags = O_RDWR|O_CREAT|O_TRUNC|O_LARGEFILE;

		if (maximize)
		{
			flags |= O_DIRECT;
		}

		(void) close(STDOUT_FILENO);

#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))

		if (open(ofname, flags, 0664) != STDOUT_FILENO) //lander change

#elif defined(_WIN32)

	hFile = CreateFile(ofname,                 /* name of file to create */
				GENERIC_READ | GENERIC_WRITE,  /* permissions of file to create */
				FILE_SHARE_READ,               /* sharing permissions */
				NULL,                          /* security attributes for inheritance */
				CREATE_ALWAYS,                 /* action to take on files that exist */
				FILE_ATTRIBUTE_NORMAL,         /* file flags and attributes */
				NULL);                         /* file template for creation attributes */

	if (INVALID_HANDLE_VALUE == hFile)
	{
		LPTSTR message_buffer;
		FormatMessage(
			FORMAT_MESSAGE_ALLOCATE_BUFFER | 
			FORMAT_MESSAGE_FROM_SYSTEM | 
			FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL,
			GetLastError(),
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), /* default language*/
			&message_buffer,
			0,
			NULL);

		fprintf(stderr, "open(): %s\n", message_buffer);
		return EXIT_FAILURE;
	}

	outfile = _open_osfhandle((intptr_t) hFile, _O_APPEND | _O_CREAT | _O_TRUNC);

	if(outfile == -1)
#endif /* Platform-specific code. */
		{
			dagutil_panic("open %s: %s\n", ofname, strerror(errno));
		}
	}
	else
	{
		ofname = "(stdout)";
	}

	if ((1 == maximize) && (maxwrite < 16 * ONE_MEBI))
	{
		maxwrite = 16 * ONE_MEBI;
	}
#ifdef LANDER_ADDITION
	/* fork seems to confuse dagfd's; fork before dag_open XXX */
	if (odaemonize) {
		pid_t pid = fork();
		switch(pid) {
		case 0: /* child */
			{
				/* redirect input/output to /dev/null */
				int nulld = open("/dev/null", O_RDWR);
				dup2(nulld, STDIN_FILENO);
				dup2(nulld, STDOUT_FILENO);
                                dup2(nulld, STDERR_FILENO);
			}
			break;
		case -1: 
			exit(1);
		default:
			sleep(3); /* give child a chance to start */
			if (kill(pid, 0) == -1 && errno == ESRCH)
				exit(1);
			exit(0);
		}
	}
#endif /* LANDER_ADDITION */

	if((dagfd = dag_open(dagname)) < 0)
		dagutil_panic("dag_open %s: %s\n", dagname, strerror(errno));

	if(dag_configure(dagfd, buffer) < 0)
		dagutil_panic("dag_configure %s: %s\n", dagname, strerror(errno));

	if(dagstream & 1) {
		/* Setting reverse mode for using with soft dag from daemon side */
		if (dag_set_mode(dagfd,dagstream,DAG_REVERSE_MODE)) {
			dagutil_panic("Could not set reverse mode on %s:%d\n", dagname, dagstream);
		}
	} else {
		//default is normal mode 
		dag_set_mode(dagfd,dagstream,DAG_NORMAL_MODE);
	} 

	if(dag_attach_stream(dagfd, dagstream, 0, 0) < 0)
		dagutil_panic("dag_attach_stream %s:%u: %s\n", dagname, dagstream, strerror(errno));

	if(dag_start_stream(dagfd, dagstream) < 0)
		dagutil_panic("dag_start_stream %s:%u: %s\n", dagname, dagstream, strerror(errno));

	dagutil_set_signal_handler(anysig);

#ifdef LANDER_ADDITION
	signal(SIGCHLD, release_child);
	init_statrec();
	init_seqnum();
	open_new_file();
#endif /* LANDER_ADDITION */
	
#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))

	dagutil_set_timer_handler(alarmsig, 1);

#elif defined(_WIN32)

	SetTimerFn();

#endif /* Platform-specific code. */

	report(); /* initialization */

	if (seconds)
		seconds += time(NULL);

	if (waitseconds)
		waitseconds += time(NULL);
	/*
	 * Initialise DAG Polling parameters.
	 */
	timerclear(&maxwait);
	maxwait.tv_usec = 100 * 1000; /* 100ms timeout */
	timerclear(&poll);
	poll.tv_usec = 10 * 1000; /* 10ms poll interval */


//	/* 32kB minimum data to return */
//	dag_set_stream_poll(dagfd, dagstream, 32*ONE_KIBI, &maxwait, &poll);
	/* Lander: 256kB minimum data to return */
	dag_set_stream_poll(dagfd, dagstream, 256*ONE_KIBI, &maxwait, &poll);

	/* wait seconds before capturing starts */
	/* give possibility to fill the memory hole for
		different testings
	*/
	while (waitseconds && (time(NULL) < waitseconds))
	{
	}

	/*
	 * The main Dag capture loop, wait for data and deliver.
	 * Reporting on the side.
	 */
 
	for (;;)
	{
		/* Call nonblocking to permit reporting in the absence of packets. */
		top = dag_advance_stream(dagfd, dagstream, &bottom);

		if (NULL == top)
		{
			if((dagstream & 0x1) && (errno == EAGAIN)) {
				usleep(10 * 1000);
				bottom = top;
				continue;
			}
			else {
				dagutil_panic("dag_advance_stream %s:%u: %s\n", dagname, dagstream, strerror(dag_get_last_error()));
			}
		}
		diff = top - bottom;

		if (diff == 0)
		{
                        fflush(ofd);
		}
#ifdef LANDER_ADDITION
		else if (fileoff + diff >= ofilesize && written_clip == 0) {
			written_clip = diff;
		}
#endif /* LANDER_ADDITION */
		/* 
		 * If more than maxwrite data available, then just write maxwrite now
		 * and then call dag_advance_stream again to free maxwrite space
		 */
		if (diff > maxwrite)
		{
			if (dagutil_get_verbosity() > 1)
				dagutil_verbose("diff = 0x%"PRIu64" (greater than maxwrite) bottom = 0x%08"PRIxPTR", top = 0x%08"PRIxPTR"\n", diff, (uintptr_t) bottom, (uintptr_t) top);
			written = maxwrite;
		}
		else if (0 == maximize)
		{
			written = (uint32_t)diff;
		}
		else
		{
			/* With the maximize option given, we only write complete chunks. */
			if (dagutil_get_verbosity() > 1)
				dagutil_verbose("diff = 0x%"PRIu64"    (less than maxwrite) bottom = 0x%08"PRIxPTR", top = 0x%08"PRIxPTR"\n", diff, (uintptr_t) bottom, (uintptr_t) top);
			if (doreport)
				report();
			
			continue;
		}
#ifdef LANDER_ADDITION
		if (ofilesize_over > 0 && fileoff + written > ofilesize + ofilesize_over) {
			written = ofilesize + ofilesize_over - fileoff; /* clip written even more */
		}
		if (written_clip > 0 && written > written_clip) {
			written = written_clip;
		}
#endif /* LANDER_ADDITION */

		errno = 0; /* no error */

		if (1 == maximize)
		{
			const void* source = bottom;
			int result;
			
			if (NULL != bounce_buffer)
			{
				/* Copy data to the bounce buffer, then write it. */
				memcpy(bounce_buffer, bottom, written);
				
				source = bounce_buffer;
			}

			/* Try writing directly. */
			result = write(STDOUT_FILENO, source, written);
			if (result != written)
			{
				if ((EFAULT == errno) && (NULL == bounce_buffer))
				{
					/* Allocate bounce buffer for next time through.
					posix_memalign(&bounce_buffer, ONE_KIBI, maxwrite); */
				
				#if defined(__FreeBSD__) || defined(__NetBSD__) || (defined(__APPLE__) && defined(__ppc__)) || defined(_WIN32) || (defined(__SVR4) && defined(__sun))
				
					/* FreeBSD (4.9, 4.11) doesn't have posix_memalign(), so use malloc() with extra padding and then align manually. */
					bounce_buffer = dagutil_malloc(maxwrite + ONE_KIBI);
					if (NULL == bounce_buffer)
					{
						dagutil_panic("malloc() failed: %s\n", strerror(errno));
						return EXIT_FAILURE;
					}
				
#if defined (__LP64__)
                    /* Align to a 1024-byte boundary if necessary. */
                    if (0 != ((uint64_t) bounce_buffer & 0x3ffLL))
                    {
                        /* Round up to next 1024-byte boundary. */
                        bounce_buffer = (void*) (((uint64_t) bounce_buffer + 0x400) & 0xfffffffffffffc00LL);
                    }
#else
                    /* Align to a 1024-byte boundary if necessary. */
                    if (0 != ((uint32_t) bounce_buffer & 0x3ff))
                    {
                        /* Round up to next 1024-byte boundary. */
                        bounce_buffer = (void*) (((uint32_t) bounce_buffer + 0x400) & 0xfffffc00);
                    }
#endif
				#else
				
					/* Use system posix_memalign(). */
					int error = posix_memalign(&bounce_buffer, ONE_KIBI, maxwrite);
					
					if (0 != error)
					{
						dagutil_panic("posix_memalign() failed: %d\n", error);
						return EXIT_FAILURE;
					}
					
				#endif /* posix_memalign() */
					
					if (dagutil_get_verbosity() > 1)
						dagutil_verbose("posix_memalign(%u, %d) returned 0x%08"PRIxPTR"\n", ONE_KIBI, written, (uintptr_t) bounce_buffer);
					continue;
				}
				else
				{
					dagutil_warning("write(%d, 0x%08"PRIxPTR", %u) returned %d\n", STDOUT_FILENO, (uintptr_t) source, written, result);
					dagutil_panic("write %d bytes to %s: %s\n", written, ofname, strerror(errno));
				}
			}
		}
		else
		{
#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))

			if (fwrite(bottom, 1, written, ofd) != written)
			{
				dagutil_panic("fwrite %d bytes to %s: %s\n", written, ofname, strerror(errno));
			}

#elif defined(_WIN32)

			if(_write( outfile , bottom , written) != written)
					dagutil_panic("write failed\n");

#endif /* Platform-specific code. */

			if (delay_msecs)
				dagutil_microsleep(delay_msecs*1000);
		}

		fileoff += written;

		if (doreport)
		{
			report();
		}
		
		if (diff)
			bottom += written;

		/* exit the loop if a signal was received, or the time has expired */
		if ((exit_now) || (seconds && (time(NULL) >= seconds)))
			break;
#ifdef LANDER_ADDITION
		/* if written < diff, we may not be at the record
		   boundary, so we can't start a new file */
		if (written_clip > 0) {
			if (written_clip < written)
				dagutil_panic("Clipping failed miserably: clip: %lu < %lu\n", 
					      (ulong)written_clip, (ulong)written);
			written_clip -= written;
			if (written_clip == 0) {
				open_new_file();
			} else if (ofilesize_over >0 && fileoff >= ofilesize + ofilesize_over) {
				/* reached the hard limit */
				uint64_t dropping = diff - written;
				dagutil_warning("reached hard limit on file size (file=%s, fileoff=%"
						PRIu64", dropping=%"PRIu64")\n", 
						cur_filename, fileoff, dropping);
				bottom += dropping;
				written_clip = 0;
				open_new_file();
			}
		}
#endif /* LANDER_ADDITION */
	}
	if (dag_stop_stream(dagfd, dagstream) < 0)
		dagutil_panic("dag_stop_stream %s:%u: %s\n", dagname, dagstream, strerror(errno));

	if (dag_detach_stream(dagfd, dagstream) < 0)
		dagutil_panic("dag_detach_stream %s:%u: %s\n", dagname, dagstream, strerror(errno));

	dag_close(dagfd);

#if defined(_WIN32)
	close(outfile);
#endif
	return EXIT_SUCCESS;
}

#ifdef LANDER_ADDITION
#define DAG_RECORD_TYPE_ETH	2
#define ETHERNET_FCS_BYTES	4
#define ETH_ADDR_LEN 		6	/* length of an ethernet address */
typedef struct stat_record_ {
	uint64_t	ts;
	uint8_t		type;
	flags_t		flags;
	uint16_t	rlen;
	uint16_t	lctr;
	uint16_t	wlen;
	uint8_t		offset;
	uint8_t		pad;
	u_char		dst[ETH_ADDR_LEN];
	u_char		src[ETH_ADDR_LEN];
	uint16_t	etype; /* ==0xffff */
	/* payload */
	uint16_t	lander_reserved; /* = 0 */
	uint8_t		lander_flags; /* = 0 */
	uint8_t		lander_version; /* = 1 */
	uint32_t	lander_wpkts; //XXX not implemented yet
	uint32_t	lander_wbytes;
} stat_record_t;
static stat_record_t _stat_record;
static void init_statrec(void) {
	static const uint16_t rlen = sizeof(stat_record_t);
	_stat_record = (stat_record_t) {
		.ts = 0,
		.type = DAG_RECORD_TYPE_ETH,
		.flags = {0},
		.rlen = htons(rlen),
		.lctr = 0,
		.wlen = htons(rlen + ETHERNET_FCS_BYTES),
		.offset = 0,
		.pad = 0,
		.dst = {0,0,0,0,0,0},
		.src = {0,0,0,0,0,0},
		.etype = htons(0xffff),
		.lander_reserved = 0,
		.lander_flags = 0,
		.lander_version = 1,
		.lander_wpkts = htonl(0),
		.lander_wbytes = htonl(0)
	};
}
static void write_statrec(FILE *fd) {
	struct timeval tv_now;
	gettimeofday(&tv_now, NULL);
	_stat_record.ts = ((uint64_t)tv_now.tv_sec << 32) | (((uint64_t)tv_now.tv_usec << 32)/1000000ULL);
	_stat_record.ts = TS(_stat_record.ts);
	
	if (fd== NULL)
		fd = stdout;

	if (fwrite(&_stat_record, 1, sizeof(_stat_record), fd) != sizeof(_stat_record))
		dagutil_panic("cannot insert stat record: %s\n", strerror(errno));
}
static void init_seqnum(void) {
	/* write _seqnum to a disk */
	int  rlen;
	char str[128];
	char *endptr = &str[0];
	int seqnumfd;

	if (oseqnumfile == NULL) {
		return;
	}
	seqnumfd = open(oseqnumfile, 
			O_RDONLY|O_CREAT|O_SYNC, 
			S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH);
	if (seqnumfd < 0) {
		dagutil_panic("cannot open seqnum file (%s): %s\n", 
			      oseqnumfile, strerror(errno));
	}
	rlen = read(seqnumfd, str, sizeof(str)-1);
	if (rlen < 0) {
		dagutil_panic("problem reading from seqnum file (%s): %s\n", 
			      oseqnumfile, strerror(errno));
	} else if (rlen == 0) {
		dagutil_warning("seqnum file (%s) is truncated, starting from zero\n",
				oseqnumfile);
		_seqnum = 0;
	} else {
		/* basic error checking */
		str[rlen] = '\0';
		if (str[rlen-1] != '\n' || strlen(str) != rlen) {
			dagutil_panic("corrupt seqnum file (%s): %s\n", 
				      oseqnumfile, str);
		}
		_seqnum = strtoul(str, &endptr, 10);
		if (*endptr != '\n') {
			dagutil_panic("corrupt seqnum file (%s): %s [%lu]\n", 
				      oseqnumfile, str, _seqnum);
		}
		if (++_seqnum > SEQNUM_MAX)
		  _seqnum = 0; /* wrap */

	}
	close(seqnumfd);
}
static void save_seqnum(void) {
	char str[128];
	int seqnumfd;
	int wlen;
	if (oseqnumfile == NULL) {
		return;
	}
	seqnumfd = open(oseqnumfile, 
			O_WRONLY|O_CREAT|O_SYNC|O_TRUNC
			,
			S_IRUSR|S_IRGRP|S_IROTH);

	if (seqnumfd < 0) {
		dagutil_panic("error reopening seqnum file (%s): %s\n", 
			      oseqnumfile, strerror(errno));
	}
	sprintf(str, "%lu\n", _seqnum);
	wlen = strlen(str);
	if (wlen != write(seqnumfd, str, wlen))
		dagutil_panic("error writing seqnum file (%s): %s\n", 
			      oseqnumfile, strerror(errno));
	close(seqnumfd);
}
static void *close_file(void *p) {
	FILE *fd = p;
	if (fclose(fd) < 0)
		perror("fclose():");

	return NULL;
}
static void open_new_file(void) {
	time_t erf_time;
	uint64_t erf_timestamp = 0;
	uint32_t erf_seconds;
	struct tm *tm_ptr;
	static char namebuf[1024];
	unsigned diff = 0;
	int flags = 0;
	size_t slept = 0;
	int pipefd[2];
	
	if (ofd != NULL) {
		/* inject last made up record with gettimeofday() timestamps */
		write_statrec(ofd); 

#if 0
		/* close previous file: closing NFS files can take a while, so
		 * put the file descriptor in non-blocking mode first */
		if (-1 == fcntl(fileno(ofd), F_SETFL,
				fcntl(fileno(ofd), F_GETFL) | 
				O_NONBLOCK))
			dagutil_panic("fcntl: can't do non-blocking close %s:%u: %s\n", 
				      dagname, dagstream, strerror(dag_get_last_error()));
		fclose(ofd);
#else
		/* close previous file: closing NFS files can take a while, so
		 * fork off a new thread to do that. */
		pthread_t th;
		pthread_attr_t attr;
		
		if (pthread_attr_init(&attr) != 0)
			dagutil_panic("pthread_attr_init");
		
		if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0)
			dagutil_panic("pthread_attr_setdetachstate");
		
		if (pthread_create(&th, &attr, close_file, ofd) != 0)
			dagutil_panic("pthread_create");

#endif
		ofd = NULL;
	}


	/* before opening, sleep on a watchfile if any */
	slept = sleep_on_watchfile(owatchfile);
	if (slept > 0)
		dagutil_warning("slept on %s for %u seconds\n", 
			owatchfile, (unsigned)slept);
	
	do {
		uint8_t *xbottom = bottom;
		/* read some data into a temp buffer to get latest timestamps */
		top = dag_advance_stream(dagfd, dagstream, &bottom);
		if (xbottom == NULL && orecsize) {
			/* first time: find the remainder */
			_remainder = (ulong)bottom % (ulong)orecsize;
		}
		if (NULL == top)
			dagutil_panic("dag_advance_stream %s:%u: %s\n", 
				      dagname, dagstream, strerror(dag_get_last_error()));
		diff = top - bottom;
	} while (diff <= orecsize + sizeof(erf_timestamp));

	if (orecsize) {
		ulong rem = (ulong)bottom % (ulong)orecsize;
		if (rem != _remainder) {
			int correction = _remainder - rem;
			if (correction < 0)
				correction += orecsize;
			bottom += correction;
			dagutil_warning("needs correction: %lu, %lu (skip %d bytes)\n", 
				rem, _remainder, correction);
		}
		
	}

	/* read the first packet timestamp from the buffer: first 8 bytes */
	memcpy(&erf_timestamp, bottom, sizeof(erf_timestamp));
	erf_timestamp = TS(erf_timestamp);
	erf_seconds = (erf_timestamp >> 32);
	
	/* make up a new file's name */
	erf_time = (time_t)erf_seconds;
	tm_ptr = (ouse_utc_time) 
		? gmtime(&erf_time) 
		: localtime(&erf_time);
			
	tm_ptr->tm_year += 1900;
	tm_ptr->tm_mon++;

    sprintf(namebuf, "%s%4d%02d%02d-%02d%02d%02d-"SEQNUM_FMT"%s",
			obasename,
			tm_ptr->tm_year, tm_ptr->tm_mon, tm_ptr->tm_mday,
			tm_ptr->tm_hour, tm_ptr->tm_min, tm_ptr->tm_sec,
            _seqnum, file_ext_buf);

	/* open a new one */
	errno = 0;
	cur_filename = namebuf;
	flags = O_WRONLY|O_LARGEFILE|O_CREAT;
	if (maximize)
		flags |= O_DIRECT;

	{
		int fd;
		char *mode = "w";
		if ((fd = open(cur_filename, flags, 0660)) == -1) {
			if (errno == EEXIST) {
				dagutil_warning("Output file '%s' already exists\n",
                                cur_filename);
			} else {
				dagutil_warning("Error opening file '%s': %s\n",
                                cur_filename, strerror(errno));
			}
			exit(1);
		}
		ofd = fdopen(fd, mode);
		if (ofd == NULL)
			dagutil_panic("fdopen %s: %s\n", cur_filename, strerror(errno));
	}

	if (compressor_enabled) {
		int pipefd[2];
		pid_t compressor_pid;
		int i;
		int maxfd = getdtablesize();
#define PIPEBUFSZ (8*ONE_MEBI)
        int bufsz;

        if (0 != socketpair(AF_UNIX, SOCK_STREAM, 0, pipefd)) {
			dagutil_panic("Error creating pipe: '%s'\n", strerror(errno));
		}
        bufsz = PIPEBUFSZ;
        if (0!= setsockopt(pipefd[0], SOL_SOCKET, SO_RCVBUF, &bufsz, sizeof(bufsz))) {
            dagutil_warning("setsockopt SO_RCVBUF failed\n");
        }
        bufsz = PIPEBUFSZ;
        if (0!= setsockopt(pipefd[1], SOL_SOCKET, SO_SNDBUF, &bufsz, sizeof(bufsz))) {
            dagutil_warning("setsockopt SO_SNDBUF failed\n");
        }
		compressor_pid = fork();
		if (compressor_pid == -1) {
			dagutil_panic("Error forking compressor: '%s'\n", strerror(errno));
		}
		if (compressor_pid == 0) {
			/* child */
			if (-1 == dup2(pipefd[0], STDIN_FILENO)) {
				dagutil_panic("Error DUP compressor stdin: '%s'\n", strerror(errno));
			}
			close(pipefd[0]);
			if (-1 == dup2(fileno(ofd), STDOUT_FILENO)) {
				dagutil_panic("Error DUP compressor stdout: '%s'\n", strerror(errno));
			}
			//close(pipefd[1]);
			for (i=0; i<maxfd; ++i) {
				if (i != STDIN_FILENO && i != STDOUT_FILENO)
					close(i);
			}
			execvp(compressor_arg[0], compressor_arg);
			/* should not return */
			dagutil_panic("Return from execvp");
		} else {
			/* parent */
			close(pipefd[0]); /* read end of pipe */
			fclose(ofd);
			ofd = fdopen(pipefd[1], "w");
		}
		
	} 
     
	/* inject first made up record with gettimeofday() timestamps */
	write_statrec(ofd);

	save_seqnum(); /* save last sequence number written */
	if (++_seqnum > SEQNUM_MAX)
		_seqnum = 0; /* wrap */


	fileoff = 0;
#if 0
	/* write what we just read to the new file */
	{
		size_t towrite = diff;
		size_t written = 0;
		int recs = 0;	
		while (towrite) {
			errno = 0; /* no error */
			recs = fwrite(bottom+written, towrite, 1, ofd);
			if (recs < 0)
				dagutil_panic("write %u bytes to %s: %s\n",
							  (unsigned int)towrite, cur_filename, strerror(errno));
			towrite -= towrite;
			written += towrite;
			fileoff += towrite;
		}
		bottom += diff;
	}
#else
	/* don't write anything, but don't change bottom ptr either */
#endif
	/* dup the descriptor */
	//if (ofd != STDOUT_FILENO) {
	//	dup2(ofd, STDOUT_FILENO);
	//	close(ofd);
	//	ofd = STDOUT_FILENO;
	//}
}

static size_t sleep_on_watchfile(const char *watchfile)
{
	int slept = 0;
	int suspended = 0;
	if (!watchfile)
		return slept;
	
	for (;;) {
	        if (exit_now) exit(0);
		int fd = open(watchfile, O_RDONLY);
		if (fd != -1) {
			/* file exists */
			close(fd);
			break;
		} else {
			/* file does not exist: must sleep */
			if (!suspended) {
				dagutil_warning("going into sleep...\n");
				/* suspend capture */
				if (dag_stop_stream(dagfd, dagstream) < 0)
					dagutil_panic("dag_stop_stream %s:%u: %s\n", 
						      dagname, dagstream, strerror(errno));
				if (dag_detach_stream(dagfd, dagstream) < 0)
					dagutil_panic("dag_detach_stream %s:%u: %s\n", 
						      dagname, dagstream, strerror(errno));
				suspended = 1;
			}
			/* file doesn't exist: sleep then repeat */
			sleep(1);
			++slept;
		}
	}
	if (suspended) {
		/* resume */
		if(dag_attach_stream(dagfd, dagstream, 0, 0) < 0)
			dagutil_panic("dag_attach_stream %s:%u: %s\n", 
				      dagname, dagstream, strerror(errno));
		if(dag_start_stream(dagfd, dagstream) < 0)
			dagutil_panic("dag_start_stream %s:%u: %s\n", 
						  dagname, dagstream, strerror(errno));
		top = bottom = NULL;
	}
	return slept;
}

static void
release_child(int sig) {
	int status = 0;
	waitpid(-1, &status, WNOHANG);
	signal(SIGCHLD, release_child);
	return;
}

#endif /* LANDER_ADDITION */

static void
anysig(int sig)
{
#if defined(__FreeBSD__) || defined(__linux__) || defined (__NetBSD__) || (defined(__SVR4) && defined(__sun)) || (defined(__APPLE__) && defined(__ppc__))
    /* Restore the default signal handlers, so the next interrupt will have the default effect (e.g. exit) */
    dagutil_set_signal_handler(SIG_DFL);

    /* Tell the main loop to exit */
    exit_now = 1;
    return;

#elif defined(_WIN32) 

	/* FIXME: shouldn't really call this from a signal handler. */
	if (dag_stop(dagfd) < 0)
		_exit(EXIT_FAILURE);

	_commit(outfile);
#endif /* Platform-specific code. */

	_exit(EXIT_SUCCESS);
}

static void
alarmsig(int sig)
{
	doreport++;
}

static void
report(void)
{
	static struct timeval last;
	static int64_t lastoff;
	static char label[80] = "";

	struct timeval now;
	struct timeval tdiff;
	uint64_t odiff;
	double rate;

	gettimeofday(&now, NULL);
	if (0 == timerisset(&last))
	{
		/*
		 * Initial call
		 */
		if (dagutil_get_verbosity() > 1)
			snprintf(label, sizeof(label), "%s\t", dagname);
		last = now;
		return;
	}

	timersub(&now, &last, &tdiff);
	odiff = (uint64_t) (fileoff - lastoff);
	/*
	 * Bytes/mebisecond == Mebibytes/second.
	 */
	if (tdiff.tv_sec == 0 && tdiff.tv_usec == 0 )
		rate = 0;
	else
		rate = ((double) odiff) / (((double) tdiff.tv_sec * ONE_MEBI) + tdiff.tv_usec);
	
	dagutil_verbose("%s%10.3f MiBytes %8.3f MiBytes %8.3f MiBytes/sec (%d Megabps)\n", label,
		((double) fileoff) / ONE_MEBI, ((double) diff) / ONE_MEBI, rate, (unsigned int) ((((8 * rate*1024)/1000)*1024)/1000));
	lastoff = fileoff;
	last = now;
	doreport = 0;
}


#ifndef ENDACE_UNIT_TEST
int
main(int argc, const char* const * argv)
{
	return dagsnap_main(argc, (char**) argv);
}
#endif /* ENDACE_UNIT_TEST */
