/************************************************************
 *
 * COPYRIGHT (C) HITACHI,LTD. 2002-2003 ALL RIGHTS RESERVED.
 * WRITTEN BY HITACHI SYSTEMS DEVELOPMENT LABORATORY,
 *            HITACHI CENTRAL RESEARCH LABORATORY.
 *
 * Created by T.Nakamura <tetsu@crl.hitachi.co.jp>
 * Updated by M.Hiramatsu <hiramatu@sdl.hitachi.co.jp>
 *
 ************************************************************/

#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <string.h>
#include <stdarg.h>
#include <syslog.h>
#include <fcntl.h>
#include <linux/lkst.h>
#include <linux/lkst_maskset.h>
#include <linux/lkst_buffer.h>
#include <linux/lkst_evhandler.h>

#define LKST_ETYPE_DEF_DESCRIPTIONS
#include <linux/lkst_events.h>

#define COPYRIGHT "COPYRIGHT (C) HITACHI,LTD. 2002-2003 ALL RIGHTS RESERVED."
#define NAME	"lkstlogd"
#define VERSION "1.5"

#define LOG_HEADER_SIZE \
	(sizeof(log_header_t) +					\
	sizeof(struct posix_log_entry) +			\
	2*sizeof(int) + sizeof(char)*LKST_ARCH_NAME_LEN)


//#define DEBUG

int  owner_daemon();
int  child_daemon_main();
void child_buf_read();
void chk_limit_and_seek(int, int);
void write_header(int);
int  file_write(int, struct lkst_log_record*, int);
void cleanup();
void parse_arg(int argc, char **argv);
int  setup_log_file(void);

void (*signal_reuse(int signum, void (*handler)(int)))(int);
void owner_hup_handler(int);
void owner_die_handler(int);
void owner_snap_handler(int);
void owner_switch_handler(int);
void owner_child_handler(int);
void child_die_handler(int);
void child_switch_handler(int);
void child_snap_handler(int);

int  kill_children(int);

void usage(void);
void buffer_setrmod(int);
void buffer_ring(int, int);

void init_maskset(int);
int restore_maskset(int);

void init_buffer(int);
int restore_buffer(int);

int  create_pid_file();
int  delete_pid_file();

void syslog_info(const char *format, ...);
void syslog_warning(const char *format, ...);
void syslog_error(const char *format, ...);
void stderr_info(const char *format, ...);
void stderr_warning(const char *format, ...);
void stderr_error(const char *format, ...);

volatile int read_on = 0;
volatile int term_flg = 0;
volatile int restart = 0;
volatile int file_snum = 0;
volatile int switchlog = 0;

struct timeval xtime;
lkst_tsc_t tsc;
u_int64_t cpu_hz;
int endian_big;
int buf_ver;
char arch[LKST_ARCH_NAME_LEN];

#ifndef LKSTLOGD_PID
# ifdef DEBUG
#  define LKSTLOGD_PID "./lkstlogd.pid"
# else
#  define LKSTLOGD_PID "/var/run/lkstlogd.pid"
# endif
#endif

#ifndef LKSTLOGD_LOGFILE
# ifdef DEBUG
#  define LKSTLOGD_LOGFILE "./sebuf"
# else
#  define LKSTLOGD_LOGFILE "/var/log/lkst/sebuf"
# endif
#endif

#define DEFAULT_LIMIT_SIZE	10*1024*1024	/* 10M byte */
#define INIT_BUF_SIZE_DEFAULT	2*1024*1024	/* 2Mbyte */
#define INIT_BUF_SIZE_MAX	64*1024*1024	/* 64Mbyte */
#define INIT_BUF_SIZE_MIN	8*1024		/* 8Kbyte */
#define INIT_BUF_NUM_DEFAULT	2
#define INIT_BUF_NUM_MAX	10
#define INIT_BUF_NUM_MIN	2

static char pid_file[256] = LKSTLOGD_PID;
static char log_file[256] = LKSTLOGD_LOGFILE;

int init_buf_size = INIT_BUF_SIZE_DEFAULT;
int init_buf_num = INIT_BUF_NUM_DEFAULT;

int ncpu = 0;				// number of forked process
unsigned long cpu_online_map = 0; 	// a bitmap of online cpus.
int cpu_id = -1;	// cpu id of child process(set on the child process)

