prelude-lml/master: File notification improvement

[email protected] Mon, 8 Feb 2010 11:24:48 +0100 (CET)
Newsgroups gmane.comp.security.ids.prelude.cvs
Message-ID <[email protected]>
commit 436dad24ea6bfb8666f6babf629c6e79e0902875
Author: Yoann Vandoorselaere <[email protected]>
Date:   Fri Feb 5 18:28:50 2010 +0100

    File notification improvement
    
    Always gather our own statistics using fstat() instead of relying on
    libev stats that often are outdated.
    
    When we notice a file deletion, trigger reading of any unread events
    on the deleted file for which we still have an open file descriptor.
    
    Additionaly, improve handling of fast delete + re-create event.


========================================

 src/file-server.c |   69 ++++++++++++++++++++++++++++++++++++++--------------
 1 files changed, 50 insertions(+), 19 deletions(-)

========================================

diff --git a/src/file-server.c b/src/file-server.c
index 3b88f71..253dffb 100644
--- a/src/file-server.c
+++ b/src/file-server.c
@@ -254,7 +254,7 @@ static void logfile_alert(monitor_fd_t *fd, ev_statdata *st_old, ev_statdata *st
 
 
 
-static void logfile_modified_alert(monitor_fd_t *monitor, ev_statdata *st_old, ev_statdata *st_new)
+static void logfile_modified_alert(monitor_fd_t *monitor, ev_statdata *st_old, struct stat *st_new)
 {
         int ret;
         prelude_string_t *str;
@@ -507,7 +507,7 @@ static int file_metadata_open(monitor_fd_t *monitor)
 }
 
 
-static int check_logfile_data(monitor_fd_t *monitor, ev_statdata *st)
+static int check_logfile_data(monitor_fd_t *monitor, struct stat *st)
 {
         off_t len, ret;
         int eventno = 0;
@@ -731,7 +731,7 @@ static void monitor_close(monitor_fd_t *monitor)
  * cause heavy performance problem. The best solution may be to centralize
  * the logging on a remote host.
  */
-static void check_modification_time(monitor_fd_t *monitor, ev_statdata *prev, ev_statdata *st)
+static void check_modification_time(monitor_fd_t *monitor, ev_statdata *prev, struct stat *st)
 {
         int ret;
         const char *filename;
@@ -802,12 +802,11 @@ static int get_rotation_time_offset(monitor_fd_t *monitor, ev_statdata *st)
 
 
 
-static int is_file_already_used(monitor_fd_t *monitor, ev_statdata *st_old, ev_statdata *st_new)
+static int is_file_already_used(monitor_fd_t *monitor, ev_statdata *st_prev, ev_statdata *st_cur, struct stat *st_now)
 {
         int ret;
         char buf[1024];
         int toff, soff;
-        struct stat st;
         const char *filename, *ctxt;
         idmef_impact_t *impact;
         idmef_classification_t *classification;
@@ -816,15 +815,19 @@ static int is_file_already_used(monitor_fd_t *monitor, ev_statdata *st_old, ev_s
 
         filename = lml_log_source_get_name(monitor->source);
 
-        if ( st_new->st_nlink > 0 )
+        /*
+         * rename = 1 - 0
+         * rename + re-create = 1 - 1 - !=
+         * delete = 0 - 0
+         * delete + re-create = 0 - 1
+         */
+        if ( st_now->st_nlink > 0 && st_cur->st_nlink > 0 && st_now->st_ino == st_cur->st_ino )
                 return 0;
 
-        fstat(fileno(monitor->fd), &st);
-
         /*
          * test if the file has been removed
          */
-        if ( st.st_nlink > 0 && st_new->st_nlink == 0 ) {
+        if ( st_now->st_nlink > 0 ) {
                 prelude_log(PRELUDE_LOG_INFO, "%s: has been renamed.\n", filename);
                 ctxt = LOGFILE_RENAME_CLASS;
                 is_deleted = FALSE;
@@ -834,6 +837,12 @@ static int is_file_already_used(monitor_fd_t *monitor, ev_statdata *st_old, ev_s
                 is_deleted = TRUE;
         }
 
+        /*
+         * Before closing the monitor, handle any unread data.
+         */
+        if ( st_now->st_size > monitor->last_size )
+                check_logfile_data(monitor, st_now);
+
         monitor_close(monitor);
 
         ret = idmef_classification_new(&classification);
@@ -857,8 +866,8 @@ static int is_file_already_used(monitor_fd_t *monitor, ev_statdata *st_old, ev_s
         idmef_impact_set_type(impact, IDMEF_IMPACT_TYPE_FILE);
         idmef_impact_set_completion(impact, IDMEF_IMPACT_COMPLETION_SUCCEEDED);
 
-        soff = get_rotation_size_offset(monitor, st_new);
-        toff = get_rotation_time_offset(monitor, st_new);
+        soff = get_rotation_size_offset(monitor, st_now);
+        toff = get_rotation_time_offset(monitor, st_now);
 
         if ( toff <= max_rotation_time_offset|| soff <= max_rotation_size_offset ) {
                 idmef_impact_set_severity(impact, IDMEF_IMPACT_SEVERITY_INFO);
@@ -876,9 +885,9 @@ static int is_file_already_used(monitor_fd_t *monitor, ev_statdata *st_old, ev_s
         }
 
         if ( is_deleted )
-                logfile_alert(monitor, st_new, NULL, classification, impact);
+                logfile_alert(monitor, st_now, NULL, classification, impact);
         else
-                logfile_alert(monitor, st_old, st_new, classification, impact);
+                logfile_alert(monitor, st_prev, st_now, classification, impact);
 
         return -1;
 }
@@ -897,25 +906,47 @@ static void libev_io_cb(ev_io *io, int revents)
 static void libev_stat_cb(ev_stat *st, int revents)
 {
         int ret;
+        struct stat fst;
         monitor_fd_t *monitor = st->data;
 
         if ( ! monitor->fd )
                 if ( monitor_open(monitor) < 0 )
                         return;
 
-        ret = is_file_already_used(monitor, &st->prev, &st->attr);
-        if ( ret < 0 )
-                return;
+        /*
+         * Do not rely on libev statistics gathered by stat().
+         *
+         * If a file is written then deleted, but re-created very fast,
+         * only fstat() can report an st_nlink of 0.
+         *
+         * If a file is deleted from libev point of view, only fstat()
+         * will be able to provide latest information about the stat of
+         * the file.
+         */
+        fstat(fileno(monitor->fd), &fst);
+
+        ret = is_file_already_used(monitor, &st->prev, &st->attr, &fst);
+        if ( ret < 0 ) {
+                if ( st->attr.st_nlink == 0 )
+                        return;
+                else {
+                        /*
+                         * The file has been deleted then created again: trigger opening/reading of the new dfile.
+                         */
+                        return libev_stat_cb(st, revents);
+                }
+        }
 
         /*
-         * check mtime consistency.
+         * check mtime/size consistency.
          */
-        check_modification_time(monitor, &st->prev, &st->attr);
+        check_modification_time(monitor, &st->prev, &fst);
+
 
         /*
          * read and analyze available data.
          */
-        check_logfile_data(monitor, &st->attr);
+        check_logfile_data(monitor, &fst);
 }
 
 
_______________________________________________
Prelude-cvslog site list
[email protected]
http://lists.prelude-technologies.com/mailman/listinfo/prelude-cvslog