#include <assert.h>
#include <errno.h>
#include <fcntl.h>
#include <poll.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>
#include <sys/sched.h>
#include <sys/stat.h>
#include <sys/uio.h>
#include "clog.h"
#define BUFFER_SIZE 16384
static void init_log(const char *lname, size_t size) __dead2;
static void read_log(const char *lname, int optf) __dead2;
static void usage(void) __dead2;
static const char *pname;
int
main(int argc, char **argv)
{
int ch;
int init = 0;
int size = 0;
int optf = 0;
pname = argv[0];
while ((ch = getopt(argc, argv, "fis:")) != -1)
switch(ch) {
case 'i':
init = 1;
break;
case 's':
size = atol(optarg);
if (size==0) usage();
break;
case 'f':
optf = 1;
}
if ((size>0)&&(init==0)) {
fprintf(stderr,"%s: WARNING: -s argument ignored without -i.\n",pname);
size = 0;
}
if (argv[optind]==NULL) {
fprintf(stderr,"%s: ERROR: log_file argument must be specified.\n",pname);
usage();
}
if ((init==1)&&(size==0)) {
fprintf(stderr,"%s: ERROR: -i argument requires -s.\n",pname);
usage();
}
if ((init==1)&&(optf==1)) {
fprintf(stderr,"%s: ERROR: flags -f and -i are incompatible.\n",pname);
usage();
}
if (init==1) init_log(argv[optind],size);
read_log(argv[optind],optf);
return 0;
}
static void
usage(void)
{
fprintf(stderr,"usage: %s [-i -s log_size] [ -f ] log_file\n",pname);
exit(1);
}
static void
read_log(const char *lname, int optf)
{
int fd;
struct stat sb;
struct clog_footer *pcf;
char *pbuffer;
struct iovec iov[2];
int iovcnt = 0;
uint32_t start = 0;
uint32_t next;
struct pollfd pfd;
pfd.fd = -1;
fd = open(lname,O_RDONLY);
if (fd==-1) {
fprintf(stderr,"%s: ERROR: could not open %s (%s)\n",pname,lname,strerror(errno));
exit(11);
}
if (fstat(fd,&sb)==-1) {
fprintf(stderr,"%s: ERROR: could not stat %s (%s)\n",pname,lname,strerror(errno));
exit(13);
}
pbuffer = mmap(NULL,sb.st_size,PROT_READ,MAP_SHARED,fd,0);
if (pbuffer==NULL) {
fprintf(stderr,"%s: ERROR: could not mmap %s body (%s)\n",pname,lname,strerror(errno));
exit(14);
}
pcf = (struct clog_footer*)(pbuffer + sb.st_size - sizeof(struct clog_footer));
if (pcf->cf_wrap==1) start = pcf->cf_next + 1;
while(1) {
while(pcf->cf_lock==1) sched_yield();
next = pcf->cf_next;
iovcnt = 0;
if (start>next) {
iov[iovcnt].iov_base = pbuffer + start;
iov[iovcnt++].iov_len = pcf->cf_max - start;
start = 0;
}
iov[iovcnt].iov_base = pbuffer + start;
iov[iovcnt++].iov_len = next - start;
if (writev(1,iov,iovcnt)==-1) {
fprintf(stderr,"%s: ERROR: could not write output (%s)\n",pname,strerror(errno));
exit(15);
}
start = next;
if (optf==0) break;
if (poll(&pfd,1,50)==-1) {
fprintf(stderr,"%s: ERROR: could not poll (%s)\n",pname,strerror(errno));
exit(16);
}
}
munmap(pbuffer,sb.st_size);
close(fd);
exit(0);
}
static void
init_log(const char *lname, size_t size)
{
int fd;
size_t fill = size;
char buffer[BUFFER_SIZE];
struct clog_footer cf;
memcpy(&cf.cf_magic,MAGIC_CONST,4);
cf.cf_max = size - sizeof(struct clog_footer);
memset(buffer,0,BUFFER_SIZE);
fd = open(lname,O_RDWR|O_CREAT,0666);
if (fd==-1) {
fprintf(stderr,"%s: ERROR: could not open %s (%s)\n",pname,lname,strerror(errno));
exit(2);
}
if (ftruncate(fd,(off_t)0)==-1) {
fprintf(stderr,"%s: ERROR: could not truncate %s (%s)\n",pname,lname,strerror(errno));
exit(3);
}
while(fill>BUFFER_SIZE) {
if (write(fd,buffer,BUFFER_SIZE)==-1){
fprintf(stderr,"%s: ERROR: could not write %s (%s)\n",pname,lname,strerror(errno));
exit(4);
}
fill -= BUFFER_SIZE;
}
assert(fill<=BUFFER_SIZE);
if (fill>0) {
if (write(fd,buffer,fill)==-1) {
fprintf(stderr,"%s: ERROR: could not write %s (%s)\n",pname,lname,strerror(errno));
exit(5);
}
}
if (lseek(fd,-(off_t)(sizeof(struct clog_footer)),SEEK_END)==-1) {
fprintf(stderr,"%s: ERROR: could not seek in %s (%s)\n",pname,lname,strerror(errno));
exit(6);
}
if (write(fd,&cf,sizeof(cf))==-1) {
fprintf(stderr,"%s: ERROR: could not write magic in %s (%s)\n",pname,lname,strerror(errno));
exit(7);
}
close(fd);
exit(0);
}