]> git.ipfire.org Git - thirdparty/sarg.git/blobdiff - realtime.c
Add support to decompress xz files
[thirdparty/sarg.git] / realtime.c
old mode 100755 (executable)
new mode 100644 (file)
index 7652186..cf90e47
@@ -1,6 +1,6 @@
 /*
  * SARG Squid Analysis Report Generator      http://sarg.sourceforge.net
- *                                                            1998, 2011
+ *                                                            1998, 2015
  *
  * SARG donations:
  *      please look at http://sarg.sourceforge.net/donations.php
 
 #include "include/conf.h"
 #include "include/defs.h"
+#include "include/filelist.h"
+#include "include/readlog.h"
 
-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();
-}
-
-static void getlog(void)
+       //! 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 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[512];
-       char *buf;
-       int  fd1,fd2;
-       int cstatus;
-       longline line;
-
-       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);
+       FileListIterator FIter;
+       const char *file;
+       bool found=false;
+       struct stat st;
+       time_t latest;
+
+       FIter=FileListIter_Open(AccessLog);
+       while ((file=FileListIter_Next(FIter))!=NULL)
+       {
+               if (stat(file,&st)==-1) {
+                       debuga(__FILE__,__LINE__,_("Cannot stat \"%s\": %s\n"),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);
+       return(found);
+}
 
-       sprintf(cmd,"tail -%d \"%s\"",realtime_access_log_lines,AccessLog[0]);
-       fp = popen(cmd, "r");
-       if (!fp) {
-               debuga(_("Failed to get the %d trailing lines of %s: %s\n"),realtime_access_log_lines,AccessLog[0],strerror(errno));
-               debuga(_("tail command: %s\n"),cmd);
-               exit(EXIT_FAILURE);
-       }
-       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);
+/*!
+ * \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++);
                }
-       pclose(fp);
-       fclose(tmp);
-       longline_destroy(&line);
-
-       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 (unlink(template1)) {
-               debuga(_("Cannot delete %s - %s\n"),template1,strerror(errno));
-               exit(EXIT_FAILURE);
+               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("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"");
+       puts(" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
+       puts("<html>\n");
+       puts("<head>\n");
+       if(realtime_refresh)
+               printf("  <meta http-equiv=refresh content=\"%d\" url=\"sarg-php/sarg-realtime.php\"; charset=\"%s\">\n",realtime_refresh,CharSet);
+       else
+               printf("  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n",CharSet);
+       css(stdout);
+       puts("</head>\n");
+       printf("<body style=\"font-family:%s;font-size:%s;background-color:%s;background-image:url(%s)\">\n",FontFace,TitleFontSize,BgColor,BgImage);
+       puts("<div align=\"center\"><table cellpadding=\"1\" cellspacing=\"1\">\n");
+       printf("<tr><th class=\"title_l\" colspan=\"10\">SARG %s</th></tr>\n",_("Realtime"));
+       printf("<tr><th class=\"text\" colspan=\"10\">%s: %d s</th></tr>\n",_("Auto refresh"),realtime_refresh);
+       printf("<tr><th class=\"header_c\">%s</th><th class=\"header_c\">%s</th><th class=\"header_c\">%s</th><th class=\"header_c\">%s</th><th class=\"header_l\">%s</th></tr>\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 ; i<realtime_access_log_lines ; i++)
+       {
+               entry=List+Index;
+               Index--;
+               if (Index<0) Index=Size-1;
+
+               if (UserIp)
+                       strcpy(user,entry->Ip);
+               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("<tr><td class=\"data\">%s</td><td class=\"data3\">%s</td><td class=\"data3\">%s</td><td class=\"data3\">%s</td><td class=\"data2\"><a href=\"http://%s\">%s</td></tr>\n",
+                          tbuf,entry->Ip,name,entry->HttpMethod,entry->Url,entry->Url);
+       }
+
+       puts("</table>\n</div>\n</body>\n</html>\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;
+       FileObject *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));
+       init_usertab(UserTabFile);
+       LogLine_Init(&log_line);
+
+       /*
+        * 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(__FILE__,__LINE__,_("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));
 
-       header();
+       if (!GetLatestModified(file_name,sizeof(file_name)))
+       {
+               debuga(__FILE__,__LINE__,_("No log file to read the last %d lines from\n"),realtime_access_log_lines);
+               exit(EXIT_FAILURE);
+       }
+       fp = FileObject_Open(file_name);
+       if (!fp) {
+               debuga(__FILE__,__LINE__,_("Cannot open file \"%s\": %s\n"),file_name,FileObject_GetLastOpenError());
+               exit(EXIT_FAILURE);
+       }
 
        if ((line=longline_create())==NULL) {
-               debuga(_("Not enough memory to read the log file\n"));
+               debuga(__FILE__,__LINE__,_("Not enough memory to read file \"%s\"\n"),file_name);
                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);
+       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(DateFormat[0]=='u')
-                       strftime(tbuf, sizeof(tbuf), "%Y-%m-%d %H:%M", t);
-               else if(DateFormat[0]=='e')
-                       strftime(tbuf, sizeof(tbuf), "%d-%m-%Y %H:%M", t);
-               
-               printf("<tr><td class=\"data\">%s</td><td class=\"data3\">%s</td><td class=\"data3\">%s</td><td class=\"data3\">%s</td><td class=\"data2\"><a href=\"http://%s\">%s</td></tr>\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;
        }
-       longline_destroy(&line);
-       if (ourl) free(ourl);
-
-       puts("</table>\n</div>\n</body>\n</html>\n");
-       fclose(fin);
-       if (unlink(tmp)) {
-               debuga(_("Cannot delete %s - %s\n"),tmp,strerror(errno));
+       if (FileObject_Close(fp)) {
+               debuga(__FILE__,__LINE__,_("Read error in \"%s\": %s\n"),file_name,FileObject_GetLastCloseError());
                exit(EXIT_FAILURE);
        }
-       fflush(NULL);
-}
+       longline_destroy(&line);
 
-static void header(void)
-{
-       puts("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\"");
-       puts(" \"http://www.w3.org/TR/html4/loose.dtd\">\n");
-       puts("<html>\n");
-       puts("<head>\n");
-       if(realtime_refresh)
-               printf("  <meta http-equiv=refresh content=\"%d\" url=\"sarg-php/sarg-realtime.php\"; charset=\"%s\">\n",realtime_refresh,CharSet);
-       else
-               printf("  <meta http-equiv=\"Content-Type\" content=\"text/html; charset=%s\">\n",CharSet);
-       css(stdout);
-       puts("</head>\n");
-       printf("<body style=\"font-family:%s;font-size:%s;background-color:%s;background-image:url(%s)\">\n",FontFace,TitleFontSize,BgColor,BgImage);
-       puts("<div align=\"center\"><table cellpadding=\"1\" cellspacing=\"1\">\n");
-       printf("<tr><th class=\"title_l\" colspan=\"10\">SARG %s</th></tr>\n",_("Realtime"));
-       printf("<tr><th class=\"text\" colspan=\"10\">%s: %d s</th></tr>\n",_("Auto refresh"),realtime_refresh);
-       printf("<tr><th class=\"header_c\">%s</th><th class=\"header_c\">%s</th><th class=\"header_c\">%s</th><th class=\"header_c\">%s</th><th class=\"header_l\">%s</th></tr>\n",_("DATE/TIME"),_("IP/NAME"),_("USERID"),_("TYPE"),_("ACCESSED SITE"));
+       datashow(StoredLogEntries,StoreIndex,StoreSize);
+       free(StoredLogEntries);
 }