lwatch.c File Reference

Implements the basic watching logic for log files. More...

#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
#include <sys/inotify.h>
#include <sys/select.h>
#include <signal.h>
#include "config.h"
#include "dhash.h"
#include "path_utils.h"
#include "logging.h"
#include "regexp.h"
#include "inotify_watch.h"
#include "util.h"
#include "lwatch.h"

Go to the source code of this file.

Defines

#define _GNU_SOURCE
 Turn on GNU extensions.
#define LOG_LEVEL_ARG   256
 log_level command line argument
#define LOG_TO_CONSOLE_ARG   257
 log_to_console command line argument
#define LOGFILE_PATH_ARG   258
 logfile_path command line argument
#define NO_LOGGING_TO_FILE_ARG   259
 no_logging_to_file command line argument
#define LOG_SHOW_LEVEL_ARG   260
 log_show_level command line argument
#define LOG_NO_SHOW_LEVEL_ARG   261
 log_no_show_level command line argument
#define LOG_SHOW_FILE_ARG   262
 log_show_file command line argument
#define LOG_NO_SHOW_FILE_ARG   263
 log_no_show_file command line argument
#define LOG_SHOW_LINE_ARG   264
 log_show_line command line argument
#define LOG_NO_SHOW_LINE_ARG   265
 log_no_show_line command line argument
#define LOG_SHOW_FUNCTION_ARG   266
 log_show_function command line argument
#define LOG_NO_SHOW_FUNCTION_ARG   267
 log_no_show_function command line argument
#define LOG_SHOW_TIME_ARG   268
 log_show_time command line argument
#define LOG_NO_SHOW_TIME_ARG   269
 log_no_show_time command line argument

Enumerations

enum  prog_action_t { PROG_ACTION_MONITOR, PROG_ACTION_LIST, PROG_ACTION_FIND }
 Operational program modes, e.g. More...
enum  file_event_t { FILE_NO_CHANGE, FILE_APPENDED, FILE_REPLACED }
 Possible states of a file we're monitoring. More...

Functions

static const char * file_event_string (file_event_t event_type)
 Translate a file_event_t enumeration into a string.
static int reap_file_data (struct lwatch_path_info_t *path_info)
 The file has already been identified for reaping, perform the reap by collecting the file contents and updating our bookkeeping.
static int get_create_path_info (const char *path, struct lwatch_path_info_t *path_info)
 Given a path name lookup it's path_info, if it doesn't exist create it.
static int process_file_modification (const char *path)
 A file has been modified, record that it will need to be reaped.
static int process_file_close (const char *path)
 A file has been closed, test if it will need to be reaped and record reap status.
static int process_file_create (const char *path)
 A file has been opened, validate it's a watch target.
static int process_file_delete (const char *path)
 A file has been deleted, record that it's file contents will be different.
static int process_file_rename (const char *prev_path, const char *new_path)
 A file has been renamed, track both the old and new file contents.
static int regexp_init (void)
 Initialize our regular expressions.
static int regexp_fini (void)
 Close our regular expression usage by releasing resources.
static int lwatch_init (void)
 Initialize the log watcher.
static int lwatch_fini ()
 Close down the log watch system.
static void signal_handler (int signum)
 Respond to signals.
static const char * lwatch_event_type_string (lwatch_event_type event_type)
 Given a lwatch_event_type enumeration return it's name as a string.
static const char * lwatch_event_string (struct lwatch_event_t *event)
 Given a lwatch_event_t render it as a string.
static int lwatch_event_callback (struct lwatch_event_t *event)
 Respond to the events sent by the monitoring back end.
static int start_monitoring (const char *path_arg)
 Start monitoring a primary file.
static int compare_stat_info (struct stat *stat_info, struct lwatch_path_info_t *path_info, file_event_t *result_arg)
 Compare operating system information about a file with our saved information, return how the file's state may have transformed.
static int get_path_info_reap_candidacy (struct lwatch_path_info_t *path_info, bool *should_reap)
 Examine the file system, set parameter if the file should be reaped.
static int set_path_info_reap_candidacy (struct lwatch_path_info_t *path_info)
 Determine if watched file needs to be reaped, set internal flag accordingly.
static int update_stat_info_and_write (struct stat *cur_stat_info, struct lwatch_path_info_t *path_info)
 Merge stat information about a file into our struct and write the updated entry in the persistent database.
static int dump_database ()
 Display the contents of the persistent log watch database.
static int validate_backups_in_database ()
 Check the state of known backups since the last time we ran.
static int process_pending_reaps ()
 For each file in the database marked as needing to be reaped perform a reap.
static int main_loop ()
 Main loop when running in daemon monitor mode.
int destroy_path_info (struct lwatch_path_info_t **ppath_info)
 Release lwatch_path_info_t resources, free it's memory, set variable to NULL.
int update_stat_info (struct stat *cur_stat_info, struct lwatch_path_info_t *path_info)
 Merge stat information into path into our saved information about the file.
bool is_log_backup (const char *path)
 Given a path name return true if it appears to be a rotated backup of a primary log file.
static bool find_log_files_callback (const char *directory, const char *filename, const char *path, struct stat *info, void *user_data)
 Recursive directory walk callback used to find potential log files.
static int find_log_files (const char *dir_path, int recursive)
 Find potential log files in a directory (and below).
static void usage (void)
 Write the program usage help information.
int main (int argc, char **argv)
 Main program entry point.

Variables

struct regexp_t logrotate_numbered_backup_regexp = {"(.*/)?([^/]+)\\.(\\d+)(\\.gz|.\\bz2)?$"}
 regular expression which matches a numbered backup path name match 0 = full match match 1 = directory match 2 = basename match 3 = number match 4 = extension
struct regexp_t logrotate_date_backup_regexp = {"(.*/)?([^/]+)-((\\d{4})(\\d{2})(\\d{2}))(\\.gz|.\\bz2)?$"}
 regular expression which matches a dated backup path name match 0 = full match match 1 = directory match 2 = basename match 3 = full date match 4 = year match 5 = month match 6 = day match 7 = extension
static int reap_interval = 15
 How often to reap log file updates (number of seconds).


Detailed Description

Implements the basic watching logic for log files.

Definition in file lwatch.c.


Enumeration Type Documentation

Possible states of a file we're monitoring.

Enumerator:
FILE_NO_CHANGE  file has not changed
FILE_APPENDED  file had data appended to it
FILE_REPLACED  file had it's contents replaced

Definition at line 351 of file lwatch.c.

00351              {
00352     FILE_NO_CHANGE,             /**< file has not changed */
00353     FILE_APPENDED,              /**< file had data appended to it */
00354     FILE_REPLACED,              /**< file had it's contents replaced */
00355 } file_event_t;

Operational program modes, e.g.

what the user wants the program to do.

Enumerator:
PROG_ACTION_MONITOR  run in daemon mode monitoring files
PROG_ACTION_LIST  dump the contents of file watch database
PROG_ACTION_FIND  locate log files

Definition at line 342 of file lwatch.c.

00342              {
00343     PROG_ACTION_MONITOR,        /**< run in daemon mode monitoring files */
00344     PROG_ACTION_LIST,           /**< dump the contents of file watch database */
00345     PROG_ACTION_FIND,           /**< locate log files */
00346 } prog_action_t;


Function Documentation

static int compare_stat_info ( struct stat *  stat_info,
struct lwatch_path_info_t path_info,
file_event_t result_arg 
) [static]

Compare operating system information about a file with our saved information, return how the file's state may have transformed.

Parameters:
[in] stat_info The stat information to compare against our saved information
[in] path_info Our saved information about the file
[in] result_arg A file_event_t enumeration describing the state transformation
Returns:
SUCCESS (0) or non-zero error code otherwise
After comparing the operating system information about the file to our saved information returns whether the file has:
  • not changed
  • has been appended (data added)
  • has been replaced by new file contents

Definition at line 988 of file lwatch.c.

Referenced by get_path_info_reap_candidacy().