#define for_each_online_cpus(cpu, online_map) \
	for(cpu = 0; cpu < LKST_CPU_MAX; cpu++) \
		if ( online_map & (1UL << cpu) ) 


struct log_info {
	int c_pid;
	int buf_size;
	int limit_size;
	int total_size;
	lkst_buffer_id prev_buf_id;
	lkst_buffer_id buf_id[INIT_BUF_NUM_MAX];
};

struct log_info info[LKST_CPU_MAX] = {
	[0 ... LKST_CPU_MAX-1] = {
		.c_pid=		-1, 
		.buf_size=	0, 
		.limit_size=	0, 
		.total_size=	0, 
		.prev_buf_id=	LKST_BUFFER_ID_VOID,
		.buf_id=	{[0 ... INIT_BUF_NUM_MAX-1] = LKST_BUFFER_ID_VOID}
	}
};

lkst_maskset_id maskset_id_prev = LKST_MASKSET_ID_VOID;
		// previous selected maskset id
lkst_maskset_id maskset_id_daemon = LKST_MASKSET_ID_VOID;
		// ID of maskset made by daemon 

void (*infolog)(const char *format, ...) = stderr_info;
void (*warnlog)(const char *format, ...) = stderr_warning;
void (*errlog)(const char *format, ...)  = stderr_error;


int __inline__ buffer_read_size(int buf_size)
{
	return buf_size * sizeof(struct lkst_log_record) / LKST_SIZEOF_LKST_EVENT_RECORD;
}

int __inline__ open_dev(void)
{
	int fd;
	if ((fd = open("/dev/lkst", O_RDONLY)) < 0) {
		errlog("Cannot open /dev/lkst!; %s\n",strerror(errno));
		exit(1);
	}
	return fd;
}

int main(int argc, char** argv)
{
	parse_arg(argc, argv);

	if ( getuid()!=0 ) {
		errlog("Must be root.\n Program abooted.\n");
		return 1;
	}

	if ( daemon(0,0) < 0) {
		errlog("Failed to daemonize\n");
		return 1;
	}
	owner_daemon();

	exit(0);
}

int owner_daemon()
{
	int i;
	int devfd;

	/* open syslog */
	openlog("lkstlogd", LOG_PID, LOG_DAEMON);
	errlog = syslog_error;
	warnlog = syslog_warning;
	infolog = syslog_info;
	
	infolog("Invoked %s Ver%s\n", NAME, VERSION);
	/* register cleanup function to run at exit*/
	if ( atexit(cleanup) ) {
		errlog("atexit() failed\n");
		exit(1);
	}

	/* check & write pid file */
	if (create_pid_file() < 0 ){
		exit(1);
	}

	/* open device */
	devfd = open_dev();

	/* set restart flag */
	restart = 1;

	/* Main loop */
	do {

		/* change maskset for daemon */
		init_maskset(devfd);

		/* add buffers and get active cpus */
		init_buffer(devfd);

		/* set up sighandler */
		if(signal(SIGHUP, owner_hup_handler) == SIG_ERR ||
		   signal(SIGINT, owner_die_handler) == SIG_ERR ||
		   signal(SIGTERM, owner_die_handler) == SIG_ERR ||
		   signal(SIGCHLD, owner_child_handler) == SIG_ERR ||
		   signal_reuse(SIGUSR1, owner_snap_handler) == SIG_ERR ||
		   signal_reuse(SIGUSR2, owner_switch_handler) == SIG_ERR ) {
			errlog( "Cannot register signal handlers\n");
			exit(1);
		}

		/* fork children */
		for_each_online_cpus(i, cpu_online_map){
			if ( (info[i].c_pid = fork()) < 0) {
				errlog( "Unable to fork process\n");
				exit(1);
			} else if(!info[i].c_pid) {
				cpu_id = i;
				child_daemon_main();
				exit(0);
			}
		}

		/* wait children */
		for_each_online_cpus(i, cpu_online_map){
			int status;
			while( 0 > waitpid((pid_t)info[i].c_pid, &status, 0));
			info[i].c_pid = -1;
		}

		/* restore previous setting */
		if ( restore_buffer(devfd) < 0 ||
		     restore_maskset(devfd) < 0 ) {
			exit(1);
		}

	} while (restart);

	/* close device */
	close(devfd);
	closelog();

	return 0;
}

