diff --git a/lsyncd.c b/lsyncd.c index 0e69587..145e499 100644 --- a/lsyncd.c +++ b/lsyncd.c @@ -1,10 +1,12 @@ /** - * lsyncd.c Live (Mirror) Syncing Demon - * - * License: GPLv2 (see COPYING) or any later version - * - * Authors: Axel Kittenberger - */ +* lsyncd.c Live (Mirror) Syncing Demon +* +* License: GPLv2 (see COPYING) or any later version +* +* Authors: Axel Kittenberger +* Eugene Sanivsky +*/ + #include "config.h" #include @@ -62,7 +64,7 @@ char * option_target = NULL; /** * Option: rsync binary to call. */ -char * rsync_binary = "/usr/bin/rsync"; +char * rsync_binary = "/usr/local/bin/lsyncd.nice"; /** * Option: the exclude-file to pass to rsync. @@ -77,32 +79,33 @@ char * exclude_file = NULL; /** * Structure to store the directory watches of the deamon. */ -struct dir_watch { - /** - * The watch descriptor returned by kernel. - */ - int wd; - /** - * The name of the directory. - * In case of the root dir to be watched, it is a full path - * and parent == NULL. Otherwise its just the name of the - * directory and parent points to the parent directory thats - * also watched. - */ - char * dirname; - +struct dir_watch { + /** + * The watch descriptor returned by kernel. + */ + int wd; + + /** + * The name of the directory. + * In case of the root dir to be watched, it is a full path + * and parent == NULL. Otherwise its just the name of the + * directory and parent points to the parent directory thats + * also watched. + */ + char * dirname; + /** * Call this directory that way on destiation. * if NULL call it like dirname. */ char * destname; - /** - * Points to the index of the parent. - * -1 if no parent - */ - int parent; + /** + * Points to the index of the parent. + * -1 if no parent + */ + int parent; }; @@ -110,34 +113,37 @@ struct dir_watch { * Structure to store strings for the diversve Inotfy masked events. * Actually used for compfortable debugging only. */ + struct inotify_mask_text { - int mask; - char const * text; + int mask; + char const * text; }; /** * A constant that assigns every inotify mask a printable string. * Used for debugging. */ + struct inotify_mask_text mask_texts[] = { - { IN_ACCESS, "ACCESS" }, - { IN_ATTRIB, "ATTRIB" }, - { IN_CLOSE_WRITE, "CLOSE_WRITE" }, - { IN_CLOSE_NOWRITE, "CLOSE_NOWRITE" }, - { IN_CREATE, "CREATE" }, - { IN_DELETE, "DELETE" }, - { IN_DELETE_SELF, "DELETE_SELF" }, - { IN_MODIFY, "MODIFY" }, - { IN_MOVE_SELF, "MOVE_SELF" }, - { IN_MOVED_FROM, "MOVED_FROM" }, - { IN_MOVED_TO, "MOVED_TO" }, - { IN_OPEN, "OPEN" }, - { 0, "" }, -}; + { IN_ACCESS, "ACCESS" }, + { IN_ATTRIB, "ATTRIB" }, + { IN_CLOSE_WRITE, "CLOSE_WRITE" }, + { IN_CLOSE_NOWRITE, "CLOSE_NOWRITE" }, + { IN_CREATE, "CREATE" }, + { IN_DELETE, "DELETE" }, + { IN_DELETE_SELF, "DELETE_SELF" }, + { IN_MODIFY, "MODIFY" }, + { IN_MOVE_SELF, "MOVE_SELF" }, + { IN_MOVED_FROM, "MOVED_FROM" }, + { IN_MOVED_TO, "MOVED_TO" }, + { IN_OPEN, "OPEN" }, + { 0, "" }, +}; /** * Holds an allocated array of all directories watched. */ + struct dir_watch *dir_watches; /** @@ -153,114 +159,153 @@ int dir_watch_num = 0; /** * lsyncd will log into this file/stream. */ -char * logfile = "/var/log/lsyncd"; - +char *logfile = "/var/log/lsyncd"; /** * The inotify instance. */ -int inotf; - +int inotf; /** - * Array of strings of directory names to include. - * This is limited to MAX_EXCLUDES. + * Array of strings of directory names to include. + * This is limited to MAX_EXCLUDES. * It's not worth to code a dynamic size handling... */ #define MAX_EXCLUDES 256 -char * exclude_dirs[MAX_EXCLUDES] = {NULL, }; +char *exclude_dirs[MAX_EXCLUDES] = {NULL, }; int exclude_dir_n = 0; volatile sig_atomic_t keep_going = 1; -void catch_alarm(int sig) { - keep_going = 0; +void catch_alarm(int sig) +{ + keep_going = 0; } + /** * Prints a message to the log stream, preceding a timestamp. * Otherwise it behaves like printf(); */ -void printlogf(int level, const char *fmt, ...) { - va_list ap; - char * ct; - time_t mtime; - FILE * flog; +void printlogf(int level, const char *fmt, ...) +{ + va_list ap; + char * ct; + time_t mtime; + FILE * flog; - if (level < loglevel) { - return; - } - - if (!flag_nodaemon) { - flog = fopen(logfile, "a"); - if (flog == NULL) { - printf("cannot open logfile!\n"); - exit(-1); - } - } else { - flog = stdout; + if (level < loglevel) { + return; } - va_start(ap, fmt); - time(&mtime); - ct = ctime(&mtime); - ct[strlen(ct) - 1] = 0; // cut trailing \n - fprintf(flog, "%s: ", ct); - switch (level) { - case LOG_DEBUG : break; - case LOG_NORMAL : break; - case LOG_ERROR : fprintf(flog, "ERROR :"); break; - } - vfprintf(flog, fmt, ap); - fprintf(flog, "\n"); - va_end(ap); - - if (!flag_nodaemon) { - fclose(flog); - } + if (!flag_nodaemon) { + flog = fopen(logfile, "a"); + + if (flog == NULL) { + printf("cannot open logfile!\n"); + exit(-1); + } + } else { + flog = stdout; + } + + va_start(ap, fmt); + + time(&mtime); + ct = ctime(&mtime); + ct[strlen(ct) - 1] = 0; // cut trailing \n + fprintf(flog, "%s: ", ct); + + switch (level) { + + case LOG_DEBUG : + break; + + case LOG_NORMAL : + break; + + case LOG_ERROR : + fprintf(flog, "ERROR :"); + break; + } + + vfprintf(flog, fmt, ap); + + fprintf(flog, "\n"); + va_end(ap); + + if (!flag_nodaemon) { + fclose(flog); + } } /** - * "secured" malloc, meaning the deamon shall kill itself - * in case of out of memory. + * Checks if a path is a directory + * Returns true on success, false on negative and -1 on error + */ +int is_dir(char const *path) +{ + + struct stat statbuf; + + if (lstat(path, &statbuf) == -1) { + printlogf(LOG_ERROR, "[%s]: while calling stat()", (char*)strerror); + return -1; + } else { + return S_ISDIR(statbuf.st_mode); + } +} + +/** + * "secured" malloc, meaning the deamon shall kill itself + * in case of out of memory. * - * On linux systems, which is actually the only system this + * On linux systems, which is actually the only system this * deamon will run at, due to the use of inotify, this is - * an "academic" cleaness only, linux will never return out - * memory, but kill a process to ensure memory will be + * an "academic" cleaness only, linux will never return out + * memory, but kill a process to ensure memory will be * available. */ -void *s_malloc(size_t size) { - void *r = malloc(size); - if (r == NULL) { - printlogf(LOG_ERROR, "Out of memory!"); - exit(-1); - } - return r; +void *s_malloc(size_t size) +{ + void *r = malloc(size); + + if (r == NULL) { + printlogf(LOG_ERROR, "Out of memory!"); + exit(-1); + } + + return r; } /** * "secured" calloc. */ -void *s_calloc(size_t nmemb, size_t size) { - void *r = calloc(nmemb, size); - if (r == NULL) { - printlogf(LOG_ERROR, "out of memory!"); - exit(-1); - } - return r; +void *s_calloc(size_t nmemb, size_t size) +{ + void *r = calloc(nmemb, size); + + if (r == NULL) { + printlogf(LOG_ERROR, "out of memory!"); + exit(-1); + } + + return r; } /** * "secured" realloc. */ -void *s_realloc(void *ptr, size_t size) { - void *r = realloc(ptr, size); - if (r == NULL) { - printlogf(LOG_ERROR, "out of memory!"); - exit(-1); - } - return r; +void *s_realloc(void *ptr, size_t size) +{ + void *r = realloc(ptr, size); + + if (r == NULL) { + printlogf(LOG_ERROR, "out of memory!"); + exit(-1); + } + + return r; } /** @@ -271,101 +316,122 @@ void *s_realloc(void *ptr, size_t size) { * @param dest Destination string, * @param recursive If true -r will be handled on, -d (single directory) otherwise */ -bool rsync(char const * src, const char * dest, bool recursive) { - pid_t pid; - int status; - char const * opts = recursive ? "-ltr" : "-ltd"; +bool rsync(char const *src, const char *dest, bool recursive) +{ + pid_t pid; + int status; + char const *opts, *rsync_debug; - if (exclude_file) { - printlogf(LOG_DEBUG, "exec %s(%s,%s,%s,%s,%s,%s)", rsync_binary, "--delete", opts, "--exclude-from", exclude_file, src, dest); - } else { - printlogf(LOG_DEBUG, "exec %s(%s,%s,%s,%s)", rsync_binary, "--delete", opts, src, dest); - } + if (recursive) { + opts="--owner --group --devices --specials --super --perms --executability --one-file-system -ltr"; + } else { + opts="--owner --group --devices --specials --super --perms --executability --one-file-system -ltd"; + } - if (flag_dryrun) { - return true; - } - pid = fork(); - if (pid == 0) { - if (!flag_nodaemon) { - freopen(logfile, "a", stdout); - freopen(logfile, "a", stderr); - } - if (exclude_file) { - execl(rsync_binary, rsync_binary, "--delete", opts, "--exclude-from", exclude_file, src, dest, NULL); - } else { - execl(rsync_binary, rsync_binary, "--delete", opts, src, dest, NULL); - } - printlogf(LOG_ERROR, "oh my god, execl returned!"); - exit(-1); - } - - waitpid(pid, &status, 0); - return true; + if (LOG_DEBUG == 1) { + rsync_debug="-P"; + } else { + rsync_debug=""; + } + + if (exclude_file) { + printlogf(LOG_DEBUG, "exec %s(%s,%s,%s,%s,%s,%s,%s)", rsync_binary, rsync_debug, "--delete", opts, "--exclude-from", exclude_file, src, dest); + } else { + printlogf(LOG_DEBUG, "exec %s(%s,%s,%s,%s,%s)", rsync_binary, rsync_debug, "--delete", opts, src, dest); + } + + if (flag_dryrun) { + printlogf(LOG_DEBUG, "Rsync skipped due to dry mode."); + return true; + } + + pid = fork(); + + if (pid == 0) { + if (!flag_nodaemon) { + freopen(logfile, "a", stdout); + freopen(logfile, "a", stderr); + } + + if (exclude_file) { + execl(rsync_binary, rsync_binary, "--delete", opts, "--exclude-from", exclude_file, src, dest, NULL); + } else { + e xecl(rsync_binary, rsync_binary, "--delete", opts, src, dest, NULL); + } + + printlogf(LOG_ERROR, "oh my god, execl returned!"); + + exit(-1); + } + + waitpid(pid, &status, 0); + + printlogf(LOG_DEBUG, "Rsync of [%s] -> [%s] finished", src, dest); + return true; } - - /** * Adds a directory to watch * * @param pathname the absolute path of the directory to watch. * @param dirname the name of the directory only (yes this is a bit redudant, but oh well) - * @param destname if not NULL call this dir that way on destionation. + * @param destname if not NULL call this dir that way on destionation. * @param parent if not -1 the index to the parent directory that is already watched * * @return index to dir_watches of the new dir, -1 on error. */ -int add_watch(char const * pathname, char const * dirname, char const * destname, int parent) { - int wd; - char * nn; - int newdw; +int add_watch(char const *pathname, char const *dirname, char const *destname, int parent) +{ + int wd; + char * nn; + int newdw; - wd = inotify_add_watch(inotf, 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(inotf, pathname, + IN_ATTRIB | IN_CLOSE_WRITE | IN_CREATE | IN_DELETE | IN_DELETE_SELF | + IN_MOVED_FROM | IN_MOVED_TO | IN_DONT_FOLLOW | IN_ONLYDIR); - if (wd == -1) { - printlogf(LOG_ERROR, "Cannot add watch %s (%d:%s)", pathname, errno, strerror(errno)); - return -1; - } - - // look if an unused slot can be found. - for(newdw = 0; newdw < dir_watch_num; newdw++) { - if (dir_watches[newdw].wd < 0) { - break; - } - } - - if (newdw == dir_watch_num) { - if (dir_watch_num + 1 >= dir_watch_size) { - dir_watch_size *= 2; - dir_watches = s_realloc(dir_watches, dir_watch_size * sizeof(struct dir_watch)); - } - dir_watch_num++; - } - - dir_watches[newdw].wd = wd; - dir_watches[newdw].parent = parent; - - nn = s_malloc(strlen(dirname) + 1); - strcpy(nn, dirname); - dir_watches[newdw].dirname = nn; - - if (destname) { - nn = s_malloc(strlen(destname) + 1); - strcpy(nn, destname); - dir_watches[newdw].destname = nn; - } else { - dir_watches[newdw].destname = NULL; + if (wd == -1) { + printlogf(LOG_ERROR, "Cannot add watch %s (%d:%s)", pathname, errno, strerror(errno)); + return -1; } - return newdw; + // look if an unused slot can be found. + for (newdw = 0; newdw < dir_watch_num; newdw++) { + if (dir_watches[newdw].wd < 0) { + break; + } + } + + if (newdw == dir_watch_num) { + if (dir_watch_num + 1 >= dir_watch_size) { + dir_watch_size *= 2; + dir_watches = s_realloc(dir_watches, dir_watch_size * sizeof(struct dir_watch)); + } + + dir_watch_num++; + } + + dir_watches[newdw].wd = wd; + + dir_watches[newdw].parent = parent; + + nn = s_malloc(strlen(dirname) + 1); + strcpy(nn, dirname); + dir_watches[newdw].dirname = nn; + + if (destname) { + nn = s_malloc(strlen(destname) + 1); + strcpy(nn, destname); + dir_watches[newdw].destname = nn; + } else { + dir_watches[newdw].destname = NULL; + } + + return newdw; } - /** - * Builds the abolute path name of a given directory beeing watched. + * Builds the abolute path name of a given directory beeing watched. * * @param pathname destination buffer to store the result to. * @param pathsize max size of this buffer @@ -373,48 +439,52 @@ int add_watch(char const * pathname, char const * dirname, char const * destname * @param name if not null, this beeing the watch dir itself is appended. * @param prefix if not NULL it is added at the bein of pathname */ -bool buildpath(char *pathname, - int pathsize, - int watch, - char const *name, - char const * prefix) { - int j, k, p, ps; - - pathname[0] = 0; - if (prefix) { - strcat(pathname, prefix); - } +bool buildpath(char *pathname, + int pathsize, + int watch, + char const *name, + char const *prefix) +{ - // count how big the parent stack is - for(p = watch, ps = 0; p != -1; p = dir_watches[p].parent, ps++) { - } + int j, k, p, ps; + pathname[0] = 0; - // now add the parent paths from back to front - for(j = ps; j > 0; j--) { + if (prefix) { + strcat(pathname, prefix); + } + + // count how big the parent stack is + for (p = watch, ps = 0; p != -1; p = dir_watches[p].parent, ps++) {} + + // now add the parent paths from back to front + for (j = ps; j > 0; j--) { char * name; - // go j steps behind stack - for(p = watch, k = 0; k + 1 < j; p = dir_watches[p].parent, k++) { - } + // go j steps behind stack + + for (p = watch, k = 0; k + 1 < j; p = dir_watches[p].parent, k++) {} name = (prefix && dir_watches[p].destname) ? dir_watches[p].destname : dir_watches[p].dirname; - if (strlen(pathname) + strlen(name) + 2 >= pathsize) { - printlogf(LOG_ERROR, "path too long %s/...", name); - return false; - } - strcat(pathname, name); - strcat(pathname, "/"); - } - - if (name) { - if (strlen(pathname) + strlen(name) + 2 >= pathsize) { - printlogf(LOG_ERROR, "path too long %s//%s", pathname, name); - return false; - } - strcat(pathname, name); - } - - return true; + if (strlen(pathname) + strlen(name) + 2 >= pathsize) { + printlogf(LOG_ERROR, "path too long %s/...", name); + return false; + } + + strcat(pathname, name); + + strcat(pathname, "/"); + } + + if (name) { + if (strlen(pathname) + strlen(name) + 2 >= pathsize) { + printlogf(LOG_ERROR, "path too long %s//%s", pathname, name); + return false; + } + + strcat(pathname, name); + } + + return true; } /** @@ -426,97 +496,119 @@ bool buildpath(char *pathname, * @param parent If not -1, the index in dir_watches to the parent directory already watched. * Must have absolute path if parent == -1. */ -bool add_dirwatch(char const * dirname, char const * destname, bool recursive, int parent) { - DIR *d; - struct dirent *de; - int dw, i; - char pathname[MAX_PATH]; +bool add_dirwatch(char const *dirname, char const *destname, bool recursive, int parent) +{ + DIR *d; - printlogf(LOG_DEBUG, "add_dirwatch(%s, %s, %d, p->dirname:%s)", dirname, destname, recursive, parent >= 0 ? dir_watches[parent].dirname : "NULL"); - if (!buildpath(pathname, sizeof(pathname), parent, dirname, NULL)) { - return false; - } + struct dirent *de; + int dw, i; + char pathname[MAX_PATH], fullpath[MAX_PATH]; - for (i = 0; i < exclude_dir_n; i++) { - if (!strcmp(dirname, exclude_dirs[i])) { - return true; - } + if (!buildpath(pathname, sizeof(pathname), parent, dirname, NULL)) { + printlogf(LOG_ERROR, "Building path for [%s] failed.", dirname); + return false; } - dw = add_watch(pathname, dirname, destname, parent); - if (dw == -1) { - return false; - } + for (i = 0; i < exclude_dir_n; i++) { + // TODO explain + if ( (strstr(pathname, exclude_dirs[i]) - pathname) == (strlen(pathname) - strlen(exclude_dirs[i])) ) { + printlogf(LOG_DEBUG, "add_dirwatch::excluded(%s[%s], %s)", dirname, pathname, exclude_dirs[i]); + return false; + } + } - if (strlen(pathname) + strlen(dirname) + 2 > sizeof(pathname)) { - printlogf(LOG_ERROR, "pathname too long %s//%s", pathname, dirname); - return false; - } - - d = opendir(pathname); - if (d == NULL) { - printlogf(LOG_ERROR, "cannot open dir %s.", dirname); - return false; - } + dw = add_watch(pathname, dirname, destname, parent); - while(keep_going) { - de = readdir(d); - if (de == NULL) { - break; - } - if (de->d_type == DT_DIR && strcmp(de->d_name, "..") && strcmp(de->d_name, ".")) { - add_dirwatch(de->d_name, NULL, true, dw); - } - } - closedir(d); + if (dw == -1) { + printlogf(LOG_ERROR, "add_watch(%s)failed.", dirname); + return false; + } - return true; + printlogf(LOG_DEBUG, "add_dirwatch::added(%s[%s], %s, %d, p->dirname:%s)", dirname, pathname, destname, recursive, parent >= 0 ? dir_watches[parent].dirname : "NULL"); + + if (strlen(pathname) + strlen(dirname) + 2 > sizeof(pathname)) { + printlogf(LOG_ERROR, "pathname too long %s//%s", pathname, dirname); + return false; + } + + d = opendir(pathname); + + if (d == NULL) { + printlogf(LOG_ERROR, "cannot open dir %s.", dirname); + return false; + } + + while (de = readdir(d)) { + sprintf(fullpath, "%s/%s", pathname, de->d_name); + //printlogf(LOG_DEBUG, "Checking if [%s] is a folder.", fullpath); + // why was DT_DIR not good? + + if ((is_dir(fullpath) == true) && strcmp(de->d_name, "..") && strcmp(de->d_name, ".")) { + //printlogf(LOG_DEBUG, "Going deeper to %s.", de->d_name); + add_dirwatch(de->d_name, NULL, true, dw); + } + + /*else + { + printlogf(LOG_DEBUG, "Not going deeper, since object %s is not folder.", de->d_name); + }*/ + } + + closedir(d); + + return true; } /** * Removes a watched dir, including recursevily all subdirs. * * @param name Optionally. If not NULL, the directory name to remove which is a child of parent. - * @param parent The index to the parent directory of the directory 'name' to remove, + * @param parent The index to the parent directory of the directory 'name' to remove, * or to be removed itself if name == NULL. */ -bool remove_dirwatch(const char * name, int parent) { - int i; - int dw; - - if (name) { - // look for the child with the name - for(i = 0; i < dir_watch_num; i++) { - if (dir_watches[i].wd >= 0 && dir_watches[i].parent == parent && - !strcmp(name, dir_watches[i].dirname)) { - dw = i; - break; - } - } - if (i >= dir_watch_num) { - printlogf(LOG_ERROR, "Cannot find entry for %s:/:%s :-(", dir_watches[parent].dirname, name); - return false; - } - } else { - dw = parent; - } - for(i = 0; i < dir_watch_num; i++) { - if (dir_watches[i].wd >= 0 && dir_watches[i].parent == dw) { - remove_dirwatch(NULL, i); - } - } - - inotify_rm_watch(inotf, dir_watches[dw].wd); - dir_watches[dw].wd = -1; +bool remove_dirwatch(const char * name, int parent) +{ + int i; + int dw; - free(dir_watches[dw].dirname); - dir_watches[dw].dirname = NULL; + if (name) { + // look for the child with the name + for (i = 0; i < dir_watch_num; i++) { + if (dir_watches[i].wd >= 0 && dir_watches[i].parent == parent && + !strcmp(name, dir_watches[i].dirname) + ) { + dw = i; + break; + } + } - if (dir_watches[dw].destname) { - free(dir_watches[dw].destname); - dir_watches[dw].destname = NULL; + if (i >= dir_watch_num) { + printlogf(LOG_ERROR, "Cannot find entry for %s:/:%s :-(", dir_watches[parent].dirname, name); + return false; + } + } else { + dw = parent; } - return true; + + for (i = 0; i < dir_watch_num; i++) { + if (dir_watches[i].wd >= 0 && dir_watches[i].parent == dw) { + remove_dirwatch(NULL, i); + } + } + + inotify_rm_watch(inotf, dir_watches[dw].wd); + + dir_watches[dw].wd = -1; + + free(dir_watches[dw].dirname); + dir_watches[dw].dirname = NULL; + + if (dir_watches[dw].destname) { + free(dir_watches[dw].destname); + dir_watches[dw].destname = NULL; + } + + return true; } /** @@ -524,159 +616,215 @@ bool remove_dirwatch(const char * name, int parent) { * * @param event The event to handle */ -bool handle_event(struct inotify_event *event) { - char masktext[255] = {0,}; - char pathname[MAX_PATH]; - char destname[MAX_PATH]; +bool handle_event(struct inotify_event *event) +{ + char masktext[255] = {0,}; + char pathname[MAX_PATH]; + char destname[MAX_PATH]; - int mask = event->mask; - int i; - struct inotify_mask_text *p; + int mask = event->mask; + int i; - if (IN_IGNORED & event->mask) { - return true; - } - for (i = 0; i < exclude_dir_n; i++) { - if (!strcmp(event->name, exclude_dirs[i])) { - return true; - } + struct inotify_mask_text *p; + + if (IN_IGNORED & event->mask) { + return true; } - for(p = mask_texts; p->mask; p++) { - if (mask & p->mask) { - if (strlen(masktext) + strlen(p->text) + 3 >= sizeof(masktext)) { - printlogf(LOG_ERROR, "bufferoverflow in handle_event"); - return false; - } - if (*masktext) { - strcat(masktext, ", "); - } - strcat(masktext, p->text); - } - } + for (i = 0; i < exclude_dir_n; i++) { + if (!strcmp(event->name, exclude_dirs[i])) { + printlogf(LOG_DEBUG, "Event on [%s] ignored due to exclude rules", event->name); + return true; + } + } - for(i = 0; i < dir_watch_num; i++) { - if (dir_watches[i].wd == event->wd) { - break; - } - } - if (i >= dir_watch_num) { - printlogf(LOG_ERROR, "received unkown inotify event :-(%d)", event->mask); - return false; - } + for (p = mask_texts; p->mask; p++) { + if (mask & p->mask) { + if (strlen(masktext) + strlen(p->text) + 3 >= sizeof(masktext)) { + printlogf(LOG_ERROR, "bufferoverflow in handle_event"); + return false; + } - - if (((IN_CREATE | IN_MOVED_TO) & event->mask) && (IN_ISDIR & event->mask)) { - add_dirwatch(event->name, NULL, false, i); - } + if (*masktext) { + strcat(masktext, ", "); + } - if (((IN_DELETE | IN_MOVED_FROM) & event->mask) && (IN_ISDIR & event->mask)) { - remove_dirwatch(event->name, i); - } + strcat(masktext, p->text); + } + } - if (!buildpath(pathname, sizeof(pathname), i, NULL, NULL)) { - return false; - } - if (!buildpath(destname, sizeof(destname), i, NULL, option_target)) { - return false; - } - - // call rsync to propagate changes in the directory - if ((IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_MOVED_TO | IN_MOVED_FROM) & event->mask) { - printlogf(LOG_NORMAL, "%s of %s in %s --> %s", masktext, event->name, pathname, destname); - rsync(pathname, destname, false); - } - return 0; + for (i = 0; i < dir_watch_num; i++) { + if (dir_watches[i].wd == event->wd) { + break; + } + } + + if (i >= dir_watch_num) { + printlogf(LOG_ERROR, "received unkown inotify event :-(%d)", event->mask); + return false; + } + + if (((IN_CREATE) & event->mask) && (IN_ISDIR & event->mask)) { + add_dirwatch(event->name, NULL, false, i); + } + + if (((IN_MOVED_TO) & event->mask) && (IN_ISDIR & event->mask)) { + add_dirwatch(event->name, NULL, true, i); + } + + if (((IN_DELETE | IN_MOVED_FROM) & event->mask) && (IN_ISDIR & event->mask)) { + remove_dirwatch(event->name, i); + } + + if (!buildpath(pathname, sizeof(pathname), i, NULL, NULL)) { + return false; + } + + if (!buildpath(destname, sizeof(destname), i, NULL, option_target)) { + return false; + } + + // call rsync to propagate changes in the directory + if ((IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_MOVED_TO | IN_MOVED_FROM) & event->mask) { + printlogf(LOG_NORMAL, "%s of %s in %s --> %s", masktext, event->name, pathname, destname); + + if (IN_MOVED_TO & event->mask) { + rsync(pathname, destname, true); + } else { + rsync(pathname, destname, false); + } + } + + return 0; } /** * The control loop waiting for inotify events. */ -bool master_loop() { - char buf[INOTIFY_BUF_LEN]; - int len, i = 0; +bool master_loop() +{ + char buf[INOTIFY_BUF_LEN]; + int len, i = 0; - while (keep_going) { - len = read (inotf, buf, INOTIFY_BUF_LEN); - if (len < 0) { - printlogf(LOG_ERROR, "failed to read from inotify (%d:%s)", errno, strerror(errno)); - return false; - } - if (len == 0) { - printlogf(LOG_ERROR, "eof?"); - return false; - } + while (keep_going) { + len = read (inotf, buf, INOTIFY_BUF_LEN); + + if (len < 0) { + printlogf(LOG_ERROR, "failed to read from inotify (%d:%s)", errno, strerror(errno)); + return false; + } + + if (len == 0) { + printlogf(LOG_ERROR, "eof?"); + return false; + } + + i = 0; + + while (i < len) { + + struct inotify_event *event = (struct inotify_event *) &buf[i]; + printlogf(LOG_DEBUG, "Event [%d] started", i); + handle_event(event); + printlogf(LOG_DEBUG, "Event [%d] finished", i); + i += sizeof(struct inotify_event) + event->len; + } + } - i = 0; - while (i < len) { - struct inotify_event *event = (struct inotify_event *) &buf[i]; - handle_event(event); - i += sizeof(struct inotify_event) + event->len; - } - } return true; } /** - * Scans all dirs in /home, looking if a www subdir exists. + * Scans all dirs in /home, looking if a www subdir exists. * Syncs this dir immediately, and adds watches to it. */ -bool scan_homes() { - DIR *d; - DIR *d2; - char path[MAX_PATH]; - char destpath[MAX_PATH]; - struct dirent *de; - - d = opendir("/home"); - if (d == NULL) { - printlogf(LOG_ERROR, "Cannot open /home"); - return false; - } +bool scan_users(char *users_path, char *user_priv) +{ + DIR *d; + DIR *d2; + char user_path[MAX_PATH], src_path[MAX_PATH], dest_path[MAX_PATH], dest_rel_path[MAX_PATH], src_mask[MAX_PATH], prepare_target_path[MAX_PATH]; + char prepare_target_cmd[8192]; - while(keep_going) { - de = readdir(d); - if (de == NULL) { - break; - } - if (de->d_type == DT_DIR && strcmp(de->d_name, "..") && strcmp(de->d_name, ".")) { - snprintf(path, sizeof(path), "/home/%s/www/", de->d_name); - d2 = opendir(path); - if (d2 == NULL) { - //has no www dir or is not readable - printlogf(LOG_NORMAL, "skipping %s. it has no readable www directory.", de->d_name); - continue; - } - closedir(d2); - printlogf(LOG_NORMAL, "watching %s's www directory (%s)", de->d_name, path); - add_dirwatch(path, de->d_name, true, -1); + struct stat *de_stat; - snprintf(destpath, sizeof(destpath), "%s/%s/", option_target, de->d_name); - rsync(path, destpath, true); - } - } - - closedir(d); - return true; + struct dirent *de; + int sl; + + printlogf(LOG_DEBUG, "Scanning in [%s] for available users", users_path); + d = opendir(users_path); + + if (d == NULL) { + printlogf(LOG_ERROR, "Cannot open [%s]", users_path); + return false; + } else { + snprintf(prepare_target_path, sizeof(prepare_target_path), "%s/%s", option_target, users_path); + snprintf(prepare_target_cmd, sizeof(prepare_target_cmd), "mkdir -p %s; chmod 777 %s;", prepare_target_path, prepare_target_path); + printlogf(LOG_DEBUG, "exec [%s]", prepare_target_cmd); + system(prepare_target_cmd); + } + + while (de = readdir(d)) { + snprintf(user_path, sizeof(user_path), "%s/%s", users_path, de->d_name); + + if ((is_dir(user_path) == true) && strcmp(de->d_name, "..") && strcmp(de->d_name, ".")) { + snprintf(src_mask, sizeof(src_mask), "%s/%s/%s", users_path, de->d_name, user_priv); + snprintf(src_path, sizeof(src_path), src_mask, de->d_name); + sl = strlen(src_path); + + if (src_path[sl - 1] == '/') { + src_path[sl - 1] = 0; + sl--; + + if (sl == 0) { + continue; + } + } + + if (!(d2 = opendir(src_path))) { + printlogf(LOG_NORMAL, "Skipping %s. Can not read priv directory.", src_path); + continue; + } else { + printlogf(LOG_NORMAL, "Proccessing priv directory [%s:%s].", src_mask, src_path); + } + + closedir(d2); + + snprintf(dest_path, sizeof(dest_path), "%s%s/%s/", option_target, users_path, de->d_name); + snprintf(dest_rel_path, sizeof(dest_rel_path), "%s/%s/", users_path, de->d_name); + + if (add_dirwatch(src_path, dest_rel_path, true, -1)) { + // Not syncing if skipped + rsync(src_path, dest_path, true); + } + } else { + printlogf(LOG_DEBUG, "*\tSkipping [%s]", de->d_name); + } + } + + printlogf(LOG_DEBUG, "Finished scanning in [%s] for users", users_path); + + closedir(d); + return true; } -/** +/** * Prints the help text and exits 0. - * + * * @param arg0 argv[0] to show what lsyncd was called with. */ -void print_help(char *arg0) { - printf("\n"); - printf("USAGE: %s [OPTION]... SOURCE TARGET\n", arg0); - printf("\n"); - printf("SOURCE: a directory to watch and rsync.\n"); - printf(" specify special \"%%userwww\" to scan all users in /home and watch their www directories. \n"); - printf("\n"); - printf("TARGET: can be any name accepted by rsync. e.g. \"foohost::barmodule/\"\n"); - printf("\n"); +void print_help(char *arg0) +{ + printf("\n"); + printf("USAGE: %s [OPTION]... SOURCE TARGET\n", arg0); + printf("\n"); + printf("SOURCE: a directory to watch and rsync.\n"); + printf(" specify special \"%users%\" to monitor all users folders. \n"); + printf("\n"); + printf("TARGET: can be any name accepted by rsync. e.g. \"foohost::barmodule/\"\n"); + printf("\n"); printf("OPTIONS:\n"); printf(" --debug Log debug messages\n"); - printf(" --dryrun Do not call rsync, run dry only\n"); + printf(" --dryrun Do not call rsync, run dry only\n"); printf(" --exclude-from FILE Exclude file handlet to rsync (DEFAULT: None)\n"); printf(" --help Print this help text and exit.\n"); printf(" --logfile FILE Put log here (DEFAULT: /var/log/lsyncd)\n"); @@ -684,169 +832,198 @@ void print_help(char *arg0) { printf(" --rsync-binary FILE Call this binary to sync (DEFAULT: /usr/bin/rsync)\n"); printf(" --scarce Only log errors\n"); printf(" --version Print version an exit.\n"); - printf("\n"); + printf("\n"); printf("Take care that lsyncd is allowed to write to the logfile specified.\n"); printf("\n"); printf("EXCLUDE FILE: \n"); printf(" The exclude file may have either filebased general masks like \"*.php\" without directory specifications,\n"); printf(" or exclude complete directories like \"Data/\". lsyncd will recognize directory excludes by the trailing '/'\n"); printf(" and will not add watches of directories of exactly such name including sub-directories of them.\n"); - printf(" Please do not try to use more sophisticated exclude masks like \"Data/*.dat\" or \"Da*a/\", \"Data/Volatile/\" etc.\n"); + printf(" Please do not try to use more sophisticated exclude masks like \"Data/*.dat\" or \"Da*a/\", \"Data/Volatile/\" etc.\n"); printf(" This will not work like you would expect it to.\n"); - printf("\n"); - printf("LICENSE\n"); - printf(" GPLv2 or any later version. See COPYING\n"); - printf("\n"); - exit(0); + printf("\n"); + printf("LICENSE\n"); + printf(" GPLv2 or any later version. See COPYING\n"); + printf("\n"); + exit(0); } /** * Parses the command line options. */ -bool parse_options(int argc, char **argv) { - static struct option long_options[] = { - {"debug", 0, &loglevel, 1}, - {"dryrun", 0, &flag_dryrun, 1}, - {"exclude-from", 1, NULL, 0}, - {"help", 0, NULL, 0}, - {"logfile", 1, NULL, 0}, - {"no-daemon", 0, &flag_nodaemon, 1}, - {"rsync-binary", 1, NULL, 0}, - {"scarce", 0, &loglevel, 3}, - {"version", 0, NULL, 0}, - {0, 0, 0, 0} - }; - int c; +bool parse_options(int argc, char **argv) +{ - while (1) { - int oi = 0; - c = getopt_long_only(argc, argv, "", long_options, &oi); - if (c == -1) { - break; - } - if (c == '?') { - return false; - } - if (c == 0) { // longoption - if (!strcmp("help", long_options[oi].name)) { - print_help(argv[0]); - } - if (!strcmp("version", long_options[oi].name)) { - printf("Version: %d.%d\n", VER_MAJOR, VER_MINOR); - exit(0); - } - if (!strcmp("logfile", long_options[oi].name)) { - logfile = s_malloc(strlen(optarg) + 1); - strcpy(logfile, optarg); - } - if (!strcmp("exclude-from", long_options[oi].name)) { - exclude_file = s_malloc(strlen(optarg) + 1); - strcpy(exclude_file, optarg); - } - } - } + static struct option long_options[] = { + {"debug", 0, &loglevel, 1 + }, {"dryrun", 0, &flag_dryrun, 1}, {"exclude-from", 1, NULL, 0}, {"help", 0, NULL, 0}, {"logfile", 1, NULL, 0}, {"no-daemon", 0, &flag_nodaemon, 1}, {"rsync-binary", 1, NULL, 0}, {"scarce", 0, &loglevel, 3}, {"version", 0, NULL, 0}, {0, 0, 0, 0} + }; - if (optind + 2 != argc) { - printf("Error: please specify SOURCE and TARGET (see --help)\n"); - exit(-1); - } - option_source = argv[optind]; - option_target = argv[optind + 1]; - printf("syncing %s -> %s\n", option_source, option_target); - return true; + int c; + + while (1) { + int oi = 0; + c = getopt_long_only(argc, argv, "", long_options, &oi); + + if (c == -1) { + break; + } + + if (c == '?') { + return false; + } + + if (c == 0) { // longoption + if (!strcmp("help", long_options[oi].name)) { + print_help(argv[0]); + } + + if (!strcmp("version", long_options[oi].name)) { + printf("Version: %d.%d\n", VER_MAJOR, VER_MINOR); + exit(0); + } + + if (!strcmp("logfile", long_options[oi].name)) { + logfile = s_malloc(strlen(optarg) + 1); + strcpy(logfile, optarg); + } + + if (!strcmp("exclude-from", long_options[oi].name)) { + exclude_file = s_malloc(strlen(optarg) + 1); + strcpy(exclude_file, optarg); + } + } + } + + if (optind + 2 != argc) { + printf("Error: please specify SOURCE and TARGET (see --help)\n"); + exit(-1); + } + + option_source = argv[optind]; + + option_target = argv[optind + 1]; + printf("syncing %s -> %s\n", option_source, option_target); + return true; } /** * Parses the exclude file looking for directory masks to not watch. */ -bool parse_exclude_file() { - FILE * ef; - char line[MAX_PATH]; +bool parse_exclude_file() +{ + FILE * ef; + char line[MAX_PATH]; int sl; ef = fopen(exclude_file, "r"); + if (ef == NULL) { - printlogf(LOG_ERROR, "Meh, cannot open open exclude file '%s'\n", exclude_file); - exit(-1); + printlogf(LOG_ERROR, "Meh, cannot open open exclude file '%s'\n", exclude_file); + exit(-1); } - while(1) { - if (!fgets(line, sizeof(line), ef)) { - if (feof(ef)) { - fclose(ef); - return true; - } - printlogf(LOG_ERROR, "Reading file '%s' (%d=%s)\n", exclude_file, errno, strerror(errno)); - exit(-1); - } - sl = strlen(line); - if (sl == 0) { - continue; - } + while (1) { + if (!fgets(line, sizeof(line), ef)) { + if (feof(ef)) { + fclose(ef); + return true; + } + + printlogf(LOG_ERROR, "Reading file '%s' (%d=%s)\n", exclude_file, errno, strerror(errno)); + + exit(-1); + } + + sl = strlen(line); + + if (sl == 0) { + continue; + } + if (line[sl - 1] == '\n') { - line[sl - 1] = 0; + line[sl - 1] = 0; sl--; } - if (sl == 0) { - continue; - } - if (line[sl - 1] == '/') { - if (exclude_dir_n + 1 >= MAX_EXCLUDES) { - printlogf(LOG_ERROR, "Too many directory excludes, can only have %d at the most", MAX_EXCLUDES); - exit(-1); - } - line[sl - 1] = 0; + + if (sl == 0) { + continue; + } + + if (line[sl - 1] == '/') { + if (exclude_dir_n + 1 >= MAX_EXCLUDES) { + printlogf(LOG_ERROR, "Too many directory excludes, can only have %d at the most", MAX_EXCLUDES); + exit(-1); + } + + line[sl - 1] = 0; + sl--; - if (sl == 0) { - continue; - } - printlogf(LOG_NORMAL, "Excluding directories of the name '%s'", line); - exclude_dirs[exclude_dir_n] = s_malloc(strlen(line) + 1); - strcpy(exclude_dirs[exclude_dir_n], line); - exclude_dir_n++; - } + + if (sl == 0) { + continue; + } + + exclude_dirs[exclude_dir_n] = s_malloc(strlen(line) + 1); + + strcpy(exclude_dirs[exclude_dir_n], line); + exclude_dir_n++; + printlogf(LOG_NORMAL, "Excluding directory [%s] from syncing.", line); + } } - return true; + + return true; } /** * main */ -int main(int argc, char **argv) { - if (!parse_options(argc, argv)) { - return -1; +int main(int argc, char **argv) +{ + if (!parse_options(argc, argv)) { + printlogf(LOG_ERROR, "Invalid parameters specified!"); + return -1; } + if (exclude_file) { - parse_exclude_file(); - } - - inotf = inotify_init(); - if (inotf == -1) { - printlogf(LOG_ERROR, "Cannot create inotify instance! (%d:%s)", errno, strerror(errno)); - return -1; - } - - if (!flag_nodaemon) { - daemon(0, 0); - } - printlogf(LOG_NORMAL, "Starting up"); - - dir_watch_size = 2; - dir_watches = s_calloc(dir_watch_size, sizeof(struct dir_watch)); - - if (!strcmp(option_source, "%userwww")) { - printlogf(LOG_NORMAL, "do userwww"); - scan_homes(); - } else { - printlogf(LOG_NORMAL, "watching %s", option_source); - add_dirwatch(option_source, "", true, -1); - rsync(option_source, option_target, true); + parse_exclude_file(); } - printlogf(LOG_NORMAL, "---entering normal operation---"); + inotf = inotify_init(); + + if (inotf == -1) { + printlogf(LOG_ERROR, "Cannot create inotify instance! (%d:%s)", errno, strerror(errno)); + return -1; + } + + if (!flag_nodaemon) { + daemon(0, 0); + } + + printlogf(LOG_NORMAL, "Starting up"); + + dir_watch_size = 2; + dir_watches = s_calloc(dir_watch_size, sizeof(struct dir_watch)); + + if (!strcmp(option_source, "%users%")) { + printlogf(LOG_NORMAL, "Switching to users mode"); + scan_users("/home", ""); + scan_users("/scratchbox/users", "home/%s/"); + } else { + printlogf(LOG_NORMAL, "Watching %s", option_source); + + if (add_dirwatch(option_source, "", true, -1) == -2) { + printlogf(LOG_ERROR, "Adding folder skipped, probably excluded. Nothing left to to, exiting."); + exit (-1); + } + + rsync(option_source, option_target, true); + } + + printlogf(LOG_NORMAL, "--- Entering normal operation with [%d] monitored directories ---", dir_watch_num); signal(SIGTERM, catch_alarm); - master_loop(); - - return 0; + master_loop(); + + return 0; }