00989 {
00990     int error;
00991     file_event_t result;
00992 
00993     error = SUCCESS;
00994     result = FILE_NO_CHANGE;
00995 
00996     if (!(path_info->flags & STAT_DATA_VALID_FLAG)) {
00997         result = FILE_REPLACED; /* FIXME: is this really the correct state? */
00998         goto exit;
00999     }
01000 
01001     log_msg(LOG_DEBUG, _("st_size=%lu(%lu), st_dev=%llu(%llu) st_ino=%lu(%lu) st_mtime=%ld(%ld) \"%s\"\n"),
01002 
01003             stat_info->st_size,  path_info->size,
01004             stat_info->st_dev,   path_info->dev,
01005             stat_info->st_ino,   path_info->inode,
01006             stat_info->st_mtime, path_info->modification_time,
01007             path_info->path);
01008 
01009     /*
01010      * First determine if this is the same file associate with the pathname
01011      */
01012 
01013     /* If the device or inode is different it must be a different file */
01014     if ((stat_info->st_dev != path_info->dev) || (stat_info->st_ino != path_info->inode)) {
01015         result = FILE_REPLACED;
01016     }
01017 
01018     /* If the size shrunk it must be a different file since log data is appended */
01019     if (stat_info->st_size < path_info->size) {
01020         result = FILE_REPLACED;
01021     }
01022 
01023     /* If the size increased the file was appended to */
01024     if (stat_info->st_size > path_info->size) {
01025         result = FILE_APPENDED;
01026     }
01027 
01028  exit:
01029     *result_arg = result;
01030     return error;
01031 }

int destroy_path_info ( struct lwatch_path_info_t **  ppath_info  ) 

Release lwatch_path_info_t resources, free it's memory, set variable to NULL.

Parameters:
[out] ppath_info Pointer to a pointer of saved information about a file. Upon a successful return this pointer will be set to NULL.
Returns:
SUCCESS (0) or non-zero error code otherwise

Definition at line 1151 of file lwatch.c.

01152 {
01153     struct lwatch_path_info_t *path_info;
01154 
01155     if (!ppath_info) return EINVAL;
01156     path_info = *ppath_info;
01157     if (!path_info) return EINVAL;
01158 
01159     free(path_info);
01160     *ppath_info = NULL;
01161 
01162     return SUCCESS;
01163 }

static int dump_database (  )  [static]

Display the contents of the persistent log watch database.

Returns:
SUCCESS (0) or non-zero error code otherwise
Every file we've been asked to watch has state information persistently stored in a database. The persistent state storage allows us to resynchronize our state during intervals of time this daemon is not running without loss of data. This routine will pretty print the contents of the database.

Definition at line 1292 of file lwatch.c.

Referenced by main().

01293 {
01294     int error;
01295     struct database_iter_context_t *path_iter, *backup_iter;
01296     struct lwatch_path_info_t *path_info, *backup_path_info;
01297 
01298     if ((error = database_query_all_paths_iter(&path_iter, NULL)) != SUCCESS) {
01299         log_msg(LOG_ERROR, _("database_query_all_iter failed (%s)\n"), error_string(error));
01300     }
01301 
01302     while ((path_info = path_iter->next(path_iter))) {
01303         if (path_info->flags & BACKUP_FLAG)
01304             continue;
01305 
01306         printf("%s\n", path_info_string(path_info, ""));
01307 
01308         if ((error = database_query_backups_of_target_iter(&backup_iter, path_info->path)) != SUCCESS) {
01309             log_msg(LOG_ERROR, _("database_query_backups_of_target_iter failed (%s)\n"), error_string(error));
01310         }
01311 
01312         while ((backup_path_info = backup_iter->next(backup_iter))) {
01313             printf("%s\n", path_info_string(backup_path_info, "    "));
01314         }
01315         free(backup_iter);
01316     }
01317     free(path_iter);
01318 
01319     return SUCCESS;
01320 }

static const char * file_event_string ( file_event_t  event_type  )  [static]

Translate a file_event_t enumeration into a string.

Parameters:
[in] event_type The event type enumeartion whose string representation is sought.
Returns:
Pointer to thread local static string

Definition at line 441 of file lwatch.c.

Referenced by get_path_info_reap_candidacy().

00442 {
00443     static __thread char buf[80]; /* thread local static buffer */
00444 
00445     switch(event_type) {
00446     case FILE_NO_CHANGE: return _("No change");
00447     case FILE_APPENDED:  return _("Appended");
00448     case FILE_REPLACED:  return _("Replaced");
00449     default:
00450         snprintf(buf, sizeof(buf), "unknown(%d)", event_type);
00451         return buf;
00452     }
00453 
00454 }

static int find_log_files ( const char *  dir_path,
int  recursive 
) [static]

Find potential log files in a directory (and below).

Parameters:
[in] dir_path Directory to search (from)
[in] recursive True if the search should recursively descend into other directories.
Returns:
SUCCESS (0) or non-zero error code otherwise
Starting at the specified directory and emit the full pathname of every file found which has the potential of being a primary log file. If recursive is true then descend into sub-directories as well. The test is performed by examing the pathname of the file. Files which appear to be backups of a primary log file (i.e. the result of log file rotation) are ignored.
See also:
find_log_files_callback()

Definition at line 1605 of file lwatch.c.

Referenced by main().

01606 {
01607     int error;
01608     char path[PATH_MAX];
01609     int count;
01610 
01611     if ((error = make_normalized_absolute_path(path, sizeof(path), dir_path)) != SUCCESS) {
01612         log_msg(LOG_ERROR, _("failed make_normalized_absolute_path from \"%s\" (%s)\n"),
01613                 dir_path, error_string(error));
01614         return error;
01615     }
01616 
01617     count = 0;
01618     directory_list(path, recursive, find_log_files_callback, &count);
01619 
01620     return SUCCESS;
01621 }

static bool find_log_files_callback ( const char *  directory,
const char *  filename,
const char *  path,
struct stat *  info,
void *  user_data 
) [static]

Recursive directory walk callback used to find potential log files.

Parameters:
[in] directory The current directory during the walk
[in] filename The basename of the current directory entry
[in] path The full pathname of the current directory entry (e.g. directory/filename)
[in] info stat struct providing information about the directory entry
[in] user_data Pointer to a count of found files
Returns:
True to keep walking
This callback is invoked for every directory entry found while walking a directory tree. We want to locate potential log files and report them to the user. To be considered a log file the directory entry must:
  • must be a regular file or a link
  • not be a log backup (based on the path name) otherwise the file is reported as a potential log file

Definition at line 1577 of file lwatch.c.

Referenced by find_log_files().

01578 {
01579     int *count = (int *)user_data;
01580 
01581     if (!(S_ISREG(info->st_mode) || S_ISLNK(info->st_mode))) return true;
01582 
01583     if (is_log_backup(path)) return true;
01584 
01585     printf("%s\n", path);
01586     (*count)++;
01587 
01588     return true;
01589 }

static int get_create_path_info ( const char *  path,
struct lwatch_path_info_t path_info 
) [static]

Given a path name lookup it's path_info, if it doesn't exist create it.

Parameters:
[in] path The path name to lookup.
[out] path_info Pointer to path_info which will be initialized with result.
Returns:
SUCCESS (0) or non-zero error code otherwise

Definition at line 463 of file lwatch.c.

Referenced by start_monitoring().

00464 {
00465     int error;
00466 
00467     error = database_lookup_path_info(path, path_info);
00468     if ((error != SUCCESS) && (error != WATCH_DATABASE_ERROR_NOT_FOUND)) {
00469         log_msg(LOG_ERROR, _("database_lookup_path_info failed \"%s\" (%s)\n"),
00470                 path, error_string(error));
00471         return error;
00472     }
00473 
00474     /* Did not exist in database, create a new path_info, intialize it, write it into the database */
00475     if (error == WATCH_DATABASE_ERROR_NOT_FOUND) {
00476         if ((error = init_path_info(path_info, path)) != SUCCESS) {
00477             log_msg(LOG_ERROR, _("could not initialize path_info for path \"%s\" (%s)\n"),
00478                     path, error_string(error));
00479             return error;
00480         }
00481 
00482         if ((error = database_write_path_info(path_info)) != SUCCESS) {
00483             log_msg(LOG_ERROR, _("could not insert path_info into database for \"%s\" (%s)\n"),
00484                     path_info->path, error_string(error));
00485             return error;
00486         }
00487     }
00488 
00489     return SUCCESS;
00490 }

