diff --git a/doc/manpage.lsyncd.conf.xml b/doc/manpage.lsyncd.conf.xml
index 29df36c..b340d7f 100644
--- a/doc/manpage.lsyncd.conf.xml
+++ b/doc/manpage.lsyncd.conf.xml
@@ -120,6 +120,12 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/
With a node you can control the arguments lsyncd will call the <binary> (rsync) with. Each child node will specify one argument. <option> specifies a literal argument. Only %r will be replaced with 'r' when rsycnd is supposed to work recursive (on startup of lsyncd) or 'd' on normal operations. <exclude-file> will be replaced with if an <exclude-from> file is specified. <source> will be replaced the source directory to sync from. <destination> will be replace to the target to sync to. Default arguments are . <callopts>
+ With a node you can control the inotify events lsyncd will register to. Each event is configured by a <event> child node and its attribute 'id'. The following events may be registered: ACCESS,ATTRIB,CLOSE_WRITE,CLOSE_NOWRITE,CREATE,DELETE,DELETE_SELF,MODIFY,MOVE_SELF,MOVED_FROM,MOVED_TO,OPEN. If the inotify node is omitted the following events are registered by default: IN_ATTRIB,IN_CLOSE_WRITE,IN_CREATE,IN_DELETE,IN_DELETE_SELF,IN_MOVED_FROM,IN_MOVED_TO,IN_DONT_FOLLOW,IN_ONLYDIR.
+ <inotify>
+ <event id="MOVED_TO"/>
+ <event id="DELETE"/>
+ </inotify>
+
<option text="-lt%r"/>
<option text="--delete"/>
<exclude-file/>
@@ -132,8 +138,8 @@ man(1), man(7), http://www.tldp.org/HOWTO/Man-Page/
DIRECTORIES
- With nodes arbitrarily many sources to be watched can be specified. Within a <directory> entry you can again specify a , or a node which will override global settings just for this source. See SETTINGS for details on this options.
- The mandatory node specifies with the parameter "path" the directory to watch and sync. (once in a while something is mandatory. Also at least one node has to specified where to sync to. This has to be a format accepted by rsync.
++ With nodes arbitrarily many sources to be watched can be specified. Within a <directory> entry you can again specify a , , or a node which will override global settings just for this source. See SETTINGS for details on this options.
++ The mandatory node specifies with the paramater "path" the directory to watch and sync. (once in a while something is mandatory. Also at least one node has to specified where to sync to. This has to be a format accepted by rsync. <directory>
<source path="/absolute/path/to/source"/>
<target path="desthost::module/"/>
diff --git a/lsyncd.c b/lsyncd.c
index b65d049..b5ed052 100644
--- a/lsyncd.c
+++ b/lsyncd.c
@@ -59,7 +59,10 @@
*/
#define DEFAULT_BINARY "/usr/bin/rsync"
#define DEFAULT_CONF_FILENAME "/etc/lsyncd.conf.xml"
-
+const uint32_t standard_event_mask =
+ IN_ATTRIB | IN_CLOSE_WRITE | IN_CREATE |
+ IN_DELETE | IN_DELETE_SELF | IN_MOVED_FROM |
+ IN_MOVED_TO | IN_DONT_FOLLOW | IN_ONLYDIR;
/**
* Macros to compare times() values
* (borrowed from linux/jiffies.h)
@@ -168,6 +171,11 @@ struct dir_conf {
*/
struct call_option * callopts;
+ /**
+ * bitmask of inotify events to watch (defaults to global default setting)
+ */
+ uint32_t event_mask;
+
/**
* the exclude-file to pass to rsync (defaults to global default setting)
* TODO, Currently ignored!
@@ -284,6 +292,11 @@ struct global_options {
* Global Option: this binary is used if no other specified in dir_conf.
*/
char *default_binary;
+
+ /**
+ * Global Option: default bitmask of inotify events to react upon.
+ */
+ uint32_t default_event_mask;
/**
* Global Option: default exclude file
@@ -832,6 +845,8 @@ reset_options(struct global_options *opts) {
s_free(opts->default_binary);
}
opts->default_binary = DEFAULT_BINARY;
+
+ opts->default_event_mask = standard_event_mask;
if (opts->default_exclude_file) {
s_free(opts->default_exclude_file);
@@ -1185,7 +1200,7 @@ action(const struct global_options *opts,
/**
* Adds a directory to watch.
*
- * @param log logging information
+ * @param opts global options
* @param watches the vector of watches
* @param inotify_fd inotify file descriptor
* @param pathname the absolute path of the directory to watch
@@ -1196,7 +1211,7 @@ action(const struct global_options *opts,
* @return the watches of the new dir, NULL on error
*/
struct watch *
-add_watch(const struct log *log,
+add_watch(const struct global_options *opts,
struct watch_vector *watches,
int inotify_fd,
char const *pathname,
@@ -1204,14 +1219,13 @@ add_watch(const struct log *log,
struct watch *parent,
struct dir_conf *dir_conf)
{
- int wd; // kernels inotify descriptor
- int wi; // index to insert this watch into the watch vector
- struct watch *w; // the new watch
+ const struct log *log = &opts->log; // loginfo shortcut
+ int wd; // kernels inotify descriptor
+ int wi; // index to insert this watch into the watch vector
+ struct watch *w; // the new watch
- wd = inotify_add_watch(inotify_fd, pathname,
- IN_ATTRIB | IN_CLOSE_WRITE | IN_CREATE |
- IN_DELETE | IN_DELETE_SELF | IN_MOVED_FROM |
- IN_MOVED_TO | IN_DONT_FOLLOW | IN_ONLYDIR);
+ wd = inotify_add_watch(inotify_fd, pathname,
+ dir_conf->event_mask ? dir_conf->event_mask : opts->default_event_mask);
if (wd == -1) {
printlogf(log, ERROR, "Cannot add watch %s (%d:%s)",
@@ -1409,6 +1423,30 @@ delay_or_act_dir(const struct global_options *opts,
}
}
+/**
+ * Looks up the inotify event mask for the specified event text.
+ *
+ * @param text the name of the event to look up
+ *
+ * @return the inotify event mask or 0 if the mask name is unknown.
+ */
+int
+event_text_to_mask(char * text)
+{
+ int mask = 0;
+ struct inotify_mask_text *p;
+
+ for (p = mask_texts; p->mask; p++) {
+ if (!strcmp(p->text, text)) {
+ mask = p->mask;
+ break;
+ }
+ }
+
+ return mask;
+}
+
+
/**
* Adds a directory including all subdirectories to watch.
* Puts the directory with all subdirectories on the delay FIFO.
@@ -1460,7 +1498,7 @@ add_dirwatch(const struct global_options *opts,
}
// watch this directory
- w = add_watch(log, watches, inotify_fd, pathname, dirname, parent, dir_conf);
+ w = add_watch(opts, watches, inotify_fd, pathname, dirname, parent, dir_conf);
if (!w) {
return NULL;
}
@@ -1975,6 +2013,44 @@ parse_callopts(struct global_options *opts, xmlNodePtr node) {
return asw;
}
+/**
+ * Parses
+ */
+uint32_t
+parse_inotify(xmlNodePtr node) {
+ xmlNodePtr dnode;
+ xmlChar *xc;
+ uint32_t mask = 0;
+ int id = 0;
+ for (dnode = node->children; dnode; dnode = dnode->next) {
+ if (dnode->type != XML_ELEMENT_NODE) {
+ continue;
+ }
+ if (!xmlStrcmp(dnode->name, BAD_CAST "event")) {
+ xc = xmlGetProp(dnode, BAD_CAST "id");
+ if (xc == NULL) {
+ printlogf(NULL, ERROR, "error in config file: attribute id missing from \n");
+ exit(LSYNCD_BADCONFIGFILE);
+ }
+ id = event_text_to_mask((char*) xc);
+ if (!id) {
+ printlogf(NULL, ERROR, "error in config file: attribute id of : \"%s\" not known.\n", (char*) xc);
+ exit(LSYNCD_BADCONFIGFILE);
+ }
+ mask |= id;
+ } else {
+ printlogf(NULL, ERROR, "error in config file: unknown node in \"%s\"\n", dnode->name);
+ exit(LSYNCD_BADCONFIGFILE);
+ }
+ }
+ if (!mask) {
+ printlogf(NULL, ERROR, "error in config file: no valid node in \n");
+ exit(LSYNCD_BADCONFIGFILE);
+ }
+ return mask;
+}
+
+
/**
* Parses
*/
@@ -2013,6 +2089,12 @@ parse_directory(struct global_options *opts, xmlNodePtr node) {
terminate(NULL, LSYNCD_BADCONFIGFILE);
}
dc->binary = s_strdup(NULL, (char *) xc, "xml binary");
+ } else if (!xmlStrcmp(dnode->name, BAD_CAST "callopts")) {
+ if (dc->callopts) {
+ printlogf(NULL, ERROR, "error in config file: there is more than one in a \n");
+ terminate(NULL, LSYNCD_BADCONFIGFILE);
+ }
+ dc->callopts = parse_callopts(opts, dnode);
} else if (!xmlStrcmp(dnode->name, BAD_CAST "exclude-from")) {
xc = xmlGetProp(dnode, BAD_CAST "filename");
if (xc == NULL) {
@@ -2020,14 +2102,13 @@ parse_directory(struct global_options *opts, xmlNodePtr node) {
terminate(NULL, LSYNCD_BADCONFIGFILE);
}
dc->exclude_file = s_strdup(NULL, (char *) xc, "xml exclude");
- } else if (!xmlStrcmp(dnode->name, BAD_CAST "callopts")) {
- if (dc->callopts) {
- printlogf(NULL, ERROR, "error in config file: there is more than one in a \n");
- terminate(NULL, LSYNCD_BADCONFIGFILE);
- }
- dc->callopts = parse_callopts(opts, dnode);
+ } else if (!xmlStrcmp(dnode->name, BAD_CAST "inotify")) {
+ if (dc->event_mask) {
+ fprintf(stderr, "error in config file: there is more than one in a \n");
+ exit(LSYNCD_BADCONFIGFILE);
+ }
+ dc->event_mask = parse_inotify(dnode);
} else {
- // TODO missing sourcespecific exclude files?
printlogf(NULL, ERROR, "error in config file: unknown node in \"%s\"\n", dnode->name);
terminate(NULL, LSYNCD_BADCONFIGFILE);
}
@@ -2082,6 +2163,12 @@ parse_settings(struct global_options *opts, xmlNodePtr node) {
terminate(NULL, LSYNCD_BADCONFIGFILE);
}
opts->default_exclude_file = s_strdup(NULL, (char *) xc, "xml default-exclude-file");
+ } else if (!xmlStrcmp(snode->name, BAD_CAST "inotify")) {
+ if (opts->default_event_mask) {
+ printlogf(NULL, ERROR, "error in config file: there is more than one in a \n");
+ exit(LSYNCD_BADCONFIGFILE);
+ }
+ opts->default_event_mask = parse_inotify(snode);
} else if (!xmlStrcmp(snode->name, BAD_CAST "logfile")) {
xc = xmlGetProp(snode, BAD_CAST "filename");
if (xc == NULL) {