int child_daemon_main()
{
	/* check if daemon can write to log file.*/
	close(setup_log_file());

	/* set up sighandler */
	if (signal(SIGHUP, SIG_DFL) == SIG_ERR ||
	    signal(SIGINT, SIG_DFL) == SIG_ERR ||
	    signal(SIGTERM, child_die_handler) == SIG_ERR || 
	    signal_reuse(SIGUSR1, child_snap_handler) == SIG_ERR ||
	    signal_reuse(SIGUSR2, child_switch_handler) == SIG_ERR ) {
		errlog( "Failed to register signal handlers\n");
		exit(1);
	}

	/* child main loop */
	term_flg = 0;
	do {
		if (read_on)
			child_buf_read();
		else 
			pause();
	} while (!term_flg);

	return 0;
}

void chk_limit_and_seek(int fd, int res_size)
{
	int ret=0;

	if(info[cpu_id].total_size > info[cpu_id].limit_size) {
		ret = lseek(fd, (off_t)LOG_HEADER_SIZE, SEEK_SET);
		if(ret<0) {
			errlog("Can't seek file pointer.\n");
			exit(1);
		} else {
			info[cpu_id].total_size = LOG_HEADER_SIZE + res_size;
		}

	}
}

int setup_log_file(void)
{
	char tmpname[256];
	int fd;
	
	snprintf(tmpname,255,"%s%02d.%01d", log_file, cpu_id, file_snum);
	if ( (fd = open(tmpname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR|S_IWUSR)) < 0){
		errlog("Can't open %s.\n", tmpname);
		exit(1);
	}
	/* write log header */
	write_header(fd);
	info[cpu_id].total_size = LOG_HEADER_SIZE;
	return fd;
}

void child_buf_read(void)
{
	int read_size, res_size;
	int devfd, logfd;
	struct lkst_log_record *buffer;

	/* allocate buffer */
	read_size = buffer_read_size(info[cpu_id].buf_size);
	buffer = (struct lkst_log_record *) malloc(read_size);
	if(!buffer){
		errlog("Can't allocate buffer area.\n");
		exit(1);
	}

	devfd = open_dev();

	buffer_setrmod(devfd);
	buffer_ring(devfd, 1);

	logfd = setup_log_file();
	switchlog = 0;
	while(read_on){
		if((res_size = read(devfd, buffer, read_size)) < 0){
			errlog("lkst device read() error: %s\n", strerror(errno));
		}
		if (res_size > 0) {
			info[cpu_id].total_size += res_size;
			chk_limit_and_seek(logfd, res_size);
			file_write(logfd, buffer, res_size);
			if(switchlog){
				close(logfd);
				logfd = setup_log_file();
				switchlog = 0;
			}
		}
	}

	buffer_ring(devfd, 0);
	free(buffer);
	close(logfd);
	close(devfd);
}


void write_header(int fd)
{
	log_header_t logh = {
		log_magic: LOGFILE_MAGIC,
		log_version: 0
	};
	struct posix_log_entry posix_entry = {
		log_size: 2*sizeof(int) + sizeof(char)*LKST_ARCH_NAME_LEN,
		log_format:PXLOG_BINARY
	};
	write(fd, &logh, sizeof(log_header_t));
	write(fd, &posix_entry, sizeof(struct posix_log_entry));
	write(fd, &endian_big, sizeof(int));
	write(fd, &buf_ver, sizeof(int));
	write(fd, arch, sizeof(char)*LKST_ARCH_NAME_LEN);
}