static int get_path_info_reap_candidacy ( struct lwatch_path_info_t path_info,
bool *  should_reap 
) [static]

Examine the file system, set parameter if the file should be reaped.

Parameters:
[in] path_info Our saved information about the file
[out] should_reap Boolean result returned here
Returns:
SUCCESS (0) or non-zero error code otherwise
Examine the state of the file in the file system. Compare it to our information about the file. If there is information about the file or in the file we need to collect (e.g. reap) then set the should_reap flag to true, false otherwise.

Definition at line 1045 of file lwatch.c.

Referenced by set_path_info_reap_candidacy().

01046 {
01047     int error;
01048     struct stat cur_stat_info;
01049     file_event_t event_type;
01050 
01051     *should_reap = false;
01052 
01053     if (stat(path_info->path, &cur_stat_info) < 0) {
01054         error = errno;
01055         if (error == ENOENT) {  /* file does not exist, not an error, just clear flag */
01056             return SUCCESS;
01057         }
01058         log_msg(LOG_ERROR, _("cannot perform stat on \"%s\" (%s) \n"),
01059                 path_info->path, error_string(error));
01060         return error;
01061     }
01062 
01063     if ((error = compare_stat_info(&cur_stat_info, path_info, &event_type)) != SUCCESS) {
01064         log_msg(LOG_ERROR, _("compare_stat_info failed \"%s\" (%s) \n"),
01065                 path_info->path, error_string(error));
01066         return error;
01067     }
01068 
01069     log_msg(LOG_DEBUG, _("compare_stat_info(%s) returns %s\n"),
01070             path_info->path, file_event_string(event_type));
01071 
01072 
01073     switch(event_type) {
01074     case FILE_NO_CHANGE:
01075         break;
01076     case FILE_APPENDED:
01077     case FILE_REPLACED:
01078         *should_reap = true;
01079         break;
01080     default:
01081         log_msg(LOG_ERROR, _("unknown event type = %d\n"),  event_type);
01082         return EINVAL;
01083     }
01084 
01085     return SUCCESS;
01086 }

bool is_log_backup ( const char *  path  ) 

Given a path name return true if it appears to be a rotated backup of a primary log file.

Parameters:
[in] path The path name to test.
Returns:
SUCCESS (0) or non-zero error code otherwise
By convention backing up log files is done via a process called rotation where the current version of a (primary) log file is renamed to a backup name and the primary log file is reopened. Usually the last N copies of the backup are kept, backup.1 is moved to backup.2, backup.2 is moved to backup.3, and backup.N+1 is discarded (hence the notion of rotation). Also by convention the name of backup files is the name of the primary file with a rotation number appended, or the date of the rotation appended.

This routine uses regular expressions to intuit from a pathname if the pathname appears to follow a rotation backup naming convention. Only the pathname is considered, the file contents are not examined.

Currently there are two rotation backup naming conventions which are looked for, both are supported by the logrotate facility found on many machines.

  • numbered backups
  • dated backups

See also:
logrotate_numbered_backup_regexp

logrotate_date_backup_regexp

Definition at line 1229 of file lwatch.c.

Referenced by find_log_files_callback(), and start_monitoring().

01230 {
01231     int num_matches;
01232 
01233     if ((num_matches = regexp_search(&logrotate_numbered_backup_regexp, path)) > 0) {
01234 # if 0
01235         // FIXME: currently we don't use the sub-fields, this code is left as an
01236         // example of how to do that
01237 
01238         char directory[PATH_MAX];
01239         char filename[PATH_MAX];
01240         char number[9];
01241         char extension[PATH_MAX];
01242 
01243         regexp_substring(&logrotate_numbered_backup_regexp, 1, directory, sizeof(directory));
01244         regexp_substring(&logrotate_numbered_backup_regexp, 2, filename, sizeof(filename));
01245         regexp_substring(&logrotate_numbered_backup_regexp, 3, number, sizeof(number));
01246         regexp_substring(&logrotate_numbered_backup_regexp, 4, extension, sizeof(extension));
01247 
01248         if (0) printf(" num_matches=%d directory=\"%s\" filename=\"%s\" number=\"%s\" extension=\"%s\" ",
01249                       num_matches, directory, filename, number, extension);
01250 #endif
01251         return true;
01252     }
01253     if ((num_matches = regexp_search(&logrotate_date_backup_regexp, path)) > 0) {
01254 # if 0
01255         // FIXME: currently we don't use the sub-fields, this code is left as an
01256         // example of how to do that
01257 
01258         char directory[PATH_MAX];
01259         char filename[PATH_MAX];
01260         char date[9];
01261         char year[5];
01262         char month[3];
01263         char day[3];
01264         char extension[PATH_MAX];
01265 
01266         regexp_substring(&logrotate_date_backup_regexp, 1, directory, sizeof(directory));
01267         regexp_substring(&logrotate_date_backup_regexp, 2, filename, sizeof(filename));
01268         regexp_substring(&logrotate_date_backup_regexp, 3, date, sizeof(date));
01269         regexp_substring(&logrotate_date_backup_regexp, 4, year, sizeof(year));
01270         regexp_substring(&logrotate_date_backup_regexp, 5, month, sizeof(month));
01271         regexp_substring(&logrotate_date_backup_regexp, 6, day, sizeof(day));
01272         regexp_substring(&logrotate_date_backup_regexp, 7, extension, sizeof(extension));
01273 
01274         if (0) printf(" num_matches=%d directory=\"%s\" filename=\"%s\" date=\"%s\" year=\"%s\" month=\"%s\" day=\"%s\" extension=\"%s\" ",
01275                       num_matches, directory, filename, date, year, month, day, extension);
01276 #endif
01277         return true;
01278     }
01279     return false;
01280 }

static int lwatch_event_callback ( struct lwatch_event_t event  )  [static]

Respond to the events sent by the monitoring back end.

Parameters:
[in] event The lwatch_event_t to process
Returns:
SUCCESS (0) or non-zero error code otherwise

Definition at line 864 of file lwatch.c.

00865 {
00866     int error;
00867 
00868     log_msg(LOG_DEBUG, _("event: %s\n"),  lwatch_event_string(event));
00869 
00870     switch(event->event_type) {
00871     case LWATCH_EVENT_CREATE:
00872         if ((error = process_file_create(event->path)) != SUCCESS) {
00873             log_msg(LOG_ERROR, _("process_file_create failed for \"%s\" (%s)\n"),
00874                     event->path, error_string(error));
00875         }
00876         break;
00877     case LWATCH_EVENT_OPEN:
00878         break;
00879     case LWATCH_EVENT_DELETE:
00880         if ((error = process_file_delete(event->path)) != SUCCESS) {
00881             log_msg(LOG_ERROR, _("process_file_delete failed for \"%s\" (%s)\n"),
00882                     event->path, error_string(error));
00883         }
00884         break;
00885     case LWATCH_EVENT_RENAME:
00886         if ((error = process_file_rename(event->rename.prev_path, event->rename.new_path)) != SUCCESS) {
00887             log_msg(LOG_ERROR, _("process_file_rename failed for \"%s\" --> \"%s\" (%s)\n"),
00888                     event->rename.prev_path, event->rename.new_path, error_string(error));
00889         }
00890         break;
00891     case LWATCH_EVENT_MODIFY:
00892         if ((error = process_file_modification(event->path)) != SUCCESS) {
00893             log_msg(LOG_ERROR, _("process_file_modification failed for \"%s\" (%s)\n"),
00894                     event->path, error_string(error));
00895         }
00896         break;
00897     case LWATCH_EVENT_CLOSE:
00898         if ((error = process_file_close(event->path)) != SUCCESS) {
00899             log_msg(LOG_ERROR, _("process_file_close failed for \"%s\" (%s)\n"),
00900                     event->path, error_string(error));
00901         }
00902         break;
00903     default:
00904         log_msg(LOG_ERROR, _("unknown watch event type \"%s\"\n"),  lwatch_event_string(event));
00905         return EINVAL;
00906     }
00907 
00908     return SUCCESS;
00909 }

