From 8b4e95781c1ced38ab766be3659316c964e0de21 Mon Sep 17 00:00:00 2001 From: Frederic Marchal Date: Thu, 19 Mar 2015 20:56:30 +0100 Subject: [PATCH] Get rid of the dependency to an external tail command The real time report relied on an external "tail" command to get the last lines of the input log file. That dependency has been removed. Moreover, if several log files were passed, the real time report would always read the first one. Now, sarg chooses the most recently modified log file. The input log file can be in any supported format. It doesn't have to be a standard squid log format. --- include/readlog.h | 21 +++ log.c | 9 +- readlog.c | 163 +++++++++++------ readlog_squid.c | 10 +- realtime.c | 442 +++++++++++++++++++--------------------------- 5 files changed, 320 insertions(+), 325 deletions(-) diff --git a/include/readlog.h b/include/readlog.h index 425a5c9..a481362 100644 --- a/include/readlog.h +++ b/include/readlog.h @@ -41,6 +41,8 @@ struct ReadLogStruct long long int DataSize; //! HTTP code returned to the user for the entry. char *HttpCode; + //! HTTP method or NULL if the information is not stored in the log. + char *HttpMethod; }; /*! @@ -56,4 +58,23 @@ struct ReadLogProcessStruct enum ReadLogReturnCodeEnum (*ReadEntry)(char *Line,struct ReadLogStruct *Entry); }; +/*! + * \brief Persistant data to parse a log line. + */ +struct LogLineStruct +{ + const struct ReadLogProcessStruct *current_format; + int current_format_idx; + int successive_errors; + int total_errors; + const char *file_name; +}; + +//! Opaque object used to parse a log line. +typedef struct LogLineStruct *LogLineObject; + +void LogLine_Init(struct LogLineStruct *log_line); +void LogLine_File(struct LogLineStruct *log_line,const char *file_name); +enum ReadLogReturnCodeEnum LogLine_Parse(struct LogLineStruct *log_line,struct ReadLogStruct *log_entry,char *linebuf); + #endif //READLOG_HEADER diff --git a/log.c b/log.c index 82ee999..57cbc7e 100644 --- a/log.c +++ b/log.c @@ -458,11 +458,6 @@ int main(int argc,char *argv[]) if(outdir[0] == '\0') strcpy(outdir,OutputDir); if(outdir[0] != '\0') strcat(outdir,"/"); - if(realt) { - realtime(); - exit(EXIT_SUCCESS); - } - if(IndexTree == INDEX_TREE_FILE) strcpy(ImageFile,"../images"); else @@ -483,6 +478,10 @@ int main(int argc,char *argv[]) } } + if(realt) { + realtime(); + exit(EXIT_SUCCESS); + } if(split) { const char *file; diff --git a/readlog.c b/readlog.c index de6199d..514ad07 100644 --- a/readlog.c +++ b/readlog.c @@ -135,6 +135,101 @@ static int LatestDate=-1; //! The latest date in time format. static struct tm LatestDateTime; +/*! + * Initialize the memory structure needed by LogLine_Parse() to parse + * a log line. + * + * \param log_line The structure to initialize. + */ +void LogLine_Init(struct LogLineStruct *log_line) +{ + log_line->current_format=NULL; + log_line->current_format_idx=-1; + log_line->file_name=""; + log_line->successive_errors=0; + log_line->total_errors=0; +} + +/*! + * Set the name of the log file being parsed. + * + * \param log_line Data structure to parse the log line. + * \param file_name The name of the log file being read. + */ +void LogLine_File(struct LogLineStruct *log_line,const char *file_name) +{ + log_line->file_name=file_name; +} + +/*! + * Parse the next line from a log file. + * + * \param log_line A buffer to store the data about the current parsing. + * \param log_entry The variable to store the parsed data. + * \param linebuf The text line read from the log file. + * + * \return + */ +enum ReadLogReturnCodeEnum LogLine_Parse(struct LogLineStruct *log_line,struct ReadLogStruct *log_entry,char *linebuf) +{ + enum ReadLogReturnCodeEnum log_entry_status=RLRC_Unknown; + int x; + + if (log_line->current_format) + { + memset(log_entry,0,sizeof(*log_entry)); + log_entry_status=log_line->current_format->ReadEntry(linebuf,log_entry); + } + + // find out what line format to use + if (log_entry_status==RLRC_Unknown) + { + for (x=0 ; x<(int)(sizeof(LogFormats)/sizeof(*LogFormats)) ; x++) + { + if (LogFormats[x]==log_line->current_format) continue; + memset(log_entry,0,sizeof(*log_entry)); + log_entry_status=LogFormats[x]->ReadEntry(linebuf,log_entry); + if (log_entry_status!=RLRC_Unknown) + { + log_line->current_format=LogFormats[x]; + log_line->current_format_idx=x; + if (debugz>=LogLevel_Process) + { + /* TRANSLATORS: The argument is the log format name as translated by you. */ + debuga(_("Log format identified as \"%s\" for %s\n"),_(log_line->current_format->Name),log_line->file_name); + } + break; + } + } + if (x>=(int)(sizeof(LogFormats)/sizeof(*LogFormats))) + { + if (++log_line->successive_errors>NumLogSuccessiveErrors) { + debuga(ngettext("%d consecutive error found in the input log file %s\n", + "%d consecutive errors found in the input log file %s\n",log_line->successive_errors),log_line->successive_errors,log_line->file_name); + exit(EXIT_FAILURE); + } + if (NumLogTotalErrors>=0 && ++log_line->total_errors>NumLogTotalErrors) { + debuga(ngettext("%d error found in the input log file (last in %s)\n", + "%d errors found in the input log file (last in %s)\n",log_line->total_errors),log_line->total_errors,log_line->file_name); + exit(EXIT_FAILURE); + } + debuga(_("The following line read from %s could not be parsed and is ignored\n%s\n"),log_line->file_name,linebuf); + } + else + log_line->successive_errors=0; + } + + if (log_line->current_format_idx<0 || log_line->current_format==NULL) { + debuga(_("Sarg failed to determine the format of the input log file %s\n"),log_line->file_name); + exit(EXIT_FAILURE); + } + if (log_entry_status==RLRC_InternalError) { + debuga(_("Internal error encountered while processing %s\nSee previous message to know the reason for that error.\n"),log_line->file_name); + exit(EXIT_FAILURE); + } + return(log_entry_status); +} + /*! Read a single log file. @@ -153,15 +248,12 @@ static void ReadOneLogFile(struct ReadLogDataStruct *Filter,const char *arq) char smartfilter[MAXLEN]; const char *url; const char *user; - int current_format_idx; int OutputNonZero = REPORT_EVERY_X_LINES ; int idata=0; int x; int hmr; int nopen; int maxopenfiles=MAX_OPEN_USER_FILES; - int successive_errors=0; - int total_errors=0; unsigned long int recs1=0UL; unsigned long int recs2=0UL; FILE *fp_in=NULL; @@ -177,10 +269,10 @@ static void ReadOneLogFile(struct ReadLogDataStruct *Filter,const char *arq) struct userfilestruct *ufile; struct userfilestruct *ufile1; struct ReadLogStruct log_entry; - const struct ReadLogProcessStruct *current_format=NULL; + struct LogLineStruct log_line; - current_format=NULL; - current_format_idx=-1; + LogLine_Init(&log_line); + LogLine_File(&log_line,arq); for (x=0 ; xNewFile) LogFormats[x]->NewFile(arq); @@ -303,58 +395,19 @@ static void ReadOneLogFile(struct ReadLogDataStruct *Filter,const char *arq) printf("BUF=%s\n",linebuf); // process the line - log_entry_status=RLRC_Unknown; - memset(&log_entry,0,sizeof(log_entry)); - if (current_format) { - log_entry_status=current_format->ReadEntry(linebuf,&log_entry); - } - - // find out what line format to use - if (log_entry_status==RLRC_Unknown) { - for (x=0 ; x<(int)(sizeof(LogFormats)/sizeof(*LogFormats)) ; x++) { - if (LogFormats[x]==current_format) continue; - memset(&log_entry,0,sizeof(log_entry)); - log_entry_status=LogFormats[x]->ReadEntry(linebuf,&log_entry); - if (log_entry_status!=RLRC_Unknown) break; - } - if (x>=(int)(sizeof(LogFormats)/sizeof(*LogFormats))) { - if (++successive_errors>NumLogSuccessiveErrors) { - debuga(ngettext("%d consecutive error found in the input log file %s\n", - "%d consecutive errors found in the input log file %s\n",successive_errors),successive_errors,arq); - exit(EXIT_FAILURE); - } - if (NumLogTotalErrors>=0 && ++total_errors>NumLogTotalErrors) { - debuga(ngettext("%d error found in the input log file (last in %s)\n", - "%d errors found in the input log file (last in %s)\n",total_errors),total_errors,arq); - exit(EXIT_FAILURE); - } - debuga(_("The following line read from %s could not be parsed and is ignored\n%s\n"),arq,linebuf); - excluded_count[ER_UnknownFormat]++; - continue; - } - current_format=LogFormats[x]; - current_format_idx=x; - if (debugz>=LogLevel_Process) { - /* TRANSLATORS: The argument is the log format name as translated by you. */ - debuga(_("Log format identified as \"%s\" for %s\n"),_(current_format->Name),arq); - } - successive_errors=0; + log_entry_status=LogLine_Parse(&log_line,&log_entry,linebuf); + if (log_entry_status==RLRC_Unknown) + { + excluded_count[ER_UnknownFormat]++; + continue; } if (log_entry_status==RLRC_Ignore) { excluded_count[ER_FormatData]++; continue; } - if (current_format_idx<0 || current_format==NULL) { - debuga(_("Sarg failed to determine the format of the input log file %s\n"),arq); - exit(EXIT_FAILURE); - } - if (log_entry_status==RLRC_InternalError) { - debuga(_("Internal error encountered while processing %s\nSee previous message to know the reason for that error.\n"),arq); - exit(EXIT_FAILURE); - } - format_count[current_format_idx]++; + format_count[log_line.current_format_idx]++; - if (!fp_log && ParsedOutputLog[0] && current_format!=&ReadSargLog) { + if (!fp_log && ParsedOutputLog[0] && log_line.current_format!=&ReadSargLog) { if(access(ParsedOutputLog,R_OK) != 0) { my_mkdir(ParsedOutputLog); } @@ -447,7 +500,7 @@ static void ReadOneLogFile(struct ReadLogDataStruct *Filter,const char *arq) for (str=log_entry.HttpCode ; *str ; str++) if (*str=='\t') *str=' '; - if (current_format!=&ReadSargLog) { + if (log_line.current_format!=&ReadSargLog) { /* The full URL is not saved in sarg log. There is no point in testing the URL to detect a downloaded file. @@ -640,7 +693,7 @@ static void ReadOneLogFile(struct ReadLogDataStruct *Filter,const char *arq) } records_kept++; - if (fp_log && current_format!=&ReadSargLog) { + if (fp_log && log_line.current_format!=&ReadSargLog) { fprintf(fp_log, "%s\t%s\t%s\t%s\t%s\t%"PRIu64"\t%s\t%ld\t%s\n",dia,hora, log_entry.User,log_entry.Ip,url,(uint64_t)log_entry.DataSize, log_entry.HttpCode,log_entry.ElapsedTime,smartfilter); @@ -652,7 +705,7 @@ static void ReadOneLogFile(struct ReadLogDataStruct *Filter,const char *arq) authfail_write(&log_entry); if (download_flag) download_write(&log_entry,download_url); - if (current_format!=&ReadSargLog) { + if (log_line.current_format!=&ReadSargLog) { if (period.start.tm_year==0 || idata0){ mindate=idata; memcpy(&period.start,&log_entry.EntryTime,sizeof(log_entry.EntryTime)); diff --git a/readlog_squid.c b/readlog_squid.c index 4436321..256a051 100644 --- a/readlog_squid.c +++ b/readlog_squid.c @@ -51,6 +51,7 @@ static enum ReadLogReturnCodeEnum Squid_ReadEntry(char *Line,struct ReadLogStruc time_t log_time; int IpLen; int HttpCodeLen; + int HttpMethodLen; int UrlLen; int UserLen; struct tm *tt; @@ -109,10 +110,10 @@ static enum ReadLogReturnCodeEnum Squid_ReadEntry(char *Line,struct ReadLogStruc while (isdigit(*Line)) Entry->DataSize=Entry->DataSize*10+(*Line++-'0'); if (*Line!=' ' || Begin==Line) return(RLRC_Unknown); - // skip the HTTP function - Begin=++Line; - while (*Line && *Line!=' ') Line++; - if (*Line!=' '|| Line==Begin) return(RLRC_Unknown); + // get the HTTP method + Entry->HttpMethod=++Line; + for (HttpMethodLen=0 ; *Line && *Line!=' ' ; HttpMethodLen++) Line++; + if (*Line!=' '|| HttpMethodLen==0) return(RLRC_Unknown); // the url Entry->Url=++Line; @@ -135,6 +136,7 @@ static enum ReadLogReturnCodeEnum Squid_ReadEntry(char *Line,struct ReadLogStruc // it is safe to alter the line buffer now that we are returning a valid entry Ip[IpLen]='\0'; Entry->HttpCode[HttpCodeLen]='\0'; + Entry->HttpMethod[HttpMethodLen]='\0'; Entry->Url[UrlLen]='\0'; User[UserLen]='\0'; diff --git a/realtime.c b/realtime.c index aa7f956..4e8560e 100755 --- a/realtime.c +++ b/realtime.c @@ -27,304 +27,224 @@ #include "include/conf.h" #include "include/defs.h" #include "include/filelist.h" +#include "include/readlog.h" -extern FileListObject AccessLog; - -static int getdata(char*, FILE*); -static void datashow(const char *); -static void getlog(void); -static void header(void); +//! Maximum length of the scheme plus host name from the url. +#define MAX_URL_HOST_LEN 260 -void realtime(void) +/*! +\brief Data read from an input log file. +*/ +struct RealtimeReadLogStruct { - getlog(); -} + //! The time corresponding to the entry. + struct tm EntryTime; + //! The IP address connecting to internet. + char Ip[48]; + //! The user's name. + char User[MAX_USER_LEN]; + /*! + The URL of the visited site. + + The pointer may be NULL if the URL doesn't exists in the log file. + */ + char Url[MAX_URL_HOST_LEN]; + //! HTTP method or NULL if the information is not stored in the log. + char HttpMethod[32]; +}; + +extern FileListObject AccessLog; -static void getlog(void) +static bool GetLatestModified(char *file_name,int file_name_size) { - FILE *tmp, *fp; - char template1[255]="/var/tmp/sargtpl1.XXXXXX"; - char template2[255]="/var/tmp/sargtpl2.XXXXXX"; - char cmd[2048]; - char *buf; - const char *file; - int fd1,fd2; - int cstatus; - longline line; FileListIterator FIter; - - init_usertab(UserTabFile); - -#ifdef HAVE_MKSTEMP - fd2 = mkstemp(template2); - if (fd2 == -1) { - debuga(_("Cannot create a temporary file name to produce the report: %s\n"),strerror(errno)); - exit(EXIT_FAILURE); - } - fd1 = mkstemp(template1); -#else - buf = mktemp(template2); - if (buf[0]=='\0') { - debuga(_("Cannot create a temporary file name to produce the report: %s\n"),strerror(errno)); - exit(EXIT_FAILURE); - } - fd2 = -1; - fd1 = open(mktemp(template1),O_RDWR); -#endif - - if((fd1 == -1 ) || ((tmp = fdopen (fd1, "w+" )) == NULL) ) { /* failure, bail out */ - debuga(_("(realtime) mkstemp error - %s\n"),strerror(errno)); - exit(EXIT_FAILURE); - } - - if ((line=longline_create())==NULL) { - debuga(_("Not enough memory to read the log file\n")); - exit(EXIT_FAILURE); - } + const char *file; + bool found=false; + struct stat st; + time_t latest; FIter=FileListIter_Open(AccessLog); - file=FileListIter_Next(FIter); - if (file==NULL) { - debuga(_("No log file to read the last %d lines from\n"),realtime_access_log_lines); - exit(EXIT_FAILURE); - } - if (snprintf(cmd,sizeof(cmd),"tail -%d \"%s\"",realtime_access_log_lines,file)>=sizeof(cmd)) { - debuga(_("Input log file name too long: %s\n"),file); - exit(EXIT_FAILURE); - } - fp = popen(cmd, "r"); - if (!fp) { - debuga(_("Failed to get %d trailing lines from %s: %s\n"),realtime_access_log_lines,file,strerror(errno)); - debuga(_("tail command: %s\n"),cmd); - exit(EXIT_FAILURE); + while ((file=FileListIter_Next(FIter))!=NULL) + { + if (stat(file,&st)==-1) { + debuga(_("Cannot stat \"%s\": %s"),file,strerror(errno)); + } + if (!found) + { + found=true; + latest=st.st_mtime; + safe_strcpy(file_name,file,file_name_size); + } + else if (st.st_mtime>latest) + { + latest=st.st_mtime; + safe_strcpy(file_name,file,file_name_size); + } } FileListIter_Close(FIter); - while((buf=longline_read(fp,line)) != NULL ) - if (getdata(buf,tmp)<0) { - debuga(_("Maybe a broken record or garbage was returned by %s\n"),cmd); - exit(EXIT_FAILURE); - } - pclose(fp); - fclose(tmp); - longline_destroy(&line); + return(found); +} - if (fd2!=-1) close(fd2);//not safe at all but good enough for now. - if (snprintf(cmd,sizeof(cmd),"sort -t \"\t\" -r -n -k 1,1 -o \"%s\" \"%s\"",template2,template1)>=sizeof(cmd)) { - debuga(_("Sort command too long when sorting file \"%s\" to \"%s\"\n"),template1,template2); - exit(EXIT_FAILURE); - } - cstatus=system(cmd); - if (!WIFEXITED(cstatus) || WEXITSTATUS(cstatus)) { - debuga(_("sort command return status %d\n"),WEXITSTATUS(cstatus)); - debuga(_("sort command: %s\n"),cmd); - exit(EXIT_FAILURE); - } - if (!KeepTempLog && unlink(template1)) { - debuga(_("Cannot delete \"%s\": %s\n"),template1,strerror(errno)); - exit(EXIT_FAILURE); +/*! + * \brief Store a log entry. + * + * \param Dest A pointer to the list entry where to store the entry. + * \param Entry The entry to store. + */ +static void StoreLogEntry(struct RealtimeReadLogStruct *Dest,struct ReadLogStruct *Entry) +{ + memcpy(&Dest->EntryTime,&Entry->EntryTime,sizeof(Dest->EntryTime)); + safe_strcpy(Dest->Ip,Entry->Ip,sizeof(Dest->Ip)); + if (Entry->Url) + { + int i; + const char *url=Entry->Url; + + // skip the scheme + for (i=0 ; i<8 && url[i] && (isalnum(url[i]) || url[i]=='+' || url[i]=='-' || url[i]=='.') ; i++); + if (url[i]==':' && url[i+1]=='/' && url[i+2]=='/') + { + url+=i+3; + for (i=0 ; url[i] && url[i]!='/' ; i++); + } + if (i>=sizeof(Dest->Url)) i=sizeof(Dest->Url)-1; + strncpy(Dest->Url,url,i); + Dest->Url[i]='\0'; } - datashow(template2); + safe_strcpy(Dest->User,Entry->User,sizeof(Dest->User)); + safe_strcpy(Dest->HttpMethod,Entry->HttpMethod,sizeof(Dest->HttpMethod)); } -static int getdata(char *rec, FILE *ftmp) +static void header(void) { - int dat; - char typ[128]; - char warea[MAXLEN]; + puts("\n"); + puts("\n"); + puts("\n"); + if(realtime_refresh) + printf(" \n",realtime_refresh,CharSet); + else + printf(" \n",CharSet); + css(stdout); + puts("\n"); + printf("\n",FontFace,TitleFontSize,BgColor,BgImage); + puts("
\n"); + printf("\n",_("Realtime")); + printf("\n",_("Auto refresh"),realtime_refresh); + printf("\n",_("DATE/TIME"),_("IP/NAME"),_("USERID"),_("TYPE"),_("ACCESSED SITE")); +} + +static void datashow(struct RealtimeReadLogStruct *List,int Index,int Size) +{ + char tbuf[128]; char user[MAX_USER_LEN]; - char ip[45]; - char *url; - struct getwordstruct gwarea; + char name[MAX_USER_LEN]; + int i; + struct RealtimeReadLogStruct *entry; - getword_start(&gwarea,rec); - if (getword_atoi(&dat,&gwarea,'.')<0) { - debuga(_("The time stamp at column 1 is too long\n")); - return(-1); - } - if (getword_skip(10,&gwarea,' ')<0) { - debuga(_("The time stamp decimal part at column 1 is too long\n")); - return(-1); - } - if (getword(warea,sizeof(warea),&gwarea,' ')<0) { - debuga(_("The connection duration at column 2 is too long\n")); - return(-1); - } - while(strcmp(warea,"") == 0 && gwarea.current[0] != '\0') - if (getword(warea,sizeof(warea),&gwarea,' ')<0) { - return(-1); - } - if (getword(ip,sizeof(ip),&gwarea,' ')<0) { - debuga(_("The IP address at column 3 is too long\n")); - return(-1); - } - if (getword_skip(MAXLEN,&gwarea,' ')<0) { - debuga(_("The status at column 4 is too long\n")); - return(-1); - } - if (getword_skip(MAXLEN,&gwarea,' ')<0) { - debuga(_("The size at column 5 is too long\n")); - return(-1); - } - if (getword(typ,sizeof(typ),&gwarea,' ')<0) { - debuga(_("The action at column 6 is too long\n")); - return(-1); - } - if(strncmp(typ,"CONNECT",7) == 0) { - if (getword_ptr(rec,&url,&gwarea,' ')<0) { - debuga(_("The URL at column 7 is too long\n")); - return(-1); - } - if (getword(user,sizeof(user),&gwarea,' ')<0) { - debuga(_("The user ID at column 8 is too long\n")); - return(-1); - } - }else { - if (getword_skip(MAXLEN,&gwarea,'/')<0) { - debuga(_("The URL at column 7 is too long\n")); - return(-1); - } - if (getword_skip(MAXLEN,&gwarea,'/')<0) { - debuga(_("The URL at column 7 is too long\n")); - return(-1); - } - if (getword_ptr(rec,&url,&gwarea,'/')<0) { - debuga(_("The URL at column 7 is too long\n")); - return(-1); - } - if (getword_skip(MAXLEN,&gwarea,' ')<0) { - debuga(_("The data at column 8 is too long\n")); - return(-1); - } - if (getword(user,sizeof(user),&gwarea,' ')<0) { - debuga(_("The user at column 9 is too long\n")); - return(-1); - } - } + header(); + for (i=0 ; iIp); + else + strcpy(user,entry->User); + if(Ip2Name) + ip2name(user,sizeof(user)); + user_find(name, sizeof(name), user); - if(strncmp(user,"-",1) == 0 && RealtimeUnauthRec==REALTIME_UNAUTH_REC_IGNORE) - return(0); + if (df=='u') + strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M", &entry->EntryTime); + else if (df=='e') + strftime(tbuf, sizeof(tbuf), "%d-%m-%Y %H:%M", &entry->EntryTime); - fprintf(ftmp,"%d\t%s\t%s\t%s\t%s\n",dat,ip,user,url,typ); - return(0); + printf("\n", + tbuf,entry->Ip,name,entry->HttpMethod,entry->Url,entry->Url); + } + + puts("
SARG %s
%s: %d s
%s%s%s%s%s
%s%s%s%s%s
\n
\n\n\n"); + fflush(NULL); } -static void datashow(const char *tmp) +void realtime(void) { - FILE *fin; - time_t tt; - struct tm *t; - char tbuf[128]; - int dat; + FILE *fp; + char file_name[2048]; char *buf; - char *url; - char *ourl=NULL; - char ouser[MAX_USER_LEN]=""; - char typ[128]; - char user[MAX_USER_LEN]; - char u2[MAX_USER_LEN]; - char ip[45]; - int url_len; - int ourl_size=0; - struct getwordstruct gwarea; longline line; + struct ReadLogStruct log_entry; + enum ReadLogReturnCodeEnum log_entry_status; + struct LogLineStruct log_line; + struct RealtimeReadLogStruct *StoredLogEntries; + int StoreIndex=0; + int StoreSize=0; + int NextIndex=1; - if((fin=fopen(tmp,"r"))==NULL) { - debuga(_("(realtime) open error %s - %s\n"),tmp,strerror(errno)); - exit(EXIT_FAILURE); - } - - header(); + init_usertab(UserTabFile); + LogLine_Init(&log_line); if ((line=longline_create())==NULL) { debuga(_("Not enough memory to read the log file\n")); exit(EXIT_FAILURE); } - while((buf=longline_read(fin,line))!=NULL) { - fixendofline(buf); - getword_start(&gwarea,buf); - if (getword_atoi(&dat,&gwarea,'\t')<0) { - debuga(_("Invalid time column in file %s\n"),tmp); - exit(EXIT_FAILURE); - } - if (getword(ip,sizeof(ip),&gwarea,'\t')<0) { - debuga(_("Invalid IP address in file %s\n"),tmp); - exit(EXIT_FAILURE); - } - if (getword(user,sizeof(user),&gwarea,'\t')<0) { - debuga(_("Invalid user name in file %s\n"),tmp); - exit(EXIT_FAILURE); - } - if (strlen(user) < 1) continue; - if (getword_ptr(buf,&url,&gwarea,'\t')<0) { - debuga(_("Invalid URL in file %s\n"),tmp); - exit(EXIT_FAILURE); + /* + * Store one more entry to prepare the memory structure in place and reject it if + * it is about the same user and url as the last stored one. + */ + StoredLogEntries=calloc(realtime_access_log_lines+1,sizeof(struct RealtimeReadLogStruct)); + if (!StoredLogEntries) + { + debuga(_("Not enough memory to store %d records"),realtime_access_log_lines); + exit(EXIT_FAILURE); + } + /* + * Clear the url and user strings so that strcmp on the user and url are not + * satisfied and the first entry can be stored. + */ + memset(StoredLogEntries,0,sizeof(struct RealtimeReadLogStruct)); + + if (!GetLatestModified(file_name,sizeof(file_name))) + { + debuga(_("No log file to read the last %d lines from\n"),realtime_access_log_lines); + exit(EXIT_FAILURE); + } + fp = fopen(file_name, "r"); + if (!fp) { + debuga(_("Failed to open the last modified log file \"%s\": %s\n"),file_name,strerror(errno)); + exit(EXIT_FAILURE); + } + while((buf=longline_read(fp,line)) != NULL ) + { + log_entry_status=LogLine_Parse(&log_line,&log_entry,buf); + if (log_entry_status==RLRC_Unknown) + { + continue; } - if (getword(typ,sizeof(typ),&gwarea,'\t')<0) { - debuga(_("Invalid access type in file %s\n"),tmp); - exit(EXIT_FAILURE); + if (log_entry_status==RLRC_Ignore) + { + continue; } - if(strstr(RealtimeTypes,typ) == 0) + if (log_entry.HttpMethod && strstr(RealtimeTypes,log_entry.HttpMethod)==0) continue; - - if(strcmp(ouser,user) == 0 && ourl && strcmp(ourl,url) == 0) + if (RealtimeUnauthRec==REALTIME_UNAUTH_REC_IGNORE && log_entry.User[0]=='-' && log_entry.User[1]=='\0') + continue; + StoreLogEntry(StoredLogEntries+NextIndex,&log_entry); + if (strcmp(StoredLogEntries[StoreIndex].User,StoredLogEntries[NextIndex].User)==0 && strcmp(StoredLogEntries[StoreIndex].Url,StoredLogEntries[NextIndex].Url)==0) continue; - if(UserIp) - strcpy(user,ip); - strcpy(u2,user); - if(Ip2Name) - ip2name(u2,sizeof(u2)); - user_find(name, sizeof(name), u2); - - tt=(time_t)dat; - t=localtime(&tt); - if (df=='u') - strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M", t); - else if (df=='e') - strftime(tbuf, sizeof(tbuf), "%d-%m-%Y %H:%M", t); - - printf("%s%s%s%s%s\n",tbuf,ip,name,typ,url,url); - strcpy(ouser,user); - - url_len=strlen(url); - if (!ourl || url_len>=ourl_size) { - ourl_size=url_len+1; - ourl=realloc(ourl,ourl_size); - if (!ourl) { - debuga(_("Not enough memory to store the url\n")); - exit(EXIT_FAILURE); - } - } - strcpy(ourl,url); + StoreIndex=NextIndex; + NextIndex++; + if (NextIndex>StoreSize) StoreSize=NextIndex; + if (NextIndex>realtime_access_log_lines) NextIndex=0; } + fclose(fp); longline_destroy(&line); - if (ourl) free(ourl); - - puts("\n\n\n\n"); - fclose(fin); - if (!KeepTempLog && unlink(tmp)) { - debuga(_("Cannot delete \"%s\": %s\n"),tmp,strerror(errno)); - exit(EXIT_FAILURE); - } - fflush(NULL); -} -static void header(void) -{ - puts("\n"); - puts("\n"); - puts("\n"); - if(realtime_refresh) - printf(" \n",realtime_refresh,CharSet); - else - printf(" \n",CharSet); - css(stdout); - puts("\n"); - printf("\n",FontFace,TitleFontSize,BgColor,BgImage); - puts("
\n"); - printf("\n",_("Realtime")); - printf("\n",_("Auto refresh"),realtime_refresh); - printf("\n",_("DATE/TIME"),_("IP/NAME"),_("USERID"),_("TYPE"),_("ACCESSED SITE")); + datashow(StoredLogEntries,StoreIndex,StoreSize); + free(StoredLogEntries); } -- 2.47.2
SARG %s
%s: %d s
%s%s%s%s%s