void __inline__ calc_logtime(struct lkst_log_record *buffer, int read_count)
{
	int j;
	struct timespec *buf, base, diff;
	lkst_tsc_t log_cnt, diff_cnt;

	base.tv_sec = xtime.tv_sec;
	base.tv_nsec = 1000 * xtime.tv_usec;

	for (j = 0; j < read_count; j++) {
	/* buffer[j].posix.log_time.tv_sec has upper 32bit of cpu counter
	   buffer[j].posix.log_time.tv_nsec has lower 32bit of cpu counter */

		buf = &(buffer[j].posix.log_time);

		log_cnt = ((u_int64_t) buf->tv_sec << 32) |
			(unsigned long) buf->tv_nsec;
		
		if (tsc > log_cnt){
			diff_cnt = tsc - log_cnt;
			diff.tv_sec  = (unsigned long) (diff_cnt / cpu_hz);
			diff.tv_nsec = (unsigned long) ((diff_cnt % cpu_hz) *
							1000000000 / cpu_hz);
			
			if (base.tv_nsec > diff.tv_nsec){
				buf->tv_sec  = base.tv_sec - diff.tv_sec;
				buf->tv_nsec = base.tv_nsec - diff.tv_nsec;
			} else {
				buf->tv_sec  = base.tv_sec - diff.tv_sec - 1;
				buf->tv_nsec = base.tv_nsec + 1000000000 -
					diff.tv_nsec;
			}
		} else {
			diff_cnt = log_cnt - tsc;
			diff.tv_sec  = (unsigned long) (diff_cnt / cpu_hz);
			diff.tv_nsec = (unsigned long) ((diff_cnt % cpu_hz) *
							1000000000 / cpu_hz);
			
			if(base.tv_nsec + diff.tv_nsec >= 1000000000){
				buf->tv_sec  = base.tv_sec + diff.tv_sec + 1;
				buf->tv_nsec = base.tv_nsec + diff.tv_nsec -
					1000000000;
			} else{
				buf->tv_sec  = base.tv_sec + diff.tv_sec;
				buf->tv_nsec = base.tv_nsec + diff.tv_nsec;
			}
		}
	}
}

int file_write(int fd, struct lkst_log_record *buffer, int res_size)
{
	int read_count;

	read_count = res_size / sizeof(struct lkst_log_record);

	/* conver mc to time */
	calc_logtime(buffer, read_count);

	/* write to file */
	return write(fd, buffer, sizeof(struct lkst_log_record)*read_count);
}

void init_buffer(int devfd)
{
	struct lkst_buffer_param param;
	struct lkst_buffer_linkparam lparam;
	struct lkst_status_param sparam;
	int i, j;

	/* get ncpu and cpu_online_map*/
	if ( ioctl(devfd, LKST_IOC_TRC_STATUS, &sparam) < 0){
		errlog("ioctl(LKST_IOC_TRC_STATUS) error: %s\n", strerror(errno));
		exit(1);
	}

	cpu_online_map = sparam.online_cpu;

	ncpu = 0;
	for_each_online_cpus(i, cpu_online_map) {
		ncpu++;
		info[i].prev_buf_id =  sparam.write_buffer_id[i];
		/*create buffers*/
		for (j=0; j<init_buf_num; j++){
			param.id = LKST_BUFFER_ID_VOID;
			param.next = LKST_BUFFER_ID_VOID;
			param.size = init_buf_size;
			param.cpu = i;
			if( ioctl(devfd, LKST_IOC_BUFFER_CREATE, &param) < 0) {
				errlog("ioctl(LKST_IOC_BUFFER_CREATE) error: %s\n", strerror(errno));
				exit(1);
			}
			info[i].buf_id[j] = param.id;
			info[i].buf_size = param.result_size;
		}
		/*link(ring) buffers*/
		for (j=0; j<init_buf_num; j++){
			/*make a ring structure*/
			lparam.cpu = i;
			lparam.id = info[i].buf_id[j];
			lparam.next = info[i].buf_id[(j+1)%init_buf_num];
			if( ioctl(devfd, LKST_IOC_BUFFER_LINK, &lparam) < 0) {
				errlog("ioctl(LKST_IOC_BUFFER_LINK) error: %s\n", strerror(errno));
				exit(1);
			}
		}
	}
}