static const char * lwatch_event_string ( struct lwatch_event_t event  )  [static]

Given a lwatch_event_t render it as a string.

Parameters:
[in] event The lwatch_event_t whose string representation is desired
Returns:
Pointer to thread local static string.

Definition at line 829 of file lwatch.c.

Referenced by lwatch_event_callback().

00830 {
00831     static __thread char buf[512 + PATH_MAX * 2]; /* thread local static buffer */
00832     char *p, *buf_end;
00833 
00834     p = buf;                     /* current position in buffer */
00835     buf_end = &buf[sizeof(buf)]; /* non-inclusive end of buffer */
00836     *p = 0;
00837 
00838     p += snprintf(p, buf_end - p, "[%s] ", lwatch_event_type_string(event->event_type));
00839     if (p >= buf_end) goto fail;
00840     if (event->event_type == LWATCH_EVENT_RENAME) {
00841         p += snprintf(p, buf_end - p, "prev_path=\"%s\" ", event->rename.prev_path);
00842         if (p >= buf_end) goto fail;
00843         p += snprintf(p, buf_end - p, "new_path=\"%s\"", event->rename.new_path);
00844         if (p >= buf_end) goto fail;
00845     } else {
00846         p += snprintf(p, buf_end - p, "path=\"%s\"", event->path);
00847         if (p >= buf_end) goto fail;
00848     }
00849 
00850     return buf;
00851 
00852  fail:
00853     /* exhausted the buffer; NULL terminate then return truncated string */
00854     buf_end[-1] = 0;   /* should already be NULL terminated, but be safe */
00855     return buf;
00856 }

static const char * lwatch_event_type_string ( lwatch_event_type  event_type  )  [static]

Given a lwatch_event_type enumeration return it's name as a string.

Parameters:
[in] event_type The event type enumeration who name is desired.
Returns:
Pointer to thread local static string.

Definition at line 806 of file lwatch.c.

Referenced by lwatch_event_string().

00807 {
00808     static __thread char buf[80]; /* thread local static buffer */
00809 
00810     switch(event_type) {
00811     case LWATCH_EVENT_CREATE:      return "Created";
00812     case LWATCH_EVENT_DELETE:      return "Deleted";
00813     case LWATCH_EVENT_RENAME:      return "Renamed";
00814     case LWATCH_EVENT_MODIFY:      return "Modified";
00815     case LWATCH_EVENT_OPEN:        return "Opened";
00816     case LWATCH_EVENT_CLOSE:       return "Closed";
00817     default:
00818         snprintf(buf, sizeof(buf), "unknown(%d)", event_type);
00819         return buf;
00820     }
00821 }

static int lwatch_fini (  )  [static]

Close down the log watch system.

Returns:
SUCCESS (0) or non-zero error code otherwise

Definition at line 767 of file lwatch.c.

Referenced by main().

00768 {
00769     int error;
00770 
00771     if ((error = inotify_watch_fini()) != SUCCESS) {
00772         log_msg(LOG_ERROR, _("inotify_watch_fini() failed (%s)\n"),  error_string(error));
00773     }
00774 
00775     return SUCCESS;
00776 }

static int lwatch_init ( void   )  [static]

Initialize the log watcher.

Returns:
SUCCESS (0) or non-zero error code otherwise
  • Initialize the monitoring backend
  • Check to see what changed since the last time we ran

Definition at line 735 of file lwatch.c.

Referenced by main().

00736 {
00737     int error;
00738 
00739     signal(SIGHUP,  signal_handler);
00740     signal(SIGINT,  signal_handler);
00741     signal(SIGQUIT, signal_handler);
00742 
00743     keep_watching = true;
00744 
00745     /* Initialize the monitoring backend */
00746     if ((error = inotify_watch_init()) != SUCCESS) {
00747         log_msg(LOG_ERROR, _("inotify_watch_init() failed (%s)\n"),  error_string(error));
00748         return error;
00749     }
00750     /* Connect ourself to the backend via a callback */
00751     register_lwatch_event_callback(lwatch_event_callback);
00752 
00753     /* Check to see what changed since the last time we ran */
00754     if ((error = validate_backups_in_database()) != SUCCESS) {
00755         log_msg(LOG_ERROR, _("validate_backups_in_database() failed (%s)\n"),  error_string(error));
00756         return error;
00757     }
00758 
00759     return SUCCESS;
00760 }

int main ( int  argc,
char **  argv 
)

Main program entry point.

Parameters:
[in] argc Count of how many program arguments
[in] argv Array of program argument strings
Returns:
Exit code passed back to invoking environment.
Behavior is:
  • Initialize logging
  • Process program arguments
  • Determine which operation mode to run in
    • monitor
    • find
    • list
  • Initialize sub-systems
  • Execute in requested mode
  • Close sub-systems
  • Exit

Definition at line 1676 of file lwatch.c.

