#include <stdio.h>
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/event.h>
#include <sys/signal.h>
#include <sys/time.h>
#include <termios.h>
#include <unistd.h>
#include <syslog.h>

int logfd,portfd;
char *log_name = NULL;
char *pidfile_name = NULL;
FILE *pidfd = NULL;

int main (int argc, char *argv[])
{
	int i,rc,nread,kq,fflag,flag;
	pid_t pid;
	struct kevent ke[4]; 	
	struct termios options;
	char s[512];
	char *speed = NULL;
	char *dev_name = NULL;

	pidfile_name = "/var/run/logserial.pid";
	opterr = 0;
	fflag = 0;
	speed = "9600";
	while ((flag = getopt(argc, argv, "d:s:l:p:f")) != -1) {

		switch (flag) {
			case 'd':
				dev_name = optarg;
				break;
			case 's':
				speed = optarg;
				break;
			case 'l':
				log_name = optarg;
				break;
			case 'p':
				pidfile_name = optarg;
				break;
			case 'f':
				fflag = 1;
				break;
			case '?':
				if (isprint(optopt))
					fprintf(stderr,
					  "Unknown option -%c'.\n", optopt);
				else
					fprintf(stderr,
					  "Unknown option character `\\x%x'.\n", optopt);
				return 1;
			default:
				break;

		}
	}	
	for (; optind < argc; optind++) 
		printf("%s", argv [optind]);
	if ((dev_name == NULL) | (log_name == NULL)) {
		printf("usage: logserial -d device name [-s speed of connection] -l logfile name [-f run in foreground][-p pid file]\n");
		exit(1);
		}
	if (fflag == 0) 
		if (daemon(0, 0) == -1) {
			openlog("logserial", LOG_PID, LOG_DAEMON);
			syslog(LOG_ERR, strerror(errno));
			exit(1);
		}
	pid = getpid();
	printf("%d\n", pid);
	pidfd = fopen(pidfile_name, "w+");
	if (pidfd == NULL) {
		openlog("logserial", LOG_PID, LOG_DAEMON);
		syslog(LOG_ERR, strerror(errno));
		exit(1);
	}
	fprintf(pidfd, "%d\n", pid);
	fclose(pidfd); 

	portfd = open(dev_name, O_RDONLY | O_NOCTTY | O_NDELAY );
	if (portfd == -1) {
		syslog(LOG_ERR, strerror(errno));
		exit(1);
	}
	
	tcgetattr(portfd, &options);  
	if (cfsetspeed(&options,strtol(speed, NULL, 10)) == -1) {
		syslog(LOG_ERR, strerror(errno));
		exit(1);
	}
	options.c_lflag &= ~ICANON;
	if (tcsetattr(portfd, TCSANOW, &options) != 0) {
	syslog(LOG_ERR, strerror(errno));
		exit(1);
	}
	
	if ((kq = kqueue())== -1) {
		syslog(LOG_ERR, strerror(errno));
		exit(1);
	} 
	bzero(&ke, sizeof(ke));
	EV_SET(&ke[0], portfd, EVFILT_READ, EV_ADD | EV_CLEAR, 0, 0, NULL);
	EV_SET(&ke[1], SIGHUP, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
	EV_SET(&ke[2], SIGTERM, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
	EV_SET(&ke[3], SIGINT, EVFILT_SIGNAL, EV_ADD, 0, 0, NULL);
	if (kevent(kq, ke, 4, NULL, 0, NULL) == -1 ) {
	syslog(LOG_ERR, strerror(errno));
		exit(1);
	}
	bzero(&ke, sizeof(ke));
	if (fcntl(portfd, F_SETFL, FNDELAY | O_NONBLOCK) == -1) {
		syslog(LOG_ERR, strerror(errno));
		exit(1);
	}

	logfd = open(log_name, O_RDWR | O_APPEND | O_CREAT, 0666);
	if (logfd == -1) {
		syslog(LOG_ERR, strerror(errno));
		exit(1);
	}
	signal(SIGHUP,SIG_IGN);
	signal(SIGTERM,SIG_IGN);
	signal(SIGINT,SIG_IGN);
	for (;;) { 
		if ((rc=kevent(kq, NULL, 0, ke, 1, NULL)) == -1) {
			if (errno == EINTR) {
				syslog(LOG_ERR, "Interrupted kevent call. Strange.");
				continue;
			} else {
				syslog(LOG_ERR, strerror(errno));
				exit(1);
			}
		}

		for (i=0; i<rc; ++i) {
			if (ke[i].filter == EVFILT_READ && ke[i].ident == portfd) {
				nread = read(portfd, s, sizeof(s));
				if (nread < 0) {
					syslog(LOG_ERR, strerror(errno));
					exit(1);
				}
				write(logfd, s, nread); 
			} else if (ke[i].filter == EVFILT_SIGNAL && ke[0].ident == SIGHUP) {
				close(logfd);
				logfd = open(log_name, O_RDWR | O_APPEND | O_CREAT, 0666);
	       		if (logfd < 0) {
					syslog(LOG_ERR, strerror(errno));
					exit(1);
				}	
			} else if (ke[i].filter == EVFILT_SIGNAL &&
						(ke[0].ident == SIGTERM || ke[0].ident == SIGINT)) { 
				close(logfd);
				close(portfd);
				remove(pidfile_name);
				exit(0);
			}		
				
		}
	}
	return (0);
}



syntax highlighted by Code2HTML, v. 0.9.1