big changes: introduced tosync_stack, now a lot of race conditions should be helped. cp -r works, mv works without workaround. Everything a bit more shinny.

This commit is contained in:
Axel Kittenberger 2008-11-05 15:37:21 +00:00
parent 445292cf4e
commit 32f4b88b9c
1 changed files with 153 additions and 50 deletions

203
lsyncd.c
View File

@ -35,6 +35,11 @@
#define INOTIFY_BUF_LEN (512 * (sizeof(struct inotify_event) + 16))
/**
* Utterly fail if tosync list exceeds this.
*/
#define MAX_TOSYNC 100
#define LOG_DEBUG 1
#define LOG_NORMAL 2
#define LOG_ERROR 3
@ -76,10 +81,19 @@ char * exclude_file = NULL;
*/
char * pidfile = NULL;
/**
* A stack of offset pointers to dir_watches to directories to sync.
*/
int tosync[MAX_TOSYNC];
/**
* The pointer of the current tosync position.
*/
int tosync_pos = 0;
/**
* Structure to store the directory watches of the deamon.
*/
struct dir_watch {
/**
* The watch descriptor returned by kernel.
@ -182,8 +196,6 @@ enum lsyncd_exit_code
LSYNCD_INTERNALFAIL = 255, /* Internal exit code to denote that execv failed */
};
/**
* Array of strings of directory names to include.
@ -353,6 +365,47 @@ char *realdir(const char * dir)
return cs;
}
/**
* Adds a directory to sync.
*
* @param watch the index in dir_watches to the directory.
*/
bool append_tosync_watch(int watch) {
int i;
printlogf(LOG_DEBUG, "append_tosync_watch(%d)", watch);
// look if its already in the tosync list.
for(i = 0; i < tosync_pos; i++) {
if (tosync[i] == watch) {
return true;
}
}
if (tosync_pos + 1 == MAX_TOSYNC) {
printlogf(LOG_ERROR, "Utter fail! tosync stack exceeded (max is %d).", MAX_TOSYNC);
exit(LSYNCD_INTERNALFAIL);
}
tosync[tosync_pos++] = watch;
return true;
}
/**
* Removes a tosync entry in the stack at the position p.
*/
bool remove_tosync_pos(int p) {
int i;
assert(p < tosync_pos);
//TODO improve performance by using memcpy.
for(i = p; i < tosync_pos; i++) {
tosync[i] = tosync[i + 1];
}
tosync_pos--;
return true;
}
/**
* Calls rsync to sync from src to dest.
* Returns after rsync has finished.
@ -389,9 +442,9 @@ bool rsync(char const * src, const char * dest, bool recursive)
}
/* debug dump of command-line options */
for (i=0; i<argc; ++i) {
printlogf(LOG_DEBUG, "exec parameter %i:%s", i, argv[i]);
}
//for (i=0; i<argc; ++i) {
// printlogf(LOG_DEBUG, "exec parameter %i:%s", i, argv[i]);
//}
if (flag_dryrun) {
return true;
@ -555,6 +608,48 @@ bool buildpath(char *pathname,
return true;
}
/**
* Syncs a directory.
*
* @param watch the index in dir_watches to the directory.
*/
bool rsync_dir(int watch)
{
char pathname[PATH_MAX+1];
char destname[PATH_MAX+1];
if (!buildpath(pathname, sizeof(pathname), watch, NULL, NULL)) {
return false;
}
if (!buildpath(destname, sizeof(destname), watch, NULL, option_target)) {
return false;
}
printlogf(LOG_NORMAL, "rsyncing %s --> %s", pathname, destname);
// call rsync to propagate changes in the directory
if (!rsync(pathname, destname, false)) {
// if error on partial rsync, retry with parent dir rsync
// TODO once we fix cp -r, MOVE_TO should be fixed also.
printlogf(LOG_ERROR, "Rsync from %s to %s failed", pathname, destname);
return false;
// exit(LSYNCD_EXECRSYNCFAIL);
// if (dir_watches[i].parent != -1) {
// buildpath(pathname, sizeof(pathname), dir_watches[i].parent, NULL, NULL);
// buildpath(destname, sizeof(destname), dir_watches[i].parent, NULL, option_target);
//
// printlogf(LOG_NORMAL, "Retry Directory resync with %s to %s",
// pathname, destname);
// if (!rsync(pathname, destname, true)) {
// printlogf(LOG_ERROR, "Retry of rsync from %s to %s failed", pathname, destname);
// exit(LSYNCD_EXECRSYNCFAIL);
// }
// }
}
return true;
}
/**
* Adds a dir to watch.
*
@ -563,8 +658,13 @@ bool buildpath(char *pathname,
* @param recursive If true, will also watch all sub-directories.
* @param parent If not -1, the index in dir_watches to the parent directory already watched.
* Must have absolute path if parent == -1.
*
* @return the index in dir_watches off the directory or -1 on fail.
*
* TODO there is actually no useful situation where recursive should be false, parameter should
* be removed.
*/
bool add_dirwatch(char const * dirname, char const * destname, bool recursive, int parent)
int add_dirwatch(char const * dirname, char const * destname, bool recursive, int parent)
{
DIR *d;
@ -575,31 +675,30 @@ bool add_dirwatch(char const * dirname, char const * destname, bool recursive, i
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;
return -1;
}
for (i = 0; i < exclude_dir_n; i++) {
if (!strcmp(dirname, exclude_dirs[i])) {
return true;
return -1;
}
}
dw = add_watch(pathname, dirname, destname, parent);
if (dw == -1) {
return false;
return -1;
}
if (strlen(pathname) + strlen(dirname) + 2 > sizeof(pathname)) {
printlogf(LOG_ERROR, "pathname too long %s//%s", pathname, dirname);
return false;
return -1;
}
d = opendir(pathname);
if (d == NULL) {
printlogf(LOG_ERROR, "cannot open dir %s.", dirname);
return false;
return -1;
}
while (keep_going) {
@ -610,14 +709,16 @@ bool add_dirwatch(char const * dirname, char const * destname, bool recursive, i
}
if (de->d_type == DT_DIR && strcmp(de->d_name, "..") && strcmp(de->d_name, ".")) {
add_dirwatch(de->d_name, NULL, true, dw);
//TODO call sync_dir() to sync the new directory.
int ndw;
ndw = add_dirwatch(de->d_name, NULL, true, dw);
printlogf(LOG_NORMAL, "found new directory: %s/%s -- added on tosync stack.", dirname, de->d_name);
append_tosync_watch(ndw);
}
}
closedir(d);
return true;
return dw;
}
/**
@ -693,6 +794,22 @@ int get_dirwatch_offset(int wd) {
}
}
/**
* Processes through the tosync stack, rysncing all its directories.
*
* TODO: make special logic to determine who is a subdirectory of whom, and maybe optimizie calls.
*/
bool process_tosync_stack()
{
printlogf(LOG_DEBUG, "Processing through tosync stack.");
while(tosync_pos > 0) {
rsync_dir(tosync[--tosync_pos]);
}
printlogf(LOG_DEBUG, "being done with tosync stack");
return true;
}
/**
* Handles an inotify event.
*
@ -701,11 +818,10 @@ int get_dirwatch_offset(int wd) {
bool handle_event(struct inotify_event *event)
{
char masktext[255] = {0,};
char pathname[PATH_MAX+1];
char destname[PATH_MAX+1];
int mask = event->mask;
int i;
int subwatch = -1;
struct inotify_mask_text *p;
@ -742,43 +858,25 @@ bool handle_event(struct inotify_event *event)
}
if (((IN_CREATE | IN_MOVED_TO) & event->mask) && (IN_ISDIR & event->mask)) {
add_dirwatch(event->name, NULL, false, i);
subwatch = 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 ((IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_MOVED_TO | IN_MOVED_FROM) & event->mask) {
printlogf(LOG_NORMAL, "event %s:%s triggered a rsync", masktext, event->name);
rsync_dir(i); // TODO, worry about errors?
if (subwatch >= 0) { // sync through the new created directory as well.
rsync_dir(subwatch);
}
} else {
printlogf(LOG_DEBUG, "... ignored this event.");
}
process_tosync_stack();
// TODO move this part of function to a new function like "sync_dir"
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 (!rsync(pathname, destname, false)) {
// if error on partial rsync, retry with parent dir rsync
// TODO once we fix cp -r, MOVE_TO should be fixed also.
if (dir_watches[i].parent != -1) {
buildpath(pathname, sizeof(pathname), dir_watches[i].parent, NULL, NULL);
buildpath(destname, sizeof(destname), dir_watches[i].parent, NULL, option_target);
printlogf(LOG_NORMAL, "Retry Directory resync with %s to %s",
pathname, destname);
if (!rsync(pathname, destname, true)) {
printlogf(LOG_ERROR, "Retry of rsync from %s to %s failed", pathname, destname);
exit(LSYNCD_EXECRSYNCFAIL);
}
}
}
}
return 0;
return true;
}
/**
@ -1092,6 +1190,11 @@ int main(int argc, char **argv)
printlogf(LOG_NORMAL, "watching %s", option_source);
add_dirwatch(option_source, "", true, -1);
// clear tosync stack again, because the startup super recursive rsync will handle it eitherway.
printlogf(LOG_DEBUG, "dumped tosync stack.");
tosync_pos = 0;
if (!rsync(option_source, option_target, true)) {
printlogf(LOG_ERROR, "Initial rsync from %s to %s failed", option_source, option_target);
exit(LSYNCD_EXECRSYNCFAIL);