01677 {
01678     int error;
01679     int i;
01680     prog_action_t prog_action = PROG_ACTION_MONITOR;
01681     bool recursive = false;
01682     int log_level = LOG_WARN;
01683     char *logfile_path = NULL;
01684 
01685     log_set_show_level(true);
01686     log_set_show_file(false);
01687     log_set_show_line(false);
01688     log_set_show_function(true);
01689     log_set_show_time(false);
01690 
01691 
01692     while (1) {
01693         int arg;
01694         int option_index = 0;
01695         static struct option long_options[] = {
01696             {"help",                 0, 0, 'h'},
01697             {"find",                 0, 0, 'f'},
01698             {"recursive",            0, 0, 'r'},
01699             {"list",                 0, 0, 'l'},
01700             {"interval",             1, 0, 'i'},
01701             {"log_level",            1, 0, LOG_LEVEL_ARG},
01702             {"log_to_console",       0, 0, LOG_TO_CONSOLE_ARG},
01703             {"logfile_path",         0, 0, LOGFILE_PATH_ARG},
01704             {"no_logging_to_file",   0, 0, NO_LOGGING_TO_FILE_ARG},
01705             {"log_show_level",       0, 0, LOG_SHOW_LEVEL_ARG},
01706             {"log_no_show_level",    0, 0, LOG_NO_SHOW_LEVEL_ARG},
01707             {"log_show_file",        0, 0, LOG_SHOW_FILE_ARG},
01708             {"log_no_show_file",     0, 0, LOG_NO_SHOW_FILE_ARG},
01709             {"log_show_line",        0, 0, LOG_SHOW_LINE_ARG},
01710             {"log_no_show_line",     0, 0, LOG_NO_SHOW_LINE_ARG},
01711             {"log_show_function",    0, 0, LOG_SHOW_FUNCTION_ARG},
01712             {"log_no_show_function", 0, 0, LOG_NO_SHOW_FUNCTION_ARG},
01713             {"log_show_time",        0, 0, LOG_SHOW_TIME_ARG},
01714             {"log_no_show_time",     0, 0, LOG_NO_SHOW_TIME_ARG},
01715             {0, 0, 0, 0}
01716         };
01717 
01718         arg = getopt_long(argc, argv, "hfrli:",
01719                           long_options, &option_index);
01720         if (arg == -1) break;
01721 
01722         switch (arg) {
01723         case 'h':
01724             usage();
01725             exit(0);
01726             break;
01727         case 'f':
01728             prog_action = PROG_ACTION_FIND;
01729             break;
01730         case 'r':
01731             recursive = true;
01732             break;
01733         case 'l':
01734             prog_action = PROG_ACTION_LIST;
01735             break;
01736         case 'i':
01737             reap_interval = atoi(optarg);
01738             break;
01739         case LOG_LEVEL_ARG:
01740             if (log_level_from_string(optarg, &log_level) != SUCCESS) {
01741                 fprintf(stderr, _("unknown log level: %s\n"), optarg);
01742             }
01743             break;
01744         case LOG_TO_CONSOLE_ARG:
01745             log_to_console(true);
01746             break;
01747         case LOGFILE_PATH_ARG:
01748             logfile_path = optarg;
01749             break;
01750         case NO_LOGGING_TO_FILE_ARG:
01751             log_to_file(false);
01752             break;
01753 
01754         case LOG_SHOW_LEVEL_ARG:
01755             log_set_show_level(true);
01756             break;
01757         case LOG_NO_SHOW_LEVEL_ARG:
01758             log_set_show_level(false);
01759             break;
01760         case LOG_SHOW_FILE_ARG:
01761             log_set_show_file(true);
01762             break;
01763         case LOG_NO_SHOW_FILE_ARG:
01764             log_set_show_file(false);
01765             break;
01766         case LOG_SHOW_LINE_ARG:
01767             log_set_show_line(true);
01768             break;
01769         case LOG_NO_SHOW_LINE_ARG:
01770             log_set_show_line(false);
01771             break;
01772         case LOG_SHOW_FUNCTION_ARG:
01773             log_set_show_function(true);
01774             break;
01775         case LOG_NO_SHOW_FUNCTION_ARG:
01776             log_set_show_function(false);
01777             break;
01778         case LOG_SHOW_TIME_ARG:
01779             log_set_show_time(true);
01780             break;
01781         case LOG_NO_SHOW_TIME_ARG:
01782             log_set_show_time(false);
01783             break;
01784 
01785         default:
01786             usage();
01787             exit(1);
01788             break;
01789         }
01790     }
01791 
01792     log_set_filepath(logfile_path);
01793     log_set_level(log_level);
01794     if ((error = log_init()) != SUCCESS) {
01795         fprintf(stderr, _("log initialization failed (%s)\n"), error_string(error));
01796         return 1;
01797     }
01798 
01799     if ((error = regexp_init()) != SUCCESS) {
01800         log_msg(LOG_ERROR, _("regexp initialization failed (%s)\n"), error_string(error));
01801         return 1;
01802     }
01803 
01804     if ((error = watch_database_init()) != SUCCESS) {
01805         log_msg(LOG_ERROR, _("watch_database initialization failed (%s)\n"), error_string(error));
01806         return 1;
01807     }
01808 
01809     if ((error = lwatch_init()) != SUCCESS) {
01810         log_msg(LOG_ERROR, _("lwatch initialization failed (%s)\n"), error_string(error));
01811         return 1;
01812     }
01813 
01814     switch (prog_action) {
01815     case PROG_ACTION_MONITOR:
01816         for (i = optind; i < argc; i++) {
01817             char *path = argv[i];
01818 
01819             if ((error = start_monitoring(path)) != SUCCESS) {
01820                 log_msg(LOG_ERROR, _("could not monitor \"%s\" (%s)\n"),
01821                         path, error_string(error));
01822             }
01823         }
01824 
01825         main_loop();
01826 
01827         /* Before we exit assure we reap any outstanding data which might still be pending */
01828         if ((error = process_pending_reaps()) != SUCCESS) {
01829             log_msg(LOG_ERROR, _("error processing pending reaps (%s)\n"),
01830                     error_string(error));
01831         }
01832 
01833         break;
01834     case PROG_ACTION_FIND:
01835         for (i = optind; i < argc; i++) {
01836             char *path = argv[i];
01837 
01838             if ((error = find_log_files(path, recursive)) != SUCCESS) {
01839                 log_msg(LOG_ERROR, _("could not find log files \"%s\" (%s)\n"),
01840                         path, error_string(error));
01841             }
01842         }
01843         break;
01844     case PROG_ACTION_LIST:
01845         dump_database();
01846         break;
01847     }
01848 
01849     if ((error = lwatch_fini()) != SUCCESS) {
01850         log_msg(LOG_ERROR, _("lwatch finalization failed (%s)\n"), error_string(error));
01851     }
01852 
01853     if ((error = regexp_fini()) != SUCCESS) {
01854         log_msg(LOG_ERROR, _("regexp finalization failed (%s)\n"), error_string(error));
01855     }
01856 
01857     if ((error = watch_database_fini()) != SUCCESS) {
01858         log_msg(LOG_ERROR, _("watch_database finalization failed (%s)\n"), error_string(error));
01859     }
01860 
01861     if ((error = log_fini()) != SUCCESS) {
01862         fprintf(stderr, _("log finalization failed (%s)\n"), error_string(error));
01863     }
01864 
01865     return 0;
01866 }

static int main_loop (  )  [static]

Main loop when running in daemon monitor mode.

Returns:
SUCCESS (0) or non-zero error code otherwise
Enter a loop which keeps processing events until the keep_watching global flag is set to false. The action of the event loop is:
  • read event from inotify backend and process it.
  • if there are no events to process sleep until the reap_interval expires then wake up and process any pending reaps which were queued.

Definition at line 1518 of file lwatch.c.

Referenced by main().

01519 {
01520     int error;
01521     int result;
01522     fd_set read_fds;
01523     int inotify_fd;
01524     struct timeval timeout;
01525 
01526     inotify_fd = get_inotify_fd();
01527 
01528     timeout.tv_sec = reap_interval;
01529     timeout.tv_usec = 0;
01530 
01531     while (keep_watching) {
01532         FD_ZERO(&read_fds);
01533         FD_SET(inotify_fd, &read_fds);
01534 
01535         result = select(inotify_fd + 1, &read_fds, NULL, NULL, &timeout);
01536         if (result == -1) {     /* error */
01537             error = errno;
01538             if (error != EINTR) {
01539                 log_msg(LOG_ERROR, _("select error (%s)\n"),
01540                         error_string(error));
01541             }
01542         } else if (result) {    /* data available */
01543             read_events();
01544         } else {                /* timeout */
01545             /* The timeout expired, it needs to be reset */
01546             timeout.tv_sec = reap_interval;
01547             timeout.tv_usec = 0;
01548 
01549             if ((error = process_pending_reaps()) != SUCCESS) {
01550                 log_msg(LOG_ERROR, _("error processing pending reaps (%s)\n"),
01551                         error_string(error));
01552             }
01553         }
01554     }
01555 
01556     return SUCCESS;
01557 }

static int process_file_close ( const char *  path  )  [static]

A file has been closed, test if it will need to be reaped and record reap status.

Parameters:
[in] path Path name of the watch target to take action on.
Returns:
SUCCESS (0) or non-zero error code otherwise

Definition at line 526 of file lwatch.c.

Referenced by lwatch_event_callback().

00527 {
00528     int error;
00529     struct lwatch_path_info_t path_info;
00530 
00531     if ((error = database_lookup_path_info(path, &path_info)) != SUCCESS) {
00532         log_msg(LOG_ERROR, _("database_lookup_path_info failed \"%s\" (%s)\n"),
00533                 path, error_string(error));
00534         return LWATCH_ERROR_CANNOT_FIND_PATH_INFO;
00535     }
00536 
00537     if ((error = set_path_info_reap_candidacy(&path_info)) != SUCCESS) {
00538         log_msg(LOG_ERROR, _("get_path_info_reap_candidacy failed \"%s\" (%s)\n"),
00539                 path, error_string(error));
00540         return error;
00541     }
00542 
00543     if ((error = database_write_path_info(&path_info)) != SUCCESS) {
00544         log_msg(LOG_ERROR, _("could not insert path_info into database for \"%s\" (%s)\n"),
00545                 path_info.path, error_string(error));
00546         return error;
00547     }
00548 
00549     return SUCCESS;
00550 }

