1 /* @(#) $Id: ./src/logcollector/logcollector.c, 2012/03/28 dcid Exp $
4 /* Copyright (C) 2009 Trend Micro Inc.
7 * This program is a free software; you can redistribute it
8 * and/or modify it under the terms of the GNU General Public
9 * License (version 2) as published by the FSF - Free Software
18 #include "logcollector.h"
21 int update_fname(int i);
24 char *rand_keepalive_str(char *dst, int size)
26 static const char text[] = "abcdefghijklmnopqrstuvwxyz"
27 "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
29 "!@#$%^&*()_+-=;'[],./?";
30 int i, len = rand() % (size - 10);
31 strncpy(dst, "--MARK--: ", 12);
32 for ( i = 10; i < len; ++i )
34 dst[i] = text[rand() % (sizeof text - 1)];
40 /** void LogCollectorStart() v0.4
41 * Handle file management.
43 void LogCollectorStart()
53 /* To check for inode changes */
60 struct timeval fp_timeout;
64 /* Checking if we are on vista. */
68 /* Reading vista descriptions. */
76 debug1("%s: DEBUG: Entering LogCollectorStart().", ARGV0);
79 /* Initializing each file and structure */
82 if(logff[i].file == NULL)
86 /* Removing duplicate entries. */
87 for(r = 0; r < i; r++)
89 if(logff[r].file && strcmp(logff[i].file, logff[r].file) == 0)
91 merror("%s: WARN: Duplicated log file given: '%s'.",
92 ARGV0, logff[i].file);
94 logff[i].command = NULL;
101 if(logff[i].file == NULL)
103 /* do nothing, duplicated entry. */
106 else if(strcmp(logff[i].logformat,"eventlog") == 0)
110 verbose(READING_EVTLOG, ARGV0, logff[i].file);
111 win_startel(logff[i].file);
114 logff[i].file = NULL;
115 logff[i].command = NULL;
119 else if(strcmp(logff[i].logformat, "eventchannel") == 0)
123 #ifdef EVENTCHANNEL_SUPPORT
124 verbose(READING_EVTLOG, ARGV0, logff[i].file);
125 win_start_event_channel(logff[i].file, logff[i].future, logff[i].query);
127 merror("%s: WARN: eventchannel not available on this version of OSSEC", ARGV0);
132 logff[i].file = NULL;
133 logff[i].command = NULL;
137 else if(strcmp(logff[i].logformat, "command") == 0)
139 logff[i].file = NULL;
145 logff[i].read = (void *)read_command;
147 verbose("%s: INFO: Monitoring output of command(%d): %s", ARGV0, logff[i].ign, logff[i].command);
151 os_strdup(logff[i].command, logff[i].alias);
156 merror("%s: ERROR: Missing command argument. Ignoring it.",
160 else if(strcmp(logff[i].logformat, "full_command") == 0)
162 logff[i].file = NULL;
167 logff[i].read = (void *)read_fullcommand;
169 verbose("%s: INFO: Monitoring full output of command(%d): %s", ARGV0, logff[i].ign, logff[i].command);
172 os_strdup(logff[i].command, logff[i].alias);
176 merror("%s: ERROR: Missing command argument. Ignoring it.",
183 logff[i].command = NULL;
186 /* Initializing the files */
189 /* Day must be zero for all files to be initialized */
193 handle_file(i, 1, 1);
197 ErrorExit(PARSE_ERROR, ARGV0, logff[i].ffile);
203 handle_file(i, 1, 1);
206 verbose(READING_FILE, ARGV0, logff[i].file);
208 /* Getting the log type */
209 if(strcmp("snort-full", logff[i].logformat) == 0)
211 logff[i].read = (void *)read_snortfull;
214 if(strcmp("ossecalert", logff[i].logformat) == 0)
216 logff[i].read = (void *)read_ossecalert;
219 else if(strcmp("nmapg", logff[i].logformat) == 0)
221 logff[i].read = (void *)read_nmapg;
223 else if(strcmp("mysql_log", logff[i].logformat) == 0)
225 logff[i].read = (void *)read_mysql_log;
227 else if(strcmp("mssql_log", logff[i].logformat) == 0)
229 logff[i].read = (void *)read_mssql_log;
231 else if(strcmp("postgresql_log", logff[i].logformat) == 0)
233 logff[i].read = (void *)read_postgresql_log;
235 else if(strcmp("djb-multilog", logff[i].logformat) == 0)
237 if(!init_djbmultilog(i))
239 merror(INV_MULTILOG, ARGV0, logff[i].file);
245 logff[i].file = NULL;
247 logff[i].read = (void *)read_djbmultilog;
249 else if(logff[i].logformat[0] >= '0' && logff[i].logformat[0] <= '9')
251 logff[i].read = (void *)read_multiline;
255 logff[i].read = (void *)read_syslog;
258 /* More tweaks for Windows. For some reason IIS places
259 * some wierd characters at the end of the files and getc
260 * always returns 0 (even after clearerr).
265 logff[i].read(i, &r, 1);
273 while(logff[i].alias[ii] != '\0')
275 if(logff[i].alias[ii] == ':')
277 logff[i].alias[ii] = '\\';
285 /* Start up message */
286 verbose(STARTUP_MSG, ARGV0, (int)getpid());
302 fp_timeout.tv_sec = loop_timeout;
303 fp_timeout.tv_usec = 0;
305 /* Waiting for the select timeout */
306 if ((r = select(0, NULL, NULL, NULL, &fp_timeout)) < 0)
308 merror(SELECT_ERROR, ARGV0);
313 ErrorExit(SYSTEM_ERROR, ARGV0);
319 /* Windows don't like select that way */
320 sleep(loop_timeout + 2);
323 /* Check for messages in the event viewer */
330 /* Checking which file is available */
331 for(i = 0; i <= max_file; i++)
335 /* Run the command. */
336 if(logff[i].command && (f_check %2))
339 if((curr_time - logff[i].size) >= logff[i].ign)
341 logff[i].size = curr_time;
342 logff[i].read(i, &r, 0);
348 /* Windows with IIS logs is very strange.
349 * For some reason it always returns 0 (not EOF)
350 * the fgetc. To solve this problem, we always
351 * pass it to the function pointer directly.
354 /* We check for the end of file. If is returns EOF,
355 * we don't attempt to read it.
357 if((r = fgetc(logff[i].fp)) == EOF)
359 clearerr(logff[i].fp);
364 /* If it is not EOF, we need to return the read character */
365 ungetc(r, logff[i].fp);
369 /* Finally, send to the function pointer to read it */
370 logff[i].read(i, &r, 0);
373 /* Checking for error */
374 if(!ferror(logff[i].fp))
377 clearerr(logff[i].fp);
385 /* If ferror is set */
388 merror(FREAD_ERROR, ARGV0, logff[i].file);
390 if(fseek(logff[i].fp, 0, SEEK_END) < 0)
397 merror(FSEEK_ERROR, ARGV0, logff[i].file);
400 /* Closing the file */
405 CloseHandle(logff[i].h);
411 /* Trying to open it again */
412 if(handle_file(i, 1, 1) != 0)
419 logff[i].read(i, &r, 1);
423 /* Increase the error count */
425 clearerr(logff[i].fp);
430 /* Only check bellow if check > VCHECK_FILES */
431 if(f_check <= VCHECK_FILES)
435 /* Send keep alive message */
437 rand_keepalive_str(keepalive, 700);
438 SendMSG(logr_queue, keepalive, "ossec-keepalive", LOCALFILE_MQ);
441 /* Zeroing f_check */
445 /* Checking if any file has been renamed/removed */
446 for(i = 0; i <= max_file; i++)
448 /* These are the windows logs or ignored files */
453 /* Files with date -- check for day change */
462 CloseHandle(logff[i].h);
466 handle_file(i, 0, 1);
470 /* Variable file name */
471 else if(!logff[i].fp)
473 handle_file(i, 0, 0);
479 /* Check for file change -- if the file is open already */
483 if(stat(logff[i].file, &tmp_stat) == -1)
488 merror(FILE_ERROR, ARGV0, logff[i].file);
492 BY_HANDLE_FILE_INFORMATION lpFileInformation;
495 h1 = CreateFile(logff[i].file, GENERIC_READ,
496 FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
497 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
498 if(h1 == INVALID_HANDLE_VALUE)
501 CloseHandle(logff[i].h);
503 merror(FILE_ERROR, ARGV0, logff[i].file);
505 else if(GetFileInformationByHandle(h1, &lpFileInformation) == 0)
508 CloseHandle(logff[i].h);
511 merror(FILE_ERROR, ARGV0, logff[i].file);;
517 else if(logff[i].fd != (lpFileInformation.nFileIndexLow + lpFileInformation.nFileIndexHigh))
519 else if(logff[i].fd != tmp_stat.st_ino)
522 char msg_alert[512 +1];
524 snprintf(msg_alert, 512, "ossec: File rotated (inode "
528 /* Send message about log rotated */
529 SendMSG(logr_queue, msg_alert,
530 "ossec-logcollector", LOCALFILE_MQ);
532 debug1("%s: DEBUG: File inode changed. %s",
533 ARGV0, logff[i].file);
538 CloseHandle(logff[i].h);
543 handle_file(i, 0, 1);
547 else if(logff[i].size > (lpFileInformation.nFileSizeHigh + lpFileInformation.nFileSizeLow))
549 else if(logff[i].size > tmp_stat.st_size)
552 char msg_alert[512 +1];
554 snprintf(msg_alert, 512, "ossec: File size reduced "
555 "(inode remained): '%s'.",
558 /* Send message about log rotated */
559 SendMSG(logr_queue, msg_alert,
560 "ossec-logcollector", LOCALFILE_MQ);
562 debug1("%s: DEBUG: File size reduced. %s",
563 ARGV0, logff[i].file);
566 /* Fixing size so we don't alert more than once */
567 logff[i].size = tmp_stat.st_size;
570 /* Getting new file. */
574 CloseHandle(logff[i].h);
579 handle_file(i, 1, 1);
590 /* Too many errors for the file */
591 if(logff[i].ign > open_file_attempts)
593 /* 999 Maximum ignore */
594 if(logff[i].ign == 999)
599 merror(LOGC_FILE_ERROR, ARGV0, logff[i].file);
604 CloseHandle(logff[i].h);
611 /* If the file has a variable date, ignore it for
616 /* Variable log files should always be attempted
619 //logff[i].file = NULL;
626 /* File not opened */
629 if(logff[i].ign >= 999)
633 /* Try for a few times to open the file */
634 if(handle_file(i, 1, 1) < 0)
647 /**int update_fname(int i): updates file name */
648 int update_fname(int i)
651 time_t __ctime = time(0);
653 char lfile[OS_FLSIZE + 1];
657 p = localtime(&__ctime);
661 if(p->tm_mday == _cday)
667 lfile[OS_FLSIZE] = '\0';
668 ret = strftime(lfile, OS_FLSIZE, logff[i].ffile, p);
671 ErrorExit(PARSE_ERROR, ARGV0, logff[i].ffile);
675 /* Update the file name */
676 if(strcmp(lfile, logff[i].file) != 0)
678 os_free(logff[i].file);
680 os_strdup(lfile, logff[i].file);
682 verbose(VAR_LOG_MON, ARGV0, logff[i].file);
684 /* Setting cday to zero because other files may need
696 /* handle_file: Open, get the fileno, seek to the end and update mtime */
697 int handle_file(int i, int do_fseek, int do_log)
702 /* We must be able to open the file, fseek and get the
703 * time of change from it.
706 logff[i].fp = fopen(logff[i].file, "r");
711 merror(FOPEN_ERROR, ARGV0, logff[i].file);
715 /* Getting inode number for fp */
716 fd = fileno(logff[i].fp);
717 if(fstat(fd, &stat_fd) == -1)
719 merror(FILE_ERROR,ARGV0,logff[i].file);
725 logff[i].fd = stat_fd.st_ino;
726 logff[i].size = stat_fd.st_size;
730 BY_HANDLE_FILE_INFORMATION lpFileInformation;
733 logff[i].h = CreateFile(logff[i].file, GENERIC_READ,
734 FILE_SHARE_DELETE|FILE_SHARE_READ|FILE_SHARE_WRITE,
735 NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
736 if(logff[i].h == INVALID_HANDLE_VALUE)
740 merror(FOPEN_ERROR, ARGV0, logff[i].file);
744 fd = _open_osfhandle((long)logff[i].h, 0);
747 merror(FOPEN_ERROR, ARGV0, logff[i].file);
748 CloseHandle(logff[i].h);
751 logff[i].fp = _fdopen(fd, "r");
752 if(logff[i].fp == NULL)
754 merror(FOPEN_ERROR, ARGV0, logff[i].file);
755 CloseHandle(logff[i].h);
760 /* On windows, we also need the real inode, which is the combination
761 * of the index low + index high numbers.
763 if(GetFileInformationByHandle(logff[i].h, &lpFileInformation) == 0)
765 merror("%s: Unable to get file information by handle.", ARGV0);
767 CloseHandle(logff[i].h);
772 logff[i].fd = (lpFileInformation.nFileIndexLow + lpFileInformation.nFileIndexHigh);
773 logff[i].size = (lpFileInformation.nFileSizeHigh + lpFileInformation.nFileSizeLow);
778 /* Only seek the end of the file if set to. */
779 if(do_fseek == 1 && S_ISREG(stat_fd.st_mode))
781 /* Windows and fseek causes some weird issues.. */
783 if(fseek(logff[i].fp, 0, SEEK_END) < 0)
785 merror(FSEEK_ERROR, ARGV0,logff[i].file);
794 /* Setting ignore to zero */
802 /* Remove newlines and replace tabs in the argument fields with spaces */
803 void win_format_event_string(char *string)
805 if (string == NULL) {
809 while (*string != '\0') {
810 if (*string == '\n' || *string == '\r' || *string == ':') {
811 if (*string == '\n' || *string == '\r') {
817 while (*string == '\t') {