2 * SARG Squid Analysis Report Generator http://sarg.sourceforge.net
6 * please look at http://sarg.sourceforge.net/donations.php
8 * http://sourceforge.net/projects/sarg/forums/forum/363374
9 * ---------------------------------------------------------------------
11 * This program is free software; you can redistribute it and/or modify
12 * it under the terms of the GNU General Public License as published by
13 * the Free Software Foundation; either version 2 of the License, or
14 * (at your option) any later version.
16 * This program is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19 * GNU General Public License for more details.
21 * You should have received a copy of the GNU General Public License
22 * along with this program; if not, write to the Free Software
23 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111, USA.
27 #include "include/conf.h"
28 #include "include/defs.h"
29 #include "include/readlog.h"
30 #include "include/filelist.h"
32 #define REPORT_EVERY_X_LINES 5000
33 #define MAX_OPEN_USER_FILES 10
37 struct userfilestruct
*next
;
38 struct userinfostruct
*user
;
42 enum ExcludeReasonEnum
44 //! User name too long.
46 //! Squid logged an incomplete query received from the client.
48 //! Log file turned over.
50 //! Excluded by exclude_string from sarg.conf.
52 //! Unknown input log file format.
54 //! Line to be ignored from the input log file.
56 //! Entry not withing the requested date range.
62 //! User is not in the include_users list.
64 //! HTTP code excluded by exclude_code file.
66 //! Invalid character found in user name.
70 //! Not the IP address requested with -a.
72 //! URL excluded by -c or exclude_hosts.
74 //! Entry time outside of requested hour range.
76 //! Not the URL requested by -s.
80 //! Not the user requested by -u.
84 //! User ignored by exclude_users
87 ER_Last
//!< last entry of the list
90 numlist weekdays
= { { 0, 1, 2, 3, 4, 5, 6 }, 7 };
91 numlist hours
= { { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23 }, 24 };
93 extern char *userfile
;
94 extern FileListObject AccessLog
;
96 extern const struct ReadLogProcessStruct ReadSquidLog
;
97 extern const struct ReadLogProcessStruct ReadCommonLog
;
98 extern const struct ReadLogProcessStruct ReadSargLog
;
99 extern const struct ReadLogProcessStruct ReadExtLog
;
101 //! The list of the supported log formats.
102 static const struct ReadLogProcessStruct
const *LogFormats
[]=
110 //! The path to the sarg log file.
111 static char SargLogFile
[4096]="";
112 //! Handle to the sarg log file. NULL if not created.
113 static FILE *fp_log
=NULL
;
114 //! The number of records read from the input logs.
115 static long int totregsl
=0;
116 //! The number of records kept.
117 static long int totregsg
=0;
118 //! The number of records excluded.
119 static long int totregsx
=0;
120 //! The beginning of a linked list of user's file.
121 static struct userfilestruct
*first_user_file
=NULL
;
122 //! Count the number of occurence of each input log format.
123 static unsigned long int format_count
[sizeof(LogFormats
)/sizeof(*LogFormats
)];
124 //! The minimum date found in the input logs.
125 static int mindate
=0;
126 static int maxdate
=0;
127 //! Count the number of excluded records.
128 static unsigned long int excluded_count
[ER_Last
];
131 Read a single log file.
133 \param arq The log file name to read.
135 static void ReadOneLogFile(struct ReadLogDataStruct
*Filter
,const char *arq
)
143 char tmp3
[MAXLEN
]="";
144 char download_url
[MAXLEN
];
145 char smartfilter
[MAXLEN
];
148 int current_format_idx
;
149 int OutputNonZero
= REPORT_EVERY_X_LINES
;
154 int maxopenfiles
=MAX_OPEN_USER_FILES
;
155 int successive_errors
=0;
157 unsigned long int recs1
=0UL;
158 unsigned long int recs2
=0UL;
162 bool download_flag
=false;
164 enum ReadLogReturnCodeEnum log_entry_status
;
166 struct getwordstruct gwarea
;
167 struct userfilestruct
*prev_ufile
;
168 struct userinfostruct
*uinfo
;
169 struct userfilestruct
*ufile
;
170 struct userfilestruct
*ufile1
;
171 struct ReadLogStruct log_entry
;
172 const struct ReadLogProcessStruct
*current_format
=NULL
;
175 current_format_idx
=-1;
176 for (x
=0 ; x
<sizeof(LogFormats
)/sizeof(*LogFormats
) ; x
++)
177 if (LogFormats
[x
]->NewFile
)
178 LogFormats
[x
]->NewFile(arq
);
180 if (arq
[0]=='-' && arq
[1]=='\0') {
182 debuga(_("Reading access log file: from stdin\n"));
186 if (Filter
->DateRange
[0]!='\0') {
187 if (stat(arq
,&logstat
)!=0) {
188 debuga(_("Cannot get the modification time of input log file %s (%s). Processing it anyway\n"),arq
,strerror(errno
));
190 struct tm
*logtime
=localtime(&logstat
.st_mtime
);
191 if ((logtime
->tm_year
+1900)*10000+(logtime
->tm_mon
+1)*100+logtime
->tm_mday
<dfrom
) {
192 debuga(_("Ignoring old log file %s\n"),arq
);
197 fp_in
=decomp(arq
,&from_pipe
);
199 debuga(_("Cannot open input log file \"%s\": %s\n"),arq
,strerror(errno
));
202 if(debug
) debuga(_("Reading access log file: %s\n"),arq
);
211 // pre-read the file only if we have to show stats
212 if (ShowReadStatistics
&& ShowReadPercent
&& !from_stdin
&& !from_pipe
) {
217 while ((nread
=fread(tmp4
,1,sizeof(tmp4
),fp_in
))>0) {
218 for (i
=0 ; i
<nread
; i
++)
220 if (tmp4
[i
]!='\n' && tmp4
[i
]!='\r') {
224 if (tmp4
[i
]=='\n' || tmp4
[i
]=='\r') {
231 printf(_("SARG: Records in file: %lu, reading: %3.2f%%"),recs1
,(float) 0);
236 if ((line
=longline_create())==NULL
) {
237 debuga(_("Not enough memory to read log file %s\n"),arq
);
241 while ((linebuf
=longline_read(fp_in
,line
))!=NULL
) {
245 if (ShowReadStatistics
&& --OutputNonZero
<=0) {
247 double perc
= recs2
* 100. / recs1
;
248 printf(_("SARG: Records in file: %lu, reading: %3.2lf%%"),recs2
,perc
);
250 printf(_("SARG: Records in file: %lu"),recs2
);
254 OutputNonZero
= REPORT_EVERY_X_LINES
;
258 The following checks are retained here as I don't know to
259 what format they apply. They date back to pre 2.4 versions.
261 //if(blen < 58) continue; //this test conflict with the reading of the sarg log header line
262 if(strstr(linebuf
,"HTTP/0.0") != 0) {//recorded by squid when encountering an incomplete query
263 excluded_count
[ER_IncompleteQuery
]++;
266 if(strstr(linebuf
,"logfile turned over") != 0) {//reported by newsyslog
267 excluded_count
[ER_LogfileTurnedOver
]++;
272 if(ExcludeString
[0] != '\0') {
274 getword_start(&gwarea
,ExcludeString
);
275 while(strchr(gwarea
.current
,':') != 0) {
276 if (getword_multisep(val1
,sizeof(val1
),&gwarea
,':')<0) {
277 debuga(_("Maybe you have a broken record or garbage in your exclusion string\n"));
280 if((str
=(char *) strstr(linebuf
,val1
)) != (char *) NULL
) {
285 if(!exstring
&& (str
=(char *) strstr(linebuf
,gwarea
.current
)) != (char *) NULL
)
288 excluded_count
[ER_ExcludeString
]++;
295 printf("BUF=%s\n",linebuf
);
298 log_entry_status
=RLRC_Unknown
;
299 memset(&log_entry
,0,sizeof(log_entry
));
300 if (current_format
) {
301 log_entry_status
=current_format
->ReadEntry(linebuf
,&log_entry
);
304 // find out what line format to use
305 if (log_entry_status
==RLRC_Unknown
) {
306 for (x
=0 ; x
<(int)(sizeof(LogFormats
)/sizeof(*LogFormats
)) ; x
++) {
307 if (LogFormats
[x
]==current_format
) continue;
308 memset(&log_entry
,0,sizeof(log_entry
));
309 log_entry_status
=LogFormats
[x
]->ReadEntry(linebuf
,&log_entry
);
310 if (log_entry_status
!=RLRC_Unknown
) break;
312 if (x
>=(int)(sizeof(LogFormats
)/sizeof(*LogFormats
))) {
313 if (++successive_errors
>NumLogSuccessiveErrors
) {
314 debuga(ngettext("%d consecutive error found in the input log file %s\n",
315 "%d consecutive errors found in the input log file %s\n",successive_errors
),successive_errors
,arq
);
318 if (NumLogTotalErrors
>=0 && ++total_errors
>NumLogTotalErrors
) {
319 debuga(ngettext("%d error found in the input log file (last in %s)\n",
320 "%d errors found in the input log file (last in %s)\n",total_errors
),total_errors
,arq
);
323 debuga(_("The following line read from %s could not be parsed and is ignored\n%s\n"),arq
,linebuf
);
324 excluded_count
[ER_UnknownFormat
]++;
327 current_format
=LogFormats
[x
];
328 current_format_idx
=x
;
330 /* TRANSLATORS: The argument is the log format name as translated by you. */
331 debuga(_("Log format identified as \"%s\" for %s\n"),_(current_format
->Name
),arq
);
335 if (log_entry_status
==RLRC_Ignore
) {
336 excluded_count
[ER_FormatData
]++;
339 if (current_format_idx
<0 || current_format
==NULL
) {
340 debuga(_("Sarg failed to determine the format of the input log file %s\n"),arq
);
343 if (log_entry_status
==RLRC_InternalError
) {
344 debuga(_("Internal error encountered while processing %s\nSee previous message to know the reason for that error.\n"),arq
);
347 format_count
[current_format_idx
]++;
349 if (!fp_log
&& ParsedOutputLog
[0] && current_format
!=&ReadSargLog
) {
350 if(access(ParsedOutputLog
,R_OK
) != 0) {
351 my_mkdir(ParsedOutputLog
);
353 if (snprintf(SargLogFile
,sizeof(SargLogFile
),"%s/sarg_temp.log",ParsedOutputLog
)>=sizeof(SargLogFile
)) {
354 debuga(_("File name too long: %s/sarg_temp.log\n"),ParsedOutputLog
);
357 if((fp_log
=MY_FOPEN(SargLogFile
,"w"))==NULL
) {
358 debuga(_("(log) Cannot open log file %s: %s\n"),SargLogFile
,strerror(errno
));
361 fputs("*** SARG Log ***\n",fp_log
);
364 if (log_entry
.Ip
==NULL
) {
365 debuga(_("Unknown input log file format: no IP addresses\n"));
368 if (log_entry
.User
==NULL
) {
369 debuga(_("Unknown input log file format: no user\n"));
372 if (log_entry
.Url
==NULL
) {
373 debuga(_("Unknown input log file format: no URL\n"));
377 idata
=builddia(log_entry
.EntryTime
.tm_mday
,log_entry
.EntryTime
.tm_mon
+1,log_entry
.EntryTime
.tm_year
+1900);
379 printf("DATE=%s IDATA=%d DFROM=%d DUNTIL=%d\n",Filter
->DateRange
,idata
,dfrom
,duntil
);
381 if(Filter
->DateRange
[0] != '\0'){
382 if(idata
< dfrom
|| idata
> duntil
) {
383 excluded_count
[ER_OutOfDateRange
]++;
388 // Record only hours usage which is required
389 if( bsearch( &( log_entry
.EntryTime
.tm_wday
), weekdays
.list
, weekdays
.len
, sizeof( int ), compar
) == NULL
) {
390 excluded_count
[ER_OutOfWDayRange
]++;
394 if( bsearch( &( log_entry
.EntryTime
.tm_hour
), hours
.list
, hours
.len
, sizeof( int ), compar
) == NULL
) {
395 excluded_count
[ER_OutOfHourRange
]++;
400 if(strlen(log_entry
.User
) > MAX_USER_LEN
) {
401 if (debugm
) printf(_("User ID too long: %s\n"),log_entry
.User
);
402 excluded_count
[ER_UserNameTooLong
]++;
408 if(IncludeUsers
[0] != '\0') {
409 snprintf(val1
,sizeof(val1
),":%s:",log_entry
.User
);
410 if((str
=(char *) strstr(IncludeUsers
,val1
)) == (char *) NULL
) {
411 excluded_count
[ER_User
]++;
416 if(vercode(log_entry
.HttpCode
)) {
417 if (debugm
) printf(_("Excluded code: %s\n"),log_entry
.HttpCode
);
418 excluded_count
[ER_HttpCode
]++;
423 if(testvaliduserchar(log_entry
.User
)) {
424 excluded_count
[ER_InvalidUserChar
]++;
428 // replace any tab by a single space
429 for (str
=log_entry
.Url
; *str
; str
++)
430 if (*str
=='\t') *str
=' ';
431 for (str
=log_entry
.HttpCode
; *str
; str
++)
432 if (*str
=='\t') *str
=' ';
434 if (current_format
!=&ReadSargLog
) {
436 The full URL is not saved in sarg log. There is no point in testing the URL to detect
439 download_flag
=is_download_suffix(log_entry
.Url
);
441 safe_strcpy(download_url
,log_entry
.Url
,sizeof(download_url
));
446 url
=process_url(log_entry
.Url
,LongUrl
);
447 if (!url
|| url
[0] == '\0') {
448 excluded_count
[ER_NoUrl
]++;
453 if(strcmp(addr
,log_entry
.Ip
)!=0) {
454 excluded_count
[ER_UntrackedIpAddr
]++;
458 if(Filter
->HostFilter
) {
459 if(!vhexclude(url
)) {
460 if (debugm
) printf(_("Excluded site: %s\n"),url
);
461 excluded_count
[ER_Url
]++;
467 if(Filter
->StartTime
>= 0 && Filter
->EndTime
>= 0) {
468 hmr
=log_entry
.EntryTime
.tm_hour
*100+log_entry
.EntryTime
.tm_min
;
469 if(hmr
< Filter
->StartTime
|| hmr
> Filter
->EndTime
) {
470 excluded_count
[ER_OutOfTimeRange
]++;
476 if(strstr(url
,site
)==0) {
477 excluded_count
[ER_UntrackedUrl
]++;
483 log_entry
.User
=log_entry
.Ip
;
487 if ((log_entry
.User
[0]=='\0') || (log_entry
.User
[1]=='\0' && (log_entry
.User
[0]=='-' || log_entry
.User
[0]==' '))) {
488 if(RecordsWithoutUser
== RECORDWITHOUTUSER_IP
) {
489 log_entry
.User
=log_entry
.Ip
;
492 if(RecordsWithoutUser
== RECORDWITHOUTUSER_IGNORE
) {
493 excluded_count
[ER_NoUser
]++;
496 if(RecordsWithoutUser
== RECORDWITHOUTUSER_EVERYBODY
)
497 log_entry
.User
="everybody";
499 if(NtlmUserFormat
== NTLMUSERFORMAT_USER
) {
500 if ((str
=strchr(log_entry
.User
,'+'))!=NULL
|| (str
=strchr(log_entry
.User
,'\\'))!=NULL
|| (str
=strchr(log_entry
.User
,'_'))!=NULL
) {
501 log_entry
.User
=str
+1;
508 if(strcmp(log_entry
.User
,us
)!=0) {
509 excluded_count
[ER_UntrackedUser
]++;
514 if(Filter
->SysUsers
) {
515 snprintf(wuser
,sizeof(wuser
),":%s:",log_entry
.User
);
516 if(strstr(userfile
, wuser
) == 0) {
517 excluded_count
[ER_SysUser
]++;
522 if(Filter
->UserFilter
) {
523 if(!vuexclude(log_entry
.User
)) {
524 if (debugm
) printf(_("Excluded user: %s\n"),log_entry
.User
);
525 excluded_count
[ER_IgnoredUser
]++;
531 user
=process_user(log_entry
.User
);
532 if (log_entry
.User
!=user
) {
536 if (log_entry
.User
[0]=='\0' || (log_entry
.User
[1]=='\0' && (log_entry
.User
[0]=='-' ||
537 log_entry
.User
[0]==' ' || log_entry
.User
[0]==':'))) {
538 excluded_count
[ER_NoUser
]++;
542 if (log_entry
.DataSize
<0) log_entry
.DataSize
=0;
544 if (log_entry
.ElapsedTime
<0) log_entry
.ElapsedTime
=0;
545 if (Filter
->max_elapsed
>0 && log_entry
.ElapsedTime
>Filter
->max_elapsed
) {
546 log_entry
.ElapsedTime
=0;
549 if((str
=(char *) strstr(linebuf
, "[SmartFilter:")) != (char *) NULL
) {
551 snprintf(smartfilter
,sizeof(smartfilter
),"\"%s\"",str
+1);
552 } else strcpy(smartfilter
,"\"\"");
556 for (ufile
=first_user_file
; ufile
&& strcmp(log_entry
.User
,ufile
->user
->id
)!=0 ; ufile
=ufile
->next
) {
558 if (ufile
->file
) nopen
++;
561 ufile
=malloc(sizeof(*ufile
));
563 debuga(_("Not enough memory to store the user %s\n"),log_entry
.User
);
566 memset(ufile
,0,sizeof(*ufile
));
567 ufile
->next
=first_user_file
;
568 first_user_file
=ufile
;
569 uinfo
=userinfo_create(log_entry
.User
,(id_is_ip
) ? NULL
: log_entry
.Ip
);
574 prev_ufile
->next
=ufile
->next
;
575 ufile
->next
=first_user_file
;
576 first_user_file
=ufile
;
579 #ifdef ENABLE_DOUBLE_CHECK_DATA
580 ufile
->user
->nbytes
+=log_entry
.DataSize
;
581 ufile
->user
->elap
+=log_entry
.ElapsedTime
;
584 if (ufile
->file
==NULL
) {
585 if (nopen
>=maxopenfiles
) {
587 for (ufile1
=first_user_file
; ufile1
; ufile1
=ufile1
->next
) {
588 if (ufile1
->file
!=NULL
) {
589 if (x
>=maxopenfiles
) {
590 if (fclose(ufile1
->file
)==EOF
) {
591 debuga(_("Write error in the log file of user %s: %s\n"),ufile1
->user
->id
,strerror(errno
));
600 if (snprintf (tmp3
, sizeof(tmp3
), "%s/%s.user_unsort", tmp
, ufile
->user
->filename
)>=sizeof(tmp3
)) {
601 debuga(_("Temporary user file name too long: %s/%s.user_unsort\n"), tmp
, ufile
->user
->filename
);
604 if ((ufile
->file
= MY_FOPEN (tmp3
, "a")) == NULL
) {
605 debuga(_("(log) Cannot open temporary file %s: %s\n"), tmp3
, strerror(errno
));
610 strftime(dia
, sizeof(dia
), "%d/%m/%Y",&log_entry
.EntryTime
);
611 strftime(hora
,sizeof(hora
),"%H:%M:%S",&log_entry
.EntryTime
);
613 if (fprintf(ufile
->file
, "%s\t%s\t%s\t%s\t%"PRIu64
"\t%s\t%ld\t%s\n",dia
,hora
,
614 log_entry
.Ip
,url
,(uint64_t)log_entry
.DataSize
,
615 log_entry
.HttpCode
,log_entry
.ElapsedTime
,smartfilter
)<=0) {
616 debuga(_("Write error in the log file of user %s\n"),log_entry
.User
);
621 if (fp_log
&& current_format
!=&ReadSargLog
) {
622 fprintf(fp_log
, "%s\t%s\t%s\t%s\t%s\t%"PRIu64
"\t%s\t%ld\t%s\n",dia
,hora
,
623 log_entry
.User
,log_entry
.Ip
,url
,(uint64_t)log_entry
.DataSize
,
624 log_entry
.HttpCode
,log_entry
.ElapsedTime
,smartfilter
);
629 denied_write(&log_entry
);
630 authfail_write(&log_entry
);
631 if (download_flag
) download_write(&log_entry
,download_url
);
633 if (current_format
!=&ReadSargLog
) {
634 if (period
.start
.tm_year
==0 || idata
<mindate
|| compare_date(&period
.start
,&log_entry
.EntryTime
)>0){
636 memcpy(&period
.start
,&log_entry
.EntryTime
,sizeof(log_entry
.EntryTime
));
638 if (period
.end
.tm_year
==0 || idata
>maxdate
|| compare_date(&period
.end
,&log_entry
.EntryTime
)<0) {
640 memcpy(&period
.end
,&log_entry
.EntryTime
,sizeof(log_entry
.EntryTime
));
645 printf("IP=\t%s\n",log_entry
.Ip
);
646 printf("USER=\t%s\n",log_entry
.User
);
647 printf("ELAP=\t%ld\n",log_entry
.ElapsedTime
);
648 printf("DATE=\t%s\n",dia
);
649 printf("TIME=\t%s\n",hora
);
650 //printf("FUNC=\t%s\n",fun);
651 printf("URL=\t%s\n",url
);
652 printf("CODE=\t%s\n",log_entry
.HttpCode
);
653 printf("LEN=\t%"PRIu64
"\n",(uint64_t)log_entry
.DataSize
);
656 longline_destroy(&line
);
663 if (ShowReadStatistics
) {
665 printf(_("SARG: Records in file: %lu, reading: %3.2f%%\n"),recs2
, (float) 100 );
667 printf(_("SARG: Records in file: %lu\n"),recs2
);
674 * Display a line with the excluded entries count.
676 * \param Explain A translated string explaining the exluded count.
677 * \param Reason The reason number.
679 static void DisplayExcludeCount(const char *Explain
,enum ExcludeReasonEnum Reason
)
681 if (excluded_count
[Reason
]>0) {
682 debuga(" %s: %lu\n",Explain
,excluded_count
[Reason
]);
689 \param Filter The filtering parameters for the file to load.
691 \retval 1 Records found.
692 \retval 0 No record found.
694 int ReadLogFile(struct ReadLogDataStruct
*Filter
)
698 struct userfilestruct
*ufile
;
699 struct userfilestruct
*ufile1
;
700 FileListIterator FIter
;
703 for (x
=0 ; x
<sizeof(format_count
)/sizeof(*format_count
) ; x
++) format_count
[x
]=0;
704 for (x
=0 ; x
<sizeof(excluded_count
)/sizeof(*excluded_count
) ; x
++) excluded_count
[x
]=0;
705 first_user_file
=NULL
;
713 FIter
=FileListIter_Open(AccessLog
);
714 while ((file
=FileListIter_Next(FIter
))!=NULL
)
715 ReadOneLogFile(Filter
,file
);
716 FileListIter_Close(FIter
);
720 char val4
[4096];//val4 must not be bigger than SargLogFile without fixing the strcpy below
722 if (fclose(fp_log
)==EOF
) {
723 debuga(_("Write error in %s: %s\n"),SargLogFile
,strerror(errno
));
726 strftime(val2
,sizeof(val2
),"%d%m%Y_%H%M",&period
.start
);
727 strftime(val1
,sizeof(val1
),"%d%m%Y_%H%M",&period
.end
);
728 if (snprintf(val4
,sizeof(val4
),"%s/sarg-%s-%s.log",ParsedOutputLog
,val2
,val1
)>=sizeof(val4
)) {
729 debuga(_("File name too long: %s/sarg-%s-%s.log\n"),ParsedOutputLog
,val2
,val1
);
732 if (rename(SargLogFile
,val4
)) {
733 debuga(_("failed to rename %s to %s - %s\n"),SargLogFile
,val4
,strerror(errno
));
735 strcpy(SargLogFile
,val4
);
737 if(strcmp(ParsedOutputLogCompress
,"nocompress") != 0 && ParsedOutputLogCompress
[0] != '\0') {
739 No double quotes around ParsedOutputLogCompress because it may contain command line options. If double quotes are
740 necessary around the command name, put them in the configuration file.
742 if (snprintf(val1
,sizeof(val1
),"%s \"%s\"",ParsedOutputLogCompress
,SargLogFile
)>=sizeof(val1
)) {
743 debuga(_("Command too long: %s \"%s\"\n"),ParsedOutputLogCompress
,SargLogFile
);
746 cstatus
=system(val1
);
747 if (!WIFEXITED(cstatus
) || WEXITSTATUS(cstatus
)) {
748 debuga(_("command return status %d\n"),WEXITSTATUS(cstatus
));
749 debuga(_("command: %s\n"),val1
);
755 debuga(_("Sarg parsed log saved as %s\n"),SargLogFile
);
762 for (ufile
=first_user_file
; ufile
; ufile
=ufile1
) {
764 if (ufile
->file
!=NULL
&& fclose(ufile
->file
)==EOF
) {
765 debuga(_("Write error in the log file of user %s: %s\n"),ufile
->user
->id
,strerror(errno
));
772 unsigned long int totalcount
=0;
774 debuga(_(" Records read: %ld, written: %ld, excluded: %ld\n"),totregsl
,totregsg
,totregsx
);
776 for (x
=sizeof(excluded_count
)/sizeof(*excluded_count
)-1 ; x
>=0 && excluded_count
[x
]>0 ; x
--);
778 debuga(_("Reasons for excluded entries:\n"));
779 DisplayExcludeCount(_("User name too long"),ER_UserNameTooLong
);
780 DisplayExcludeCount(_("Squid logged an incomplete query received from the client"),ER_IncompleteQuery
);
781 DisplayExcludeCount(_("Log file turned over"),ER_LogfileTurnedOver
);
782 DisplayExcludeCount(_("Excluded by \"exclude_string\" in sarg.conf"),ER_ExcludeString
);
783 DisplayExcludeCount(_("Unknown input log file format"),ER_UnknownFormat
);
784 DisplayExcludeCount(_("Line ignored by the input log format"),ER_FormatData
);
785 DisplayExcludeCount(_("Time outside the requested date range (-d)"),ER_OutOfDateRange
);
786 DisplayExcludeCount(_("Ignored week day (\"weekdays\" parameter in sarg.conf)"),ER_OutOfWDayRange
);
787 DisplayExcludeCount(_("Ignored hour (\"hours\" parameter in sarg.conf)"),ER_OutOfHourRange
);
788 DisplayExcludeCount(_("User is not in the \"include_users\" list"),ER_User
);
789 DisplayExcludeCount(_("HTTP code excluded by \"exclude_code\" file"),ER_HttpCode
);
790 DisplayExcludeCount(_("Invalid character found in user name"),ER_InvalidUserChar
);
791 DisplayExcludeCount(_("No URL in entry"),ER_NoUrl
);
792 DisplayExcludeCount(_("Not the IP address requested with -a"),ER_UntrackedIpAddr
);
793 DisplayExcludeCount(_("URL excluded by -c or \"exclude_hosts\""),ER_Url
);
794 DisplayExcludeCount(_("Entry time outside of requested hour range (-t)"),ER_OutOfTimeRange
);
795 DisplayExcludeCount(_("Not the URL requested by -s"),ER_UntrackedUrl
);
796 DisplayExcludeCount(_("No user in entry"),ER_NoUser
);
797 DisplayExcludeCount(_("Not the user requested by -u"),ER_UntrackedUser
);
798 DisplayExcludeCount(_("System user as defined by \"password\" in sarg.conf"),ER_SysUser
);
799 DisplayExcludeCount(_("User ignored by \"exclude_users\""),ER_IgnoredUser
);
802 for (x
=0 ; x
<sizeof(LogFormats
)/sizeof(*LogFormats
) ; x
++) {
803 if (format_count
[x
]>0) {
804 /* TRANSLATORS: It displays the number of lines found in the input log files
805 * for each supported log format. The log format name is the %s and is a string
806 * you translate somewhere else. */
807 debuga(_("%s: %lu entries\n"),_(LogFormats
[x
]->Name
),format_count
[x
]);
808 totalcount
+=format_count
[x
];
812 if (totalcount
==0 && totregsg
)
813 debuga(_("Log with invalid format\n"));
817 debugaz(_("period=%s\n"),period
.text
);
819 return((totregsg
!=0) ? 1 : 0);