static int process_file_create ( const char *  path  )  [static]

A file has been opened, validate it's a watch target.

Parameters:
[in] path Path name of the watch target to take action on.
Returns:
SUCCESS (0) or non-zero error code otherwise

Definition at line 558 of file lwatch.c.

Referenced by lwatch_event_callback().

00559 {
00560     int error;
00561     struct lwatch_path_info_t path_info;
00562 
00563     if ((error = database_lookup_path_info(path, &path_info)) != SUCCESS) {
00564         log_msg(LOG_ERROR, _("database_lookup_path_info failed \"%s\" (%s)\n"),
00565                 path, error_string(error));
00566         return LWATCH_ERROR_CANNOT_FIND_PATH_INFO;
00567     }
00568 
00569     return SUCCESS;
00570 }

static int process_file_delete ( const char *  path  )  [static]

A file has been deleted, record that it's file contents will be different.

Parameters:
[in] path Path name of the watch target to take action on.
Returns:
SUCCESS (0) or non-zero error code otherwise

Definition at line 578 of file lwatch.c.

Referenced by lwatch_event_callback().

00579 {
00580     int error;
00581     struct lwatch_path_info_t path_info;
00582 
00583     if ((error = database_lookup_path_info(path, &path_info)) != SUCCESS) {
00584         log_msg(LOG_ERROR, _("database_lookup_path_info failed \"%s\" (%s)\n"),
00585                 path, error_string(error));
00586         return LWATCH_ERROR_CANNOT_FIND_PATH_INFO;
00587     }
00588 
00589     PATH_INFO_FLAG_SET_NEW_FILE(&path_info);
00590 
00591     if ((error = database_write_path_info(&path_info)) != SUCCESS) {
00592         log_msg(LOG_ERROR, _("could not insert path_info into database for \"%s\" (%s)\n"),
00593                 path_info.path, error_string(error));
00594         return error;
00595     }
00596 
00597 
00598     return SUCCESS;
00599 }

static int process_file_modification ( const char *  path  )  [static]

A file has been modified, record that it will need to be reaped.

Parameters:
[in] path Path name of the watch target to take action on.
Returns:
SUCCESS (0) or non-zero error code otherwise

Definition at line 498 of file lwatch.c.

Referenced by lwatch_event_callback().

00499 {
00500     int error;
00501     struct lwatch_path_info_t path_info;
00502 
00503     if ((error = database_lookup_path_info(path, &path_info)) != SUCCESS) {
00504         log_msg(LOG_ERROR, _("database_lookup_path_info failed \"%s\" (%s)\n"),
00505                 path, error_string(error));
00506         return LWATCH_ERROR_CANNOT_FIND_PATH_INFO;
00507     }
00508 
00509     path_info.flags |= NEEDS_REAPING_FLAG;
00510 
00511     if ((error = database_write_path_info(&path_info)) != SUCCESS) {
00512         log_msg(LOG_ERROR, _("could not insert path_info into database for \"%s\" (%s)\n"),
00513                 path_info.path, error_string(error));
00514         return error;
00515     }
00516 
00517     return SUCCESS;
00518 }

static int process_file_rename ( const char *  prev_path,
const char *  new_path 
) [static]

A file has been renamed, track both the old and new file contents.

Parameters:
[in] prev_path Previous path name of the watch target
[in] new_path New path name of the watch target
Returns:
SUCCESS (0) or non-zero error code otherwise
When a log file we've been watching is renamed we make the assumption it has become a backup file (e.g. it's been rotated). It's file contents are no less important to us, we must reap the file and we must still watch the file for modifications. Why do we need to continue to watch a file which has been renamed, doesn't the fact it's been rotated mean it won't be written to any more because the file has been retired and is no longer the active log file? Unfortunately this is not the case. In UNIX a file can be renamed while it is open because files are identified by their inode, not their path name. Log rotations are often performed by a process other than the process who has the file open for writing. The process responsible for rotating files (e.g. logrotate) renames the file independent of the process writing the file. It may also signal the process writing the file it needs to close and reopen it's files (e.g. HUP), or it may simply wait for the process writing the file to close and reopen at it's convenience. All of this is asynchronous, there is no way for us to know at the moment a file is renamed if data will ever be added to it even though it's no longer the name of the "watched log file". It is critical we do not lose any data therefore we have to assume a rotated backup file has equal status with the primary file thus we establish a full watch on the both the old and new versions of the file being careful to record the lineage so we know how the backup file relates to the primary file. This is also one of the moments when we have to record the fact the primary file will need to have it's version number incremented.

Definition at line 631 of file lwatch.c.

Referenced by lwatch_event_callback().

00632 {
00633     int error;
00634     struct lwatch_path_info_t prev_pathinfo, new_pathinfo;
00635 
00636     log_msg(LOG_DEBUG, _("\"%s\" --> \"%s\"\n"),  prev_path, new_path);
00637 
00638     /*
00639      * The backend used to watch file events may not be capable of knowing both
00640      * the previous and new path names thus we have to be prepared for one of
00641      * the two names being empty, we call this a lost rename (no rename event
00642      * will be generated if neither is known). Logically it should be impossible
00643      * for the previous name to be lost because we had a watch established on
00644      * it, if the previous name is lost we consider this a serious
00645      * error. However we might not know what the watched file was renamed to.
00646      * A lost new name is not considered a serious error, it's just an
00647      * unfortunate lack of information, but we need to be prepared for it.
00648      */
00649 
00650     if (!prev_path[0]) {
00651         log_msg(LOG_ERROR, _("%s: previous path is empty, should have been a file in the set of watched files\n"));
00652         return LWATCH_ERROR_LOST_RENAME;
00653     }
00654 
00655     if ((error = database_lookup_path_info(prev_path, &prev_pathinfo)) != SUCCESS) {
00656         log_msg(LOG_ERROR, _("database_lookup_path_info failed \"%s\" (%s)\n"),
00657                 prev_path, error_string(error));
00658         return LWATCH_ERROR_CANNOT_FIND_PATH_INFO;
00659     }
00660 
00661     if (new_path[0]) {          /* new path is known */
00662 
00663         /* copy the paths, mark it as a backup, save the path_info, monitor the new file */
00664         new_pathinfo = prev_pathinfo;
00665         if ((error = copy_path(new_pathinfo.path, new_path, sizeof(new_pathinfo.path))) != SUCCESS) {
00666             log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), new_path, error_string(error));
00667             return error;
00668         }
00669         if ((error = copy_path(new_pathinfo.original_path, prev_path, sizeof(new_pathinfo.original_path))) != SUCCESS) {
00670             log_msg(LOG_ERROR, _("failed to copy path \"%s\" (%s)\n"), prev_path, error_string(error));
00671             return error;
00672         }
00673         PATH_INFO_FLAG_SET_BACKUP(&new_pathinfo);
00674 
00675         if ((error = database_write_path_info(&new_pathinfo)) != SUCCESS) {
00676             log_msg(LOG_ERROR, _("could not insert path_info into database for \"%s\" (%s)\n"),
00677                     new_pathinfo.path, error_string(error));
00678         }
00679 
00680         if ((error = inotify_start_monitoring(new_path)) != SUCCESS) {
00681             log_msg(LOG_ERROR, _("could not inotify_start_monitoring for \"%s\" (%s)\n"),
00682                     new_path, error_string(error));
00683         }
00684     }
00685 
00686     /* Record the primary file will be a new file */
00687     prev_pathinfo.original_path[0] = 0;
00688     PATH_INFO_FLAG_SET_NEW_FILE(&prev_pathinfo);
00689     prev_pathinfo.reap_time = 0;
00690     prev_pathinfo.reap_position = 0;
00691 
00692     if ((error = database_write_path_info(&prev_pathinfo)) != SUCCESS) {
00693         log_msg(LOG_ERROR, _("could not insert path_info into database for \"%s\" (%s)\n"),
00694                 prev_pathinfo.path, error_string(error));
00695         return error;
00696     }
00697 
00698     return SUCCESS;
00699 }

