// kate: replace-tabs off; space-indent off;
/**
* @mainpage libinotifytools
*
* libinotifytools is a small C library to simplify the use of Linux's inotify
* interface.
*
* @link inotifytools/inotifytools.h Documentation for the library's public
* interface.@endlink
*
* @link todo.html TODO list.@endlink
*/
#include "../../config.h"
#include "inotifytools/inotifytools.h"
#include "inotifytools_p.h"
#include <string.h>
#include <strings.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <sys/select.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <dirent.h>
#include <time.h>
#include <regex.h>
#include <setjmp.h>
#include "inotifytools/inotify.h"
/**
* @file inotifytools/inotifytools.h
* @brief inotifytools library public interface.
* @author Rohan McGovern, \<rohan@mcgovern.id.au\>
*
* This library provides a thin layer on top of the basic inotify interface.
* The primary use is to easily set up watches on files, potentially many files
* at once, and read events without having to deal with low-level I/O. There
* are also several utility functions for inotify-related string formatting.
*
* To use this library, you must \c \#include the following headers accordingly:
* \li \c \<inotifytools/inotifytools.h\> - to use any functions declared in
* this file.
* \li \c \<inotifytools/inotify.h\> - to have the \c inotify_event type defined
* and the numeric IN_* event constants defined. If \c \<sys/inotify.h\>
* was present on your system at compile time, this header simply includes
* that. Otherwise it includes \c \<inotifytools/inotify-nosys.h\>.
*
* @section example Example
* This very simple program recursively watches the entire directory tree
* under its working directory for events, then prints them out with a
* timestamp.
* @include example.c
*
* @section events Events
*
* @note This section comes almost verbatim from the inotify(7) man page.
*
* @warning The information here applies to inotify in Linux 2.6.17. Older
* versions of Linux may not support all the events described here.
*
* The following numeric events can be specified to functions in inotifytools,
* and may be present in events returned through inotifytools:
*
* \li \c IN_ACCESS - File was accessed (read) \a *
* \li \c IN_ATTRIB - Metadata changed (permissions, timestamps,
* extended attributes, etc.) \a *
* \li \c IN_CLOSE_WRITE - File opened for writing was closed \a *
* \li \c IN_CLOSE_NOWRITE - File not opened for writing was closed \a *
* \li \c IN_CREATE - File/directory created in watched directory \a *
* \li \c IN_DELETE - File/directory deleted from watched directory \a *
* \li \c IN_DELETE_SELF - Watched file/directory was itself deleted
* \li \c IN_MODIFY - File was modified \a *
* \li \c IN_MOVE_SELF - Watched file/directory was itself moved
* \li \c IN_MOVED_FROM - File moved out of watched directory \a *
* \li \c IN_MOVED_TO - File moved into watched directory \a *
* \li \c IN_OPEN - File was opened \a *
*
* When monitoring a directory, the events marked with an asterisk \a * above
* can occur for files in the directory, in which case the name field in the
* returned inotify_event structure identifies the name of the file within the
* directory.
*
* The IN_ALL_EVENTS macro is defined as a bit mask of all of the above events.
*
* Two additional convenience macros are IN_MOVE, which equates to
* IN_MOVED_FROM|IN_MOVED_TO, and IN_CLOSE which equates to
* IN_CLOSE_WRITE|IN_CLOSE_NOWRITE.
*
* The following bitmasks can also be provided when creating a new watch:
*
* \li \c IN_DONT_FOLLOW - Don't dereference pathname if it is a symbolic link
* \li \c IN_MASK_ADD - Add (OR) events to watch mask for this pathname if
* it already exists (instead of replacing mask)
* \li \c IN_ONESHOT - Monitor pathname for one event, then remove from
* watch list
* \li \c IN_ONLYDIR - Only watch pathname if it is a directory
*
* The following bitmasks may occur in events generated by a watch:
*
* \li \c IN_IGNORED - Watch was removed explicitly
* (inotifytools_remove_watch_*) or automatically (file
* was deleted, or file system was unmounted)
* \li \c IN_ISDIR - Subject of this event is a directory
* \li \c IN_Q_OVERFLOW - Event queue overflowed (wd is -1 for this event)
* \li \c IN_UNMOUNT - File system containing watched object was unmounted
*
* @section TODO TODO list
*
* @todo Improve wd/filename mapping. Currently there is no explicit code for
* handling different filenames mapping to the same inode (and hence, wd).
* gamin's approach sounds good: let the user watch an inode using several
* different filenames, and when an event occurs on the inode, generate an
* event for each filename.
*/
#define MAX_EVENTS 4096
#define MAX_STRLEN 4096
#define INOTIFY_PROCDIR "/proc/sys/fs/inotify/"
#define WATCHES_SIZE_PATH INOTIFY_PROCDIR "max_user_watches"
#define QUEUE_SIZE_PATH INOTIFY_PROCDIR "max_queued_watches"
#define INSTANCES_PATH INOTIFY_PROCDIR "max_user_instances"
static int inotify_fd;
static unsigned num_access;
static unsigned num_modify;
static unsigned num_attrib;
static unsigned num_close_nowrite;
static unsigned num_close_write;
static unsigned num_open;
static unsigned num_move_self;
static unsigned num_moved_to;
static unsigned num_moved_from;
static unsigned num_create;
static unsigned num_delete;
static unsigned num_delete_self;
static unsigned num_unmount;
static unsigned num_total;
static int collect_stats = 0;
struct rbtree *tree_wd = 0;
struct rbtree *tree_filename = 0;
static int error = 0;
static int init = 0;
static char* timefmt = 0;
static regex_t* regex = 0;
int isdir( char const * path );
void record_stats( struct inotify_event const * event );
int onestr_to_event(char const * event);
/**
* @internal
* Assert that a condition evaluates to true, and optionally output a message
* if the assertion fails.
*
* @param cond Integer; if 0, assertion fails, otherwise assertion succeeds.
*
* @param mesg A human-readable error message shown if assertion fails.
*
* @section example Example
* @code
* int upper = 100, lower = 50;
* int input = get_user_input();
* niceassert( input <= upper && input >= lower,
* "input not in required range!");
* @endcode
*/
#define niceassert(cond,mesg) _niceassert((long)cond, __LINE__, __FILE__, \
#cond, mesg)
#define nasprintf(...) niceassert( -1 != asprintf(__VA_ARGS__), "out of memory")
/**
* @internal
* Assert that a condition evaluates to true, and optionally output a message
* if the assertion fails.
*
* You should use the niceassert() preprocessor macro instead.
*
* @param cond If 0, assertion fails, otherwise assertion succeeds.
*
* @param line Line number of source code where assertion is made.
*
* @param file Name of source file where assertion is made.
*
* @param condstr Stringified assertion expression.
*
* @param mesg A human-readable error message shown if assertion fails.
*/
void _niceassert( long cond, int line, char const * file, char const * condstr,
char const * mesg ) {
if ( cond ) return;
if ( mesg ) {
fprintf(stderr, "%s:%d assertion ( %s ) failed: %s\n", file, line,
condstr, mesg );
}
else {
fprintf(stderr, "%s:%d assertion ( %s ) failed.\n", file, line, condstr);
}
}
/**
* @internal
* Construct a string from a character.
*
* @param ch A character.
*
* @return A string of length 1 consisting of the character @a ch. The string
* will be overwritten in subsequent calls.
*/
char * chrtostr(char ch) {
static c