astyled source

This commit is contained in:
Axel Kittenberger 2008-08-12 11:51:46 +00:00
parent 814c133e82
commit 846ec0acb9

379
lsyncd.c
View File

@ -1,10 +1,12 @@
/** /**
* lsyncd.c Live (Mirror) Syncing Demon * lsyncd.c Live (Mirror) Syncing Demon
* *
* License: GPLv2 (see COPYING) or any later version * License: GPLv2 (see COPYING) or any later version
* *
* Authors: Axel Kittenberger <axel.kittenberger@univie.ac.at> * Authors: Axel Kittenberger <axel.kittenberger@univie.ac.at>
*/ * Eugene Sanivsky <eugenesan@gmail.com>
*/
#include "config.h" #include "config.h"
#include <sys/types.h> #include <sys/types.h>
@ -62,7 +64,7 @@ char * option_target = NULL;
/** /**
* Option: rsync binary to call. * 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. * Option: the exclude-file to pass to rsync.
@ -77,6 +79,7 @@ char * exclude_file = NULL;
/** /**
* Structure to store the directory watches of the deamon. * Structure to store the directory watches of the deamon.
*/ */
struct dir_watch { struct dir_watch {
/** /**
* The watch descriptor returned by kernel. * The watch descriptor returned by kernel.
@ -110,6 +113,7 @@ struct dir_watch {
* Structure to store strings for the diversve Inotfy masked events. * Structure to store strings for the diversve Inotfy masked events.
* Actually used for compfortable debugging only. * Actually used for compfortable debugging only.
*/ */
struct inotify_mask_text { struct inotify_mask_text {
int mask; int mask;
char const * text; char const * text;
@ -119,6 +123,7 @@ struct inotify_mask_text {
* A constant that assigns every inotify mask a printable string. * A constant that assigns every inotify mask a printable string.
* Used for debugging. * Used for debugging.
*/ */
struct inotify_mask_text mask_texts[] = { struct inotify_mask_text mask_texts[] = {
{ IN_ACCESS, "ACCESS" }, { IN_ACCESS, "ACCESS" },
{ IN_ATTRIB, "ATTRIB" }, { IN_ATTRIB, "ATTRIB" },
@ -138,6 +143,7 @@ struct inotify_mask_text mask_texts[] = {
/** /**
* Holds an allocated array of all directories watched. * Holds an allocated array of all directories watched.
*/ */
struct dir_watch *dir_watches; struct dir_watch *dir_watches;
/** /**
@ -153,35 +159,36 @@ int dir_watch_num = 0;
/** /**
* lsyncd will log into this file/stream. * lsyncd will log into this file/stream.
*/ */
char * logfile = "/var/log/lsyncd"; char *logfile = "/var/log/lsyncd";
/** /**
* The inotify instance. * The inotify instance.
*/ */
int inotf; int inotf;
/** /**
* Array of strings of directory names to include. * Array of strings of directory names to include.
* This is limited to MAX_EXCLUDES. * This is limited to MAX_EXCLUDES.
* It's not worth to code a dynamic size handling... * It's not worth to code a dynamic size handling...
*/ */
#define MAX_EXCLUDES 256 #define MAX_EXCLUDES 256
char * exclude_dirs[MAX_EXCLUDES] = {NULL, }; char *exclude_dirs[MAX_EXCLUDES] = {NULL, };
int exclude_dir_n = 0; int exclude_dir_n = 0;
volatile sig_atomic_t keep_going = 1; volatile sig_atomic_t keep_going = 1;
void catch_alarm(int sig) { void catch_alarm(int sig)
{
keep_going = 0; keep_going = 0;
} }
/** /**
* Prints a message to the log stream, preceding a timestamp. * Prints a message to the log stream, preceding a timestamp.
* Otherwise it behaves like printf(); * Otherwise it behaves like printf();
*/ */
void printlogf(int level, const char *fmt, ...) { void printlogf(int level, const char *fmt, ...)
{
va_list ap; va_list ap;
char * ct; char * ct;
time_t mtime; time_t mtime;
@ -193,6 +200,7 @@ void printlogf(int level, const char *fmt, ...) {
if (!flag_nodaemon) { if (!flag_nodaemon) {
flog = fopen(logfile, "a"); flog = fopen(logfile, "a");
if (flog == NULL) { if (flog == NULL) {
printf("cannot open logfile!\n"); printf("cannot open logfile!\n");
exit(-1); exit(-1);
@ -202,16 +210,27 @@ void printlogf(int level, const char *fmt, ...) {
} }
va_start(ap, fmt); va_start(ap, fmt);
time(&mtime); time(&mtime);
ct = ctime(&mtime); ct = ctime(&mtime);
ct[strlen(ct) - 1] = 0; // cut trailing \n ct[strlen(ct) - 1] = 0; // cut trailing \n
fprintf(flog, "%s: ", ct); fprintf(flog, "%s: ", ct);
switch (level) { switch (level) {
case LOG_DEBUG : break;
case LOG_NORMAL : break; case LOG_DEBUG :
case LOG_ERROR : fprintf(flog, "ERROR :"); break; break;
case LOG_NORMAL :
break;
case LOG_ERROR :
fprintf(flog, "ERROR :");
break;
} }
vfprintf(flog, fmt, ap); vfprintf(flog, fmt, ap);
fprintf(flog, "\n"); fprintf(flog, "\n");
va_end(ap); va_end(ap);
@ -220,6 +239,23 @@ void printlogf(int level, const char *fmt, ...) {
} }
} }
/**
* 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 * "secured" malloc, meaning the deamon shall kill itself
* in case of out of memory. * in case of out of memory.
@ -230,36 +266,45 @@ void printlogf(int level, const char *fmt, ...) {
* memory, but kill a process to ensure memory will be * memory, but kill a process to ensure memory will be
* available. * available.
*/ */
void *s_malloc(size_t size) { void *s_malloc(size_t size)
{
void *r = malloc(size); void *r = malloc(size);
if (r == NULL) { if (r == NULL) {
printlogf(LOG_ERROR, "Out of memory!"); printlogf(LOG_ERROR, "Out of memory!");
exit(-1); exit(-1);
} }
return r; return r;
} }
/** /**
* "secured" calloc. * "secured" calloc.
*/ */
void *s_calloc(size_t nmemb, size_t size) { void *s_calloc(size_t nmemb, size_t size)
{
void *r = calloc(nmemb, size); void *r = calloc(nmemb, size);
if (r == NULL) { if (r == NULL) {
printlogf(LOG_ERROR, "out of memory!"); printlogf(LOG_ERROR, "out of memory!");
exit(-1); exit(-1);
} }
return r; return r;
} }
/** /**
* "secured" realloc. * "secured" realloc.
*/ */
void *s_realloc(void *ptr, size_t size) { void *s_realloc(void *ptr, size_t size)
{
void *r = realloc(ptr, size); void *r = realloc(ptr, size);
if (r == NULL) { if (r == NULL) {
printlogf(LOG_ERROR, "out of memory!"); printlogf(LOG_ERROR, "out of memory!");
exit(-1); exit(-1);
} }
return r; return r;
} }
@ -271,41 +316,60 @@ void *s_realloc(void *ptr, size_t size) {
* @param dest Destination string, * @param dest Destination string,
* @param recursive If true -r will be handled on, -d (single directory) otherwise * @param recursive If true -r will be handled on, -d (single directory) otherwise
*/ */
bool rsync(char const * src, const char * dest, bool recursive) { bool rsync(char const *src, const char *dest, bool recursive)
{
pid_t pid; pid_t pid;
int status; int status;
char const * opts = recursive ? "-ltr" : "-ltd"; char const *opts, *rsync_debug;
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 (LOG_DEBUG == 1) {
rsync_debug="-P";
} else {
rsync_debug="";
}
if (exclude_file) { if (exclude_file) {
printlogf(LOG_DEBUG, "exec %s(%s,%s,%s,%s,%s,%s)", rsync_binary, "--delete", opts, "--exclude-from", exclude_file, src, dest); 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 { } else {
printlogf(LOG_DEBUG, "exec %s(%s,%s,%s,%s)", rsync_binary, "--delete", opts, src, dest); printlogf(LOG_DEBUG, "exec %s(%s,%s,%s,%s,%s)", rsync_binary, rsync_debug, "--delete", opts, src, dest);
} }
if (flag_dryrun) { if (flag_dryrun) {
printlogf(LOG_DEBUG, "Rsync skipped due to dry mode.");
return true; return true;
} }
pid = fork(); pid = fork();
if (pid == 0) { if (pid == 0) {
if (!flag_nodaemon) { if (!flag_nodaemon) {
freopen(logfile, "a", stdout); freopen(logfile, "a", stdout);
freopen(logfile, "a", stderr); freopen(logfile, "a", stderr);
} }
if (exclude_file) { if (exclude_file) {
execl(rsync_binary, rsync_binary, "--delete", opts, "--exclude-from", exclude_file, src, dest, NULL); execl(rsync_binary, rsync_binary, "--delete", opts, "--exclude-from", exclude_file, src, dest, NULL);
} else { } else {
execl(rsync_binary, rsync_binary, "--delete", opts, src, dest, NULL); e xecl(rsync_binary, rsync_binary, "--delete", opts, src, dest, NULL);
} }
printlogf(LOG_ERROR, "oh my god, execl returned!"); printlogf(LOG_ERROR, "oh my god, execl returned!");
exit(-1); exit(-1);
} }
waitpid(pid, &status, 0); waitpid(pid, &status, 0);
printlogf(LOG_DEBUG, "Rsync of [%s] -> [%s] finished", src, dest);
return true; return true;
} }
/** /**
* Adds a directory to watch * Adds a directory to watch
* *
@ -316,7 +380,8 @@ bool rsync(char const * src, const char * dest, bool recursive) {
* *
* @return index to dir_watches of the new dir, -1 on error. * @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 add_watch(char const *pathname, char const *dirname, char const *destname, int parent)
{
int wd; int wd;
char * nn; char * nn;
int newdw; int newdw;
@ -331,7 +396,7 @@ int add_watch(char const * pathname, char const * dirname, char const * destname
} }
// look if an unused slot can be found. // look if an unused slot can be found.
for(newdw = 0; newdw < dir_watch_num; newdw++) { for (newdw = 0; newdw < dir_watch_num; newdw++) {
if (dir_watches[newdw].wd < 0) { if (dir_watches[newdw].wd < 0) {
break; break;
} }
@ -342,10 +407,12 @@ int add_watch(char const * pathname, char const * dirname, char const * destname
dir_watch_size *= 2; dir_watch_size *= 2;
dir_watches = s_realloc(dir_watches, dir_watch_size * sizeof(struct dir_watch)); dir_watches = s_realloc(dir_watches, dir_watch_size * sizeof(struct dir_watch));
} }
dir_watch_num++; dir_watch_num++;
} }
dir_watches[newdw].wd = wd; dir_watches[newdw].wd = wd;
dir_watches[newdw].parent = parent; dir_watches[newdw].parent = parent;
nn = s_malloc(strlen(dirname) + 1); nn = s_malloc(strlen(dirname) + 1);
@ -363,7 +430,6 @@ int add_watch(char const * pathname, char const * dirname, char const * destname
return newdw; return newdw;
} }
/** /**
* Builds the abolute path name of a given directory beeing watched. * Builds the abolute path name of a given directory beeing watched.
* *
@ -377,24 +443,25 @@ bool buildpath(char *pathname,
int pathsize, int pathsize,
int watch, int watch,
char const *name, char const *name,
char const * prefix) { char const *prefix)
int j, k, p, ps; {
int j, k, p, ps;
pathname[0] = 0; pathname[0] = 0;
if (prefix) { if (prefix) {
strcat(pathname, prefix); strcat(pathname, prefix);
} }
// count how big the parent stack is // count how big the parent stack is
for(p = watch, ps = 0; p != -1; p = dir_watches[p].parent, ps++) { for (p = watch, ps = 0; p != -1; p = dir_watches[p].parent, ps++) {}
}
// now add the parent paths from back to front // now add the parent paths from back to front
for(j = ps; j > 0; j--) { for (j = ps; j > 0; j--) {
char * name; char * name;
// go j steps behind stack // go j steps behind stack
for(p = watch, k = 0; k + 1 < j; p = dir_watches[p].parent, k++) {
} 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; name = (prefix && dir_watches[p].destname) ? dir_watches[p].destname : dir_watches[p].dirname;
@ -402,7 +469,9 @@ bool buildpath(char *pathname,
printlogf(LOG_ERROR, "path too long %s/...", name); printlogf(LOG_ERROR, "path too long %s/...", name);
return false; return false;
} }
strcat(pathname, name); strcat(pathname, name);
strcat(pathname, "/"); strcat(pathname, "/");
} }
@ -411,6 +480,7 @@ bool buildpath(char *pathname,
printlogf(LOG_ERROR, "path too long %s//%s", pathname, name); printlogf(LOG_ERROR, "path too long %s//%s", pathname, name);
return false; return false;
} }
strcat(pathname, name); strcat(pathname, name);
} }
@ -426,48 +496,64 @@ bool buildpath(char *pathname,
* @param parent If not -1, the index in dir_watches to the parent directory already watched. * @param parent If not -1, the index in dir_watches to the parent directory already watched.
* Must have absolute path if parent == -1. * Must have absolute path if parent == -1.
*/ */
bool add_dirwatch(char const * dirname, char const * destname, bool recursive, int parent) { bool add_dirwatch(char const *dirname, char const *destname, bool recursive, int parent)
{
DIR *d; DIR *d;
struct dirent *de; struct dirent *de;
int dw, i; int dw, i;
char pathname[MAX_PATH]; char pathname[MAX_PATH], fullpath[MAX_PATH];
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)) { if (!buildpath(pathname, sizeof(pathname), parent, dirname, NULL)) {
printlogf(LOG_ERROR, "Building path for [%s] failed.", dirname);
return false; return false;
} }
for (i = 0; i < exclude_dir_n; i++) { for (i = 0; i < exclude_dir_n; i++) {
if (!strcmp(dirname, exclude_dirs[i])) { // TODO explain
return true; 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;
} }
} }
dw = add_watch(pathname, dirname, destname, parent); dw = add_watch(pathname, dirname, destname, parent);
if (dw == -1) { if (dw == -1) {
printlogf(LOG_ERROR, "add_watch(%s)failed.", dirname);
return false; return false;
} }
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)) { if (strlen(pathname) + strlen(dirname) + 2 > sizeof(pathname)) {
printlogf(LOG_ERROR, "pathname too long %s//%s", pathname, dirname); printlogf(LOG_ERROR, "pathname too long %s//%s", pathname, dirname);
return false; return false;
} }
d = opendir(pathname); d = opendir(pathname);
if (d == NULL) { if (d == NULL) {
printlogf(LOG_ERROR, "cannot open dir %s.", dirname); printlogf(LOG_ERROR, "cannot open dir %s.", dirname);
return false; return false;
} }
while(keep_going) { while (de = readdir(d)) {
de = readdir(d); sprintf(fullpath, "%s/%s", pathname, de->d_name);
if (de == NULL) { //printlogf(LOG_DEBUG, "Checking if [%s] is a folder.", fullpath);
break; // why was DT_DIR not good?
}
if (de->d_type == DT_DIR && strcmp(de->d_name, "..") && strcmp(de->d_name, ".")) { 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); 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); closedir(d);
return true; return true;
@ -480,19 +566,22 @@ bool add_dirwatch(char const * dirname, char const * destname, bool recursive, i
* @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. * or to be removed itself if name == NULL.
*/ */
bool remove_dirwatch(const char * name, int parent) { bool remove_dirwatch(const char * name, int parent)
{
int i; int i;
int dw; int dw;
if (name) { if (name) {
// look for the child with the name // look for the child with the name
for(i = 0; i < dir_watch_num; i++) { for (i = 0; i < dir_watch_num; i++) {
if (dir_watches[i].wd >= 0 && dir_watches[i].parent == parent && if (dir_watches[i].wd >= 0 && dir_watches[i].parent == parent &&
!strcmp(name, dir_watches[i].dirname)) { !strcmp(name, dir_watches[i].dirname)
) {
dw = i; dw = i;
break; break;
} }
} }
if (i >= dir_watch_num) { if (i >= dir_watch_num) {
printlogf(LOG_ERROR, "Cannot find entry for %s:/:%s :-(", dir_watches[parent].dirname, name); printlogf(LOG_ERROR, "Cannot find entry for %s:/:%s :-(", dir_watches[parent].dirname, name);
return false; return false;
@ -500,13 +589,15 @@ bool remove_dirwatch(const char * name, int parent) {
} else { } else {
dw = parent; dw = parent;
} }
for(i = 0; i < dir_watch_num; i++) {
for (i = 0; i < dir_watch_num; i++) {
if (dir_watches[i].wd >= 0 && dir_watches[i].parent == dw) { if (dir_watches[i].wd >= 0 && dir_watches[i].parent == dw) {
remove_dirwatch(NULL, i); remove_dirwatch(NULL, i);
} }
} }
inotify_rm_watch(inotf, dir_watches[dw].wd); inotify_rm_watch(inotf, dir_watches[dw].wd);
dir_watches[dw].wd = -1; dir_watches[dw].wd = -1;
free(dir_watches[dw].dirname); free(dir_watches[dw].dirname);
@ -516,6 +607,7 @@ bool remove_dirwatch(const char * name, int parent) {
free(dir_watches[dw].destname); free(dir_watches[dw].destname);
dir_watches[dw].destname = NULL; dir_watches[dw].destname = NULL;
} }
return true; return true;
} }
@ -524,52 +616,62 @@ bool remove_dirwatch(const char * name, int parent) {
* *
* @param event The event to handle * @param event The event to handle
*/ */
bool handle_event(struct inotify_event *event) { bool handle_event(struct inotify_event *event)
{
char masktext[255] = {0,}; char masktext[255] = {0,};
char pathname[MAX_PATH]; char pathname[MAX_PATH];
char destname[MAX_PATH]; char destname[MAX_PATH];
int mask = event->mask; int mask = event->mask;
int i; int i;
struct inotify_mask_text *p; struct inotify_mask_text *p;
if (IN_IGNORED & event->mask) { if (IN_IGNORED & event->mask) {
return true; return true;
} }
for (i = 0; i < exclude_dir_n; i++) { for (i = 0; i < exclude_dir_n; i++) {
if (!strcmp(event->name, exclude_dirs[i])) { if (!strcmp(event->name, exclude_dirs[i])) {
printlogf(LOG_DEBUG, "Event on [%s] ignored due to exclude rules", event->name);
return true; return true;
} }
} }
for(p = mask_texts; p->mask; p++) { for (p = mask_texts; p->mask; p++) {
if (mask & p->mask) { if (mask & p->mask) {
if (strlen(masktext) + strlen(p->text) + 3 >= sizeof(masktext)) { if (strlen(masktext) + strlen(p->text) + 3 >= sizeof(masktext)) {
printlogf(LOG_ERROR, "bufferoverflow in handle_event"); printlogf(LOG_ERROR, "bufferoverflow in handle_event");
return false; return false;
} }
if (*masktext) { if (*masktext) {
strcat(masktext, ", "); strcat(masktext, ", ");
} }
strcat(masktext, p->text); strcat(masktext, p->text);
} }
} }
for(i = 0; i < dir_watch_num; i++) { for (i = 0; i < dir_watch_num; i++) {
if (dir_watches[i].wd == event->wd) { if (dir_watches[i].wd == event->wd) {
break; break;
} }
} }
if (i >= dir_watch_num) { if (i >= dir_watch_num) {
printlogf(LOG_ERROR, "received unkown inotify event :-(%d)", event->mask); printlogf(LOG_ERROR, "received unkown inotify event :-(%d)", event->mask);
return false; return false;
} }
if (((IN_CREATE) & event->mask) && (IN_ISDIR & event->mask)) {
if (((IN_CREATE | IN_MOVED_TO) & event->mask) && (IN_ISDIR & event->mask)) {
add_dirwatch(event->name, NULL, false, i); 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)) { if (((IN_DELETE | IN_MOVED_FROM) & event->mask) && (IN_ISDIR & event->mask)) {
remove_dirwatch(event->name, i); remove_dirwatch(event->name, i);
} }
@ -577,6 +679,7 @@ bool handle_event(struct inotify_event *event) {
if (!buildpath(pathname, sizeof(pathname), i, NULL, NULL)) { if (!buildpath(pathname, sizeof(pathname), i, NULL, NULL)) {
return false; return false;
} }
if (!buildpath(destname, sizeof(destname), i, NULL, option_target)) { if (!buildpath(destname, sizeof(destname), i, NULL, option_target)) {
return false; return false;
} }
@ -584,36 +687,50 @@ bool handle_event(struct inotify_event *event) {
// call rsync to propagate changes in the directory // call rsync to propagate changes in the directory
if ((IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | IN_MOVED_TO | IN_MOVED_FROM) & event->mask) { 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); 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); rsync(pathname, destname, false);
} }
}
return 0; return 0;
} }
/** /**
* The control loop waiting for inotify events. * The control loop waiting for inotify events.
*/ */
bool master_loop() { bool master_loop()
{
char buf[INOTIFY_BUF_LEN]; char buf[INOTIFY_BUF_LEN];
int len, i = 0; int len, i = 0;
while (keep_going) { while (keep_going) {
len = read (inotf, buf, INOTIFY_BUF_LEN); len = read (inotf, buf, INOTIFY_BUF_LEN);
if (len < 0) { if (len < 0) {
printlogf(LOG_ERROR, "failed to read from inotify (%d:%s)", errno, strerror(errno)); printlogf(LOG_ERROR, "failed to read from inotify (%d:%s)", errno, strerror(errno));
return false; return false;
} }
if (len == 0) { if (len == 0) {
printlogf(LOG_ERROR, "eof?"); printlogf(LOG_ERROR, "eof?");
return false; return false;
} }
i = 0; i = 0;
while (i < len) { while (i < len) {
struct inotify_event *event = (struct inotify_event *) &buf[i]; struct inotify_event *event = (struct inotify_event *) &buf[i];
printlogf(LOG_DEBUG, "Event [%d] started", i);
handle_event(event); handle_event(event);
printlogf(LOG_DEBUG, "Event [%d] finished", i);
i += sizeof(struct inotify_event) + event->len; i += sizeof(struct inotify_event) + event->len;
} }
} }
return true; return true;
} }
@ -621,41 +738,71 @@ bool master_loop() {
* 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. * Syncs this dir immediately, and adds watches to it.
*/ */
bool scan_homes() { bool scan_users(char *users_path, char *user_priv)
{
DIR *d; DIR *d;
DIR *d2; DIR *d2;
char path[MAX_PATH]; 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 destpath[MAX_PATH]; char prepare_target_cmd[8192];
struct stat *de_stat;
struct dirent *de; struct dirent *de;
int sl;
printlogf(LOG_DEBUG, "Scanning in [%s] for available users", users_path);
d = opendir(users_path);
d = opendir("/home");
if (d == NULL) { if (d == NULL) {
printlogf(LOG_ERROR, "Cannot open /home"); printlogf(LOG_ERROR, "Cannot open [%s]", users_path);
return false; 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(keep_going) { while (de = readdir(d)) {
de = readdir(d); snprintf(user_path, sizeof(user_path), "%s/%s", users_path, de->d_name);
if (de == NULL) {
break; 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);
if (de->d_type == DT_DIR && strcmp(de->d_name, "..") && strcmp(de->d_name, ".")) { snprintf(src_path, sizeof(src_path), src_mask, de->d_name);
snprintf(path, sizeof(path), "/home/%s/www/", de->d_name); sl = strlen(src_path);
d2 = opendir(path);
if (d2 == NULL) { if (src_path[sl - 1] == '/') {
//has no www dir or is not readable src_path[sl - 1] = 0;
printlogf(LOG_NORMAL, "skipping %s. it has no readable www directory.", de->d_name); sl--;
if (sl == 0) {
continue; continue;
} }
closedir(d2); }
printlogf(LOG_NORMAL, "watching %s's www directory (%s)", de->d_name, path);
add_dirwatch(path, de->d_name, true, -1);
snprintf(destpath, sizeof(destpath), "%s/%s/", option_target, de->d_name); if (!(d2 = opendir(src_path))) {
rsync(path, destpath, true); 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); closedir(d);
return true; return true;
} }
@ -665,12 +812,13 @@ bool scan_homes() {
* *
* @param arg0 argv[0] to show what lsyncd was called with. * @param arg0 argv[0] to show what lsyncd was called with.
*/ */
void print_help(char *arg0) { void print_help(char *arg0)
{
printf("\n"); printf("\n");
printf("USAGE: %s [OPTION]... SOURCE TARGET\n", arg0); printf("USAGE: %s [OPTION]... SOURCE TARGET\n", arg0);
printf("\n"); printf("\n");
printf("SOURCE: a directory to watch and rsync.\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(" specify special \"%users%\" to monitor all users folders. \n");
printf("\n"); printf("\n");
printf("TARGET: can be any name accepted by rsync. e.g. \"foohost::barmodule/\"\n"); printf("TARGET: can be any name accepted by rsync. e.g. \"foohost::barmodule/\"\n");
printf("\n"); printf("\n");
@ -703,42 +851,43 @@ void print_help(char *arg0) {
/** /**
* Parses the command line options. * Parses the command line options.
*/ */
bool parse_options(int argc, char **argv) { bool parse_options(int argc, char **argv)
{
static struct option long_options[] = { static struct option long_options[] = {
{"debug", 0, &loglevel, 1}, {"debug", 0, &loglevel, 1
{"dryrun", 0, &flag_dryrun, 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}
{"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; int c;
while (1) { while (1) {
int oi = 0; int oi = 0;
c = getopt_long_only(argc, argv, "", long_options, &oi); c = getopt_long_only(argc, argv, "", long_options, &oi);
if (c == -1) { if (c == -1) {
break; break;
} }
if (c == '?') { if (c == '?') {
return false; return false;
} }
if (c == 0) { // longoption if (c == 0) { // longoption
if (!strcmp("help", long_options[oi].name)) { if (!strcmp("help", long_options[oi].name)) {
print_help(argv[0]); print_help(argv[0]);
} }
if (!strcmp("version", long_options[oi].name)) { if (!strcmp("version", long_options[oi].name)) {
printf("Version: %d.%d\n", VER_MAJOR, VER_MINOR); printf("Version: %d.%d\n", VER_MAJOR, VER_MINOR);
exit(0); exit(0);
} }
if (!strcmp("logfile", long_options[oi].name)) { if (!strcmp("logfile", long_options[oi].name)) {
logfile = s_malloc(strlen(optarg) + 1); logfile = s_malloc(strlen(optarg) + 1);
strcpy(logfile, optarg); strcpy(logfile, optarg);
} }
if (!strcmp("exclude-from", long_options[oi].name)) { if (!strcmp("exclude-from", long_options[oi].name)) {
exclude_file = s_malloc(strlen(optarg) + 1); exclude_file = s_malloc(strlen(optarg) + 1);
strcpy(exclude_file, optarg); strcpy(exclude_file, optarg);
@ -750,7 +899,9 @@ bool parse_options(int argc, char **argv) {
printf("Error: please specify SOURCE and TARGET (see --help)\n"); printf("Error: please specify SOURCE and TARGET (see --help)\n");
exit(-1); exit(-1);
} }
option_source = argv[optind]; option_source = argv[optind];
option_target = argv[optind + 1]; option_target = argv[optind + 1];
printf("syncing %s -> %s\n", option_source, option_target); printf("syncing %s -> %s\n", option_source, option_target);
return true; return true;
@ -759,68 +910,87 @@ bool parse_options(int argc, char **argv) {
/** /**
* Parses the exclude file looking for directory masks to not watch. * Parses the exclude file looking for directory masks to not watch.
*/ */
bool parse_exclude_file() { bool parse_exclude_file()
{
FILE * ef; FILE * ef;
char line[MAX_PATH]; char line[MAX_PATH];
int sl; int sl;
ef = fopen(exclude_file, "r"); ef = fopen(exclude_file, "r");
if (ef == NULL) { if (ef == NULL) {
printlogf(LOG_ERROR, "Meh, cannot open open exclude file '%s'\n", exclude_file); printlogf(LOG_ERROR, "Meh, cannot open open exclude file '%s'\n", exclude_file);
exit(-1); exit(-1);
} }
while(1) { while (1) {
if (!fgets(line, sizeof(line), ef)) { if (!fgets(line, sizeof(line), ef)) {
if (feof(ef)) { if (feof(ef)) {
fclose(ef); fclose(ef);
return true; return true;
} }
printlogf(LOG_ERROR, "Reading file '%s' (%d=%s)\n", exclude_file, errno, strerror(errno)); printlogf(LOG_ERROR, "Reading file '%s' (%d=%s)\n", exclude_file, errno, strerror(errno));
exit(-1); exit(-1);
} }
sl = strlen(line); sl = strlen(line);
if (sl == 0) { if (sl == 0) {
continue; continue;
} }
if (line[sl - 1] == '\n') { if (line[sl - 1] == '\n') {
line[sl - 1] = 0; line[sl - 1] = 0;
sl--; sl--;
} }
if (sl == 0) { if (sl == 0) {
continue; continue;
} }
if (line[sl - 1] == '/') { if (line[sl - 1] == '/') {
if (exclude_dir_n + 1 >= MAX_EXCLUDES) { if (exclude_dir_n + 1 >= MAX_EXCLUDES) {
printlogf(LOG_ERROR, "Too many directory excludes, can only have %d at the most", MAX_EXCLUDES); printlogf(LOG_ERROR, "Too many directory excludes, can only have %d at the most", MAX_EXCLUDES);
exit(-1); exit(-1);
} }
line[sl - 1] = 0; line[sl - 1] = 0;
sl--; sl--;
if (sl == 0) { if (sl == 0) {
continue; continue;
} }
printlogf(LOG_NORMAL, "Excluding directories of the name '%s'", line);
exclude_dirs[exclude_dir_n] = s_malloc(strlen(line) + 1); exclude_dirs[exclude_dir_n] = s_malloc(strlen(line) + 1);
strcpy(exclude_dirs[exclude_dir_n], line); strcpy(exclude_dirs[exclude_dir_n], line);
exclude_dir_n++; exclude_dir_n++;
printlogf(LOG_NORMAL, "Excluding directory [%s] from syncing.", line);
} }
} }
return true; return true;
} }
/** /**
* main * main
*/ */
int main(int argc, char **argv) { int main(int argc, char **argv)
{
if (!parse_options(argc, argv)) { if (!parse_options(argc, argv)) {
printlogf(LOG_ERROR, "Invalid parameters specified!");
return -1; return -1;
} }
if (exclude_file) { if (exclude_file) {
parse_exclude_file(); parse_exclude_file();
} }
inotf = inotify_init(); inotf = inotify_init();
if (inotf == -1) { if (inotf == -1) {
printlogf(LOG_ERROR, "Cannot create inotify instance! (%d:%s)", errno, strerror(errno)); printlogf(LOG_ERROR, "Cannot create inotify instance! (%d:%s)", errno, strerror(errno));
return -1; return -1;
@ -829,21 +999,28 @@ int main(int argc, char **argv) {
if (!flag_nodaemon) { if (!flag_nodaemon) {
daemon(0, 0); daemon(0, 0);
} }
printlogf(LOG_NORMAL, "Starting up"); printlogf(LOG_NORMAL, "Starting up");
dir_watch_size = 2; dir_watch_size = 2;
dir_watches = s_calloc(dir_watch_size, sizeof(struct dir_watch)); dir_watches = s_calloc(dir_watch_size, sizeof(struct dir_watch));
if (!strcmp(option_source, "%userwww")) { if (!strcmp(option_source, "%users%")) {
printlogf(LOG_NORMAL, "do userwww"); printlogf(LOG_NORMAL, "Switching to users mode");
scan_homes(); scan_users("/home", "");
scan_users("/scratchbox/users", "home/%s/");
} else { } else {
printlogf(LOG_NORMAL, "watching %s", option_source); printlogf(LOG_NORMAL, "Watching %s", option_source);
add_dirwatch(option_source, "", true, -1);
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); rsync(option_source, option_target, true);
} }
printlogf(LOG_NORMAL, "---entering normal operation---"); printlogf(LOG_NORMAL, "--- Entering normal operation with [%d] monitored directories ---", dir_watch_num);
signal(SIGTERM, catch_alarm); signal(SIGTERM, catch_alarm);
master_loop(); master_loop();