static int process_pending_reaps (  )  [static]

For each file in the database marked as needing to be reaped perform a reap.

Returns:
SUCCESS (0) or non-zero error code otherwise
When we detect a file needs to be reaped we defer the actual reap operation for efficiency reasons. For example if a log file is being constantly written to we will get notified upon each write operation. We don't want to reap every individual write to the file, rather it's more efficient to wait an interval of time (
See also:
reap_interval) and collect all the new data and transmit it in a single operation. This is much more foregiving of system resources.

Definition at line 1426 of file lwatch.c.

Referenced by main(), and main_loop().

01427 {
01428     int error;
01429     struct database_iter_context_t *iter;
01430     struct lwatch_path_info_t *path_info;
01431 
01432     log_msg(LOG_DEBUG, _("process_pending_reaps\n"));
01433 
01434     if ((error = database_query_pending_reaps_iter(&iter)) != SUCCESS) {
01435         log_msg(LOG_ERROR, _("database_query_pending_reaps_iter failed (%s)\n"),
01436                 error_string(error));
01437         return error;
01438     }
01439 
01440     while ((path_info = iter->next(iter))) {
01441         if ((error = reap_file_data(path_info)) != SUCCESS) {
01442             log_msg(LOG_ERROR, _("cannot reap file data for \"%s\" (%s)\n"),
01443                     path_info->path, error_string(error));
01444         }
01445     }
01446     free(iter);
01447 
01448     return SUCCESS;
01449 }

static int reap_file_data ( struct lwatch_path_info_t path_info  )  [static]

The file has already been identified for reaping, perform the reap by collecting the file contents and updating our bookkeeping.

Parameters:
[in] path_info Our saved information about the file
Returns:
SUCCESS (0) or non-zero error code otherwise

Definition at line 1458 of file lwatch.c.

Referenced by process_pending_reaps().

01459 {
01460     int error;
01461     struct stat stat_info;
01462     const char *path;
01463     size_t length;
01464 
01465     path = path_info->path;
01466     if (stat(path, &stat_info) < 0) {
01467         error = errno;
01468         log_msg(LOG_ERROR, _("cannot perform stat on \"%s\" (%s) \n"),
01469                 path, error_string(error));
01470 
01471         if (error == ENOENT) {  /* path does not exist, report error, remove reap request */
01472             int write_error;
01473 
01474             log_msg(LOG_ERROR, _("deleted \"%s\" before being fully collected\n"), path);
01475 
01476             path_info->flags &= ~NEEDS_REAPING_FLAG;
01477 
01478             if ((write_error = database_write_path_info(path_info)) != SUCCESS) {
01479                 log_msg(LOG_ERROR, _("could not insert path_info into database for \"%s\" (%s)\n"),
01480                         path_info->path, error_string(write_error));
01481             }
01482         }
01483         return error;
01484     }
01485 
01486     if (path_info->flags & INC_VERSION_FLAG) {
01487         path_info->version++;
01488         path_info->flags &= ~INC_VERSION_FLAG;
01489     }
01490 
01491     length = stat_info.st_size - path_info->reap_position;
01492 
01493     printf("REAP \"%s\" [%lu:%lu]\n", path, path_info->reap_position, path_info->reap_position + length);
01494 
01495     path_info->reap_position = path_info->reap_position + length;
01496     path_info->reap_time = time(NULL);
01497     path_info->flags &= ~NEEDS_REAPING_FLAG;
01498     if ((error = update_stat_info_and_write(&stat_info, path_info)) != SUCCESS) {
01499         log_msg(LOG_ERROR, _("could not update_stat_info_and_write for \"%s\" (%s) \n"),
01500                 path, error_string(error));
01501         return error;
01502     }
01503 
01504     return SUCCESS;
01505 }

static int regexp_fini ( void   )  [static]

Close our regular expression usage by releasing resources.

Returns:
SUCCESS (0) or non-zero error code otherwise

Definition at line 720 of file lwatch.c.

Referenced by main().

static int regexp_init ( void   )  [static]

Initialize our regular expressions.

Returns:
SUCCESS (0) or non-zero error code otherwise

Definition at line 706 of file lwatch.c.

Referenced by main().

00707 {
00708     int error;
00709 
00710     if ((error = regexp_compile(&logrotate_numbered_backup_regexp)) != SUCCESS) return error;
00711     if ((error = regexp_compile(&logrotate_date_backup_regexp)) != SUCCESS) return error;
00712     return SUCCESS;
00713 }

static int set_path_info_reap_candidacy ( struct lwatch_path_info_t path_info  )  [static]

Determine if watched file needs to be reaped, set internal flag accordingly.

Parameters:
[in] path_info Our saved information about the file, flag updated here
Returns:
SUCCESS (0) or non-zero error code otherwise

Definition at line 1094 of file lwatch.c.

Referenced by process_file_close(), start_monitoring(), and validate_backups_in_database().

01095 {
01096     int error;
01097     bool should_reap;
01098 
01099     if ((error = get_path_info_reap_candidacy(path_info, &should_reap)) != SUCCESS) {
01100         if (error == ENOENT) {  /* file does not exist, not an error, just clear flag */
01101             path_info->flags &= ~NEEDS_REAPING_FLAG;
01102             return SUCCESS;
01103         }
01104     }
01105 
01106     if (should_reap) {
01107         path_info->flags |= NEEDS_REAPING_FLAG;
01108     } else {
01109         path_info->flags &= ~NEEDS_REAPING_FLAG;
01110     }
01111     return SUCCESS;
01112 }

static void signal_handler ( int  signum  )  [static]

Respond to signals.

Parameters:
[in] signum The signal number we received
Returns:
void

Definition at line 784 of file lwatch.c.

Referenced by lwatch_init().

00785 {
00786     log_msg(LOG_INFO, _("received signal %s(%d)\n"), strsignal(signum), signum);
00787 
00788     switch(signum) {
00789     case SIGHUP:
00790         break;
00791     case SIGINT:
00792         keep_watching = false;
00793         break;
00794     case SIGQUIT:
00795         keep_watching = false;
00796         break;
00797     }
00798 }

static int start_monitoring ( const char *  path_arg  )  [static]

Start monitoring a primary file.

Parameters:
[in] path_arg The path name to monitor, will be normalized and made absolute
Returns:
SUCCESS (0) or non-zero error code otherwise
Normalize and make absolute the supplied pathname so it's in a canonical form. Validate it's a primary file and not a backup of a primary file, we already monitor backup's as a consequence of monitoring primary files. Initialize and create an entry for this file in our persistent database. Request the monitoring backend to start monitoring this file.

Definition at line 923 of file lwatch.c.

Referenced by main().

00924 {
00925     int error;
00926     struct lwatch_path_info_t path_info;
00927     char path[PATH_MAX];
00928 
00929     if ((error = make_normalized_absolute_path(path, sizeof(path), path_arg)) != SUCCESS) {
00930         log_msg(LOG_ERROR, _("failed make_normalized_absolute_path from \"%s\" (%s)\n"),
00931                 path_arg, error_string(error));
00932         return error;
00933     }
00934 
00935     log_msg(LOG_INFO, _("monitoring \"%s\"\n"), path);
00936 
00937     if (is_log_backup(path)) {
00938         log_msg(LOG_WARN, _("file \"%s\" appears to be a log backup, only original log files should be monitored\n"),
00939                 path);
00940     }
00941 
00942     if ((error = get_create_path_info(path, &path_info)) != SUCCESS) {
00943         log_msg(LOG_ERROR, _("get_create_path_info failed \"%s\" (%s)\n"),
00944                 path, error_string(error));
00945         return LWATCH_ERROR_CANNOT_MONITOR;
00946     }
00947 
00948     PATH_INFO_FLAG_SET_TARGET(&path_info);
00949 
00950     if ((error = set_path_info_reap_candidacy(&path_info)) != SUCCESS) {
00951         log_msg(LOG_ERROR, _("set_path_info_reap_candidacy failed \"%s\" (%s)\n"),
00952                 path, error_string(error));
00953         return LWATCH_ERROR_CANNOT_MONITOR;
00954     }
00955 
00956     if ((error = database_write_path_info(&path_info)) != SUCCESS) {
00957         log_msg(LOG_ERROR, _("could not insert path_info into database for \"%s\" (%s)\n"),
00958                 path_info.path, error_string(error));
00959         return error;
00960     }
00961 
00962     if (!is_watch_target(path)) {
00963         if ((error = inotify_start_monitoring(path)) != SUCCESS) {
00964             log_msg(LOG_ERROR, _("could not inotify_start_monitoring for \"%s\" (%s)\n"),
00965                     path, error_string(error));
00966             return LWATCH_ERROR_CANNOT_MONITOR;
00967         }
00968     }
00969 
00970     return SUCCESS;
00971 }