int restore_buffer(int devfd)
{
	int i, j, ret = 0;
	struct lkst_buffer_jumpparam jparam;
	struct lkst_buffer_delparam dparam;
	struct lkst_status_param sparam;

	/* get currently writing buffers */
	if ( ioctl(devfd, LKST_IOC_TRC_STATUS, &sparam) < 0){
		errlog("ioctl(LKST_IOC_TRC_STATUS) error: %s\n", strerror(errno));
		for_each_online_cpus(i, cpu_online_map)
			sparam.write_buffer_id[i] = LKST_BUFFER_ID_VOID;
	}

	for_each_online_cpus(i, cpu_online_map) {
		if (info[i].prev_buf_id != LKST_BUFFER_ID_VOID) {
			if ( info[i].prev_buf_id != sparam.write_buffer_id[i] ) {
				/* jump to previous used buffer */
				jparam.cpu = i;
				jparam.dest = info[i].prev_buf_id;
				if( ioctl(devfd, LKST_IOC_BUFFER_JUMP, &jparam) <0 ) {
					errlog("ioctl(LKST_IOC_BUFFER_JUMP) error: %s\n", strerror(errno));
					ret = -1;
				}else
					info[i].prev_buf_id = LKST_BUFFER_ID_VOID;
			}else 
				info[i].prev_buf_id = LKST_BUFFER_ID_VOID;
		}
		/* delete own buffers */
		for (j=0; j<init_buf_num; j++){
			if (info[i].buf_id[j] == LKST_BUFFER_ID_VOID) continue;
			dparam.cpu = i;
			dparam.id = info[i].buf_id[j];
			if ( ioctl(devfd, LKST_IOC_BUFFER_DELETE, &dparam) < 0) {
				errlog("ioctl(LKST_IOC_BUFFER_DELETE) error: %s\n", strerror(errno));
				ret = -1;
			}else 
				info[i].buf_id[j] = LKST_BUFFER_ID_VOID;
		}
	}
	return ret;
}

void init_maskset(int devfd)
{
	int i;
	struct lkst_maskset_param param;
	struct lkst_maskset_body maskset;
	struct lkst_maskset_entry *entry;

	/* read current maskset */
	param.id = LKST_MASKSET_ID_VOID;
	param.maskset_size = LKST_MASKSET_SIZE(LKST_MASKSET_TABLE_LEN_MAX);
	param.maskset = &maskset;

	if (ioctl(devfd, LKST_IOC_MASKSET_READ, &param) < 0) {
		errlog("ioctl(LKST_IOC_MASKSET_READ) error: %s\n", strerror(errno));
		exit(1);
	}
	maskset_id_prev = param.id;

	/* change buffer overflow event handler */
	for (i = 0; i < maskset.len; i++) {
		entry = &maskset.entry[i];
		if (entry->event_type == LKST_ETYPE_LKST_BUFF_OVFLOW) {
			entry->id = LKST_EVHANDLER_ID_BUFFER_SHIFT_DW;
			break;
		}
	}
	if (i == maskset.len) {
		errlog("Cannot initialize maskset for daemon!\n");
		exit(1);
	}

	/* write new maskset */
	param.id = LKST_MASKSET_ID_VOID;
	if (strlen(maskset.name) < (LKST_MASKSET_NAME_LEN - 8))
		strncat(maskset.name, "_DAEMON", 7);
	if ( ioctl(devfd, LKST_IOC_MASKSET_WRITE, &param) < 0){
		errlog("ioctl(LKST_IOC_MASKSET_WRITE) error: %s\n", strerror(errno));
		infolog("Maybe, lock events are recording, or maskset id is full.\n"); 
		exit(1);
	}

	maskset_id_daemon = param.id;

	/* change maskset */
	if (ioctl(devfd, LKST_IOC_MASKSET_SET, maskset_id_daemon) < 0){
		errlog("ioctl(LKST_IOC_MASKSET_SET) error: %s\n", strerror(errno));
		exit(1);
	}
}

int restore_maskset(int devfd)
{
	int retval = 0;

	if (maskset_id_prev != LKST_MASKSET_ID_VOID) {
		/* restore maskset */
		if (ioctl(devfd, LKST_IOC_MASKSET_SET, maskset_id_prev) < 0) {
			errlog("ioctl(LKST_IOC_MASKSET_SET) error: %s\n", strerror(errno));
			errlog("Cannot restore maskset!\n");
			retval = -1;
		}else
			maskset_id_prev = LKST_MASKSET_ID_VOID;
	}
	if (maskset_id_daemon != LKST_MASKSET_ID_VOID) {
		/* delete daemon maskset */
		if (ioctl(devfd, LKST_IOC_MASKSET_DELETE, maskset_id_daemon) < 0) {
			errlog("ioctl(LKST_IOC_MASKSET_DELETE) error: %s\n", strerror(errno));
			errlog("Cannot delete maskset for daemon!\n");
			retval = -1;
		}else
			maskset_id_daemon = LKST_MASKSET_ID_VOID;
	}
	return retval;
}

void cleanup(void)
{
	int devfd;
	if (cpu_id != -1){
		infolog("Child process exit.\n");
		return;
	}
	infolog("Owner process exit.\n");
	devfd = open_dev();
	restore_maskset(devfd);
	restore_buffer(devfd);
	close(devfd);
	delete_pid_file();
	closelog();
}


/* signal handlers */
void owner_hup_handler(int s)
{
	kill_children(SIGTERM);
	restart = 1;
}

void owner_die_handler(int s)
{
	kill_children(SIGTERM);
	restart = 0;
}

void owner_child_handler(int s)
{
	kill_children(SIGTERM);
}

void owner_snap_handler(int s)
{
	kill_children(SIGUSR1);
}

void owner_switch_handler(int s)
{
	kill_children(SIGUSR2);
}

void child_die_handler(int s)
{
	read_on = 0;
	term_flg = 1;
}

void child_switch_handler(int s)
{
	file_snum++;
	switchlog = 1;
}

void child_snap_handler(int s)
{
	read_on = !read_on;
}

void buffer_setrmod(int devfd)
{
	int ret;
	struct lkst_buffer_srmodparam sp;

	sp.cpu = cpu_id;
	sp.mode = STD;

	ret = ioctl(devfd, LKST_IOC_BUFFER_SETRMOD, &sp);
	if( ret ) {
		errlog("ioctl(LKST_IOC_BUFFER_SETRMOD) error: %s\n", strerror(errno));
		exit(1);
	}

	xtime = sp.xtime;
	tsc = sp.tsc;
	cpu_hz = sp.cpu_freq * 1000; /* convert kHz to Hz */
	endian_big = sp.endian_big;
	buf_ver = sp.buf_ver;
	strncpy(arch, sp.arch, LKST_ARCH_NAME_LEN);
}

void buffer_ring(int devfd, int ring)
{
	struct lkst_buffer_jumpparam jp;
	int retval=0;

	jp.cpu = cpu_id;
	if(ring)
		jp.dest = info[cpu_id].buf_id[0];
	else
		jp.dest = info[cpu_id].prev_buf_id;
  
        retval = ioctl(devfd, LKST_IOC_BUFFER_JUMP, &jp);

        if (retval) {
                errlog("ioctl(LKST_IOC_BUFFER_JUMP) error: %s\n", strerror(errno));
                exit(1);
        }
}

int kill_children(int sigid)
{
	int i;

	for_each_online_cpus(i, cpu_online_map) {
		if (info[i].c_pid != -1)
			kill(info[i].c_pid, sigid);
	}
	return 0;
}

int create_pid_file(void)
{
	FILE *pfd;
	
	if ( (pfd = fopen(pid_file, "r")) != NULL){
		errlog( "%s exist, lkstlogd is already running.\n", pid_file);
		infolog( "Please check if another lkstlogd is working.\n");
		return -1;
	}
	if ( (pfd = fopen(pid_file, "w")) == NULL){
		errlog( "Can't open %s : %s\n", pid_file,strerror(errno));
		infolog("Please check if you have write permission to %s.", 
			pid_file);
		pid_file[0]='\0';
		return -2;
	}
	if (!fprintf(pfd,"%d\n", getpid())) {
		errlog( "Can't write %s : %s\n", pid_file,strerror(errno));
		fclose(pfd);
		return -3;
	}
	fclose(pfd);
	return 0;
}

int delete_pid_file(void)
{
	if(pid_file[0] == '\0') return 0;
	/* pid_file remove */
	if ( unlink((char *)pid_file) != 0){
		errlog("Can't remove %s.\n", pid_file);
		return -1;
	}
	pid_file[0]='\0';
	return 0;
}