int update_stat_info ( struct stat *  cur_stat_info,
struct lwatch_path_info_t path_info 
)

Merge stat information into path into our saved information about the file.

Parameters:
[in] cur_stat_info Pointer to stat data to update from, or if NULL call stat() to obtain info
[in] path_info Our saved information about the file
Returns:
SUCCESS (0) or non-zero error code otherwise

Definition at line 1173 of file lwatch.c.

Referenced by update_stat_info_and_write().

01174 {
01175     int error;
01176     struct stat stat_info;
01177 
01178     if (cur_stat_info == NULL) {
01179         if (stat(path_info->path, &stat_info) < 0) {
01180             error = errno;
01181             log_msg(LOG_ERROR, _("cannot perform stat on \"%s\" (%s) \n"),
01182                     path_info->path, error_string(error));
01183             return error;
01184         }
01185         cur_stat_info = &stat_info;
01186     }
01187 
01188     path_info->dev               = cur_stat_info->st_dev;
01189     path_info->inode             = cur_stat_info->st_ino;
01190     path_info->mode              = cur_stat_info->st_mode;
01191     path_info->uid               = cur_stat_info->st_uid;
01192     path_info->gid               = cur_stat_info->st_gid;
01193     path_info->size              = cur_stat_info->st_size;
01194     path_info->access_time       = cur_stat_info->st_atime;
01195     path_info->modification_time = cur_stat_info->st_mtime;
01196     path_info->change_time       = cur_stat_info->st_ctime;
01197 
01198     path_info->flags |= STAT_DATA_VALID_FLAG;
01199 
01200     return SUCCESS;
01201 }

static int update_stat_info_and_write ( struct stat *  cur_stat_info,
struct lwatch_path_info_t path_info 
) [static]

Merge stat information about a file into our struct and write the updated entry in the persistent database.

Parameters:
[in] cur_stat_info Pointer to stat data to update from, or if NULL call stat() to obtain info
[in] path_info Our saved information about the file
Returns:
SUCCESS (0) or non-zero error code otherwise

Definition at line 1123 of file lwatch.c.

Referenced by reap_file_data().

01124 {
01125     int error;
01126 
01127     if ((error = update_stat_info(cur_stat_info, path_info)) != SUCCESS) {
01128         log_msg(LOG_ERROR, _("could not update_stat_info for \"%s\" (%s) \n"),
01129                 path_info->path, error_string(error));
01130     }
01131 
01132     if ((error = database_write_path_info(path_info)) != SUCCESS) {
01133         log_msg(LOG_ERROR, _("could not insert path_info into database for \"%s\" (%s)\n"),
01134                 path_info->path, error_string(error));
01135         return error;
01136     }
01137     return SUCCESS;
01138 }

static int validate_backups_in_database (  )  [static]

Check the state of known backups since the last time we ran.

Returns:
SUCCESS (0) or non-zero error code otherwise
We need to watch every known backup file as well as the targets because a backup might have data which needs to be reaped. So we iterate over all backups and set a watch on each one. However a backup might have had data added to it while we weren't watching (e.g. this daemon was not running at the time). Thus we also need to evaluate each backup file's reap candidacy by looking at it's current state in the file system and comparing it to our stored information about the file (i.e. path_info). If we see the reap candidacy has changed we need to update that path_info in our persistent SQL storage. However we can't write records into the same table we're currently iterating over. To solve this problem we could 1) call a function which returns a list of path_info's, that way we would be iterating over a copy 2) use a temporary table in the database to hold the result set of backup's and iterate over that copy of the data, or 3) keep a local copy of what needs to be updated and after the first query iteration go back and iterate over the list of what needed updating and write those records. We do #3 because it's the most efficient and easy to implement. First we observe the set of backup files who have had data added to them and needs reaping will in most cases be the empty set. So there is no point in making an extra copy of the result set just in case for a small percentage of the time we need to write one of the records. So instead we dynamically allocate an array to hold updates the first time we see an update is needed. A dynamic array is more efficient than keeping a linked list because with a linked list you have to malloc for every item in the list whereas with an array you might only need to malloc once, plus iterating over an array is inherently more efficient than traversing a list.

Definition at line 1354 of file lwatch.c.

Referenced by lwatch_init().

01355 {
01356     int error;
01357     struct database_iter_context_t *path_iter;
01358     struct lwatch_path_info_t *path_info;
01359 
01360     struct lwatch_path_info_t *path_info_updates;
01361     int i, alloc_increment, alloc_count, update_count;
01362 
01363     alloc_increment = 8;
01364     alloc_count = 0;
01365     update_count = 0;
01366     path_info_updates = NULL;
01367 
01368     if ((error = database_query_backups_iter(&path_iter)) != SUCCESS) {
01369         log_msg(LOG_ERROR, _("database_query_backups_iter failed (%s)\n"),  error_string(error));
01370     }
01371 
01372     while ((path_info = path_iter->next(path_iter))) {
01373         if (!(path_info->flags & NEEDS_REAPING_FLAG)) { /* no sense in checking if flag is already set */
01374             if ((error = set_path_info_reap_candidacy(path_info)) != SUCCESS) {
01375                 log_msg(LOG_ERROR, _("set_path_info_reap_candidacy failed \"%s\" (%s)\n"),
01376                         path_info->path, error_string(error));
01377             }
01378             if (path_info->flags & NEEDS_REAPING_FLAG) { /* flag was set above, remember we have to update this path_info*/
01379                 if (update_count == alloc_count) {
01380                     alloc_count += alloc_increment;
01381                     if ((path_info_updates = realloc(path_info_updates, alloc_count * sizeof(struct lwatch_path_info_t))) == NULL) {
01382                         error = ENOMEM;
01383                         free(path_info_updates);
01384                         free(path_iter);
01385                         log_msg(LOG_ERROR, _("could not alloc memory for update array (%s)\n"), error_string(error));
01386                         return error;
01387                     }
01388                 }
01389                 path_info_updates[update_count++] = *path_info;
01390             }
01391         }
01392 
01393         if ((error = inotify_start_monitoring(path_info->path)) != SUCCESS) {
01394             log_msg(LOG_ERROR, _("could not inotify_start_monitoring for \"%s\" (%s)\n"),
01395                     path_info->path, error_string(error));
01396         }
01397     }
01398     free(path_iter);
01399 
01400     /* If there were updates, now it's time to iterate over them and write the update */
01401     for (i = 0, path_info = path_info_updates; i < update_count; i++, path_info++) {
01402         if ((error = database_write_path_info(path_info)) != SUCCESS) {
01403             log_msg(LOG_ERROR, _("could not insert path_info into database for \"%s\" (%s)\n"),
01404                     path_info->path, error_string(error));
01405         }
01406     }
01407 
01408     if (path_info_updates) free(path_info_updates);
01409 
01410     return SUCCESS;
01411 }


Generated on Mon Aug 31 10:06:21 2009 by  doxygen 1.5.8