void parse_arg(int argc, char **argv)
{
	int ch;
	int i,tmp;
	char *ptr;

	for(i=0; i<LKST_CPU_MAX; i++){
		info[i].limit_size = DEFAULT_LIMIT_SIZE;
	}

	while ((ch = getopt(argc, argv, "ab:n:l:hvf:")) != EOF){
		switch((char)ch) {
		case 'a':
			read_on = 1;
			break;
		case 'b':
			init_buf_size = strtol(optarg,&ptr,0);
			if(ptr[0] == 'M' ) {
				ptr ++;
				init_buf_size *= 1024*1024;
			}else if(ptr[0] == 'K') {
				ptr ++;
				init_buf_size *= 1024;
			}
			if (ptr[0]!='\0') usage();
			if(init_buf_size > INIT_BUF_SIZE_MAX) {
				warnlog("buffer_size is too big. rounded down.\n");
				init_buf_size = INIT_BUF_SIZE_MAX;
			} else
			if(init_buf_size < INIT_BUF_SIZE_MIN) {
				warnlog("buffer_size is too small. rounded up.\n");
				init_buf_size = INIT_BUF_SIZE_MIN;
			}
			break;
		case 'n':
			init_buf_num = strtol(optarg,NULL,0);
			if(init_buf_num > INIT_BUF_NUM_MAX) {
				warnlog("num_of_buffers is too large. rounded down.\n");
				init_buf_num = INIT_BUF_NUM_MAX;
			} else
			if(init_buf_num < INIT_BUF_NUM_MIN) {
				warnlog("num_of_buffers is too small. rounded up.\n");
				init_buf_num = INIT_BUF_NUM_MIN;
			}
			break;
		case 'l':
			tmp = strtol(optarg,&ptr,0);
			if(ptr[0] == 'M' ) {
				ptr ++;
				tmp *= 1024*1024;
			}else if(ptr[0] == 'K') {
				ptr ++;
				tmp *= 1024;
			}
			if (ptr[0]!='\0') usage();
			if ( tmp < LOG_HEADER_SIZE ) {
				errlog("Too small file size.\n");
				exit(1);
			}
			for(i=0; i<LKST_CPU_MAX; i++)
				info[i].limit_size = tmp;
			break;
		case 'v':
			printf("%s %s\n%s\n", NAME, VERSION, COPYRIGHT);
			exit(0);
			break;
		case 'f':
			strncpy(log_file, optarg, 255);
			break;
		case 'h':
		case '?':
		default:
			usage();
		}
	}
	if (buffer_read_size(init_buf_size) > 
	    info[0].limit_size - LOG_HEADER_SIZE) {
		errlog("The size of log file must be bigger than 1.5 times of the size of buffer\n");
		exit(1);
	}
}

void usage(void)
{
	printf("Usage: lkstlogd [-a] [-l file_size_limit[K|M]]\n");
	printf("\t\t[-b buffer_size[K|M]] \n");
	printf("\t\t[-n num_of_buffers] \n");
	printf("\t\t[-f log_file_name] \n");
	exit(0);
}

/* signal(2) but reuse handler.(not oneshot) */
void (*signal_reuse(int signum, void (*handler)(int)))(int) 
{
	struct sigaction sa, psa;

	sa.sa_handler = handler;
	if ( sigemptyset(&sa.sa_mask) < 0 ||
	     sigaddset(&sa.sa_mask, signum) < 0 )
		return SIG_ERR;
	sa.sa_flags = 0;
	if( sigaction(signum, &sa, &psa) < 0) 
		return SIG_ERR;
	else 
		return psa.sa_handler;
}

/*write a log message to syslog*/
void syslog_info(const char *format, ...)
{
	va_list ap;
	va_start(ap,format);
	vsyslog(LOG_INFO, format, ap);
}
void syslog_warning(const char *format, ...)
{
	va_list ap;
	va_start(ap,format);
	vsyslog(LOG_WARNING, format, ap);
}
void syslog_error(const char *format, ...)
{
	va_list ap;
	va_start(ap,format);
	vsyslog(LOG_ERR, format, ap);
}
/* write a log message to stderr*/
void stderr_info(const char *format, ...)
{
	va_list ap;
	va_start(ap,format);
	fprintf(stderr, "lkstlogd info: ");
	vfprintf(stderr, format, ap);
}
void stderr_warning(const char *format, ...)
{
	va_list ap;
	va_start(ap,format);
	fprintf(stderr, "lkstlogd warning: ");
	vfprintf(stderr, format, ap);
}
void stderr_error(const char *format, ...)
{
	va_list ap;
	va_start(ap,format);
	fprintf(stderr, "lkstlogd error: ");
	vfprintf(stderr, format, ap);
}

