log to syslog (or alternatively to file).

This commit is contained in:
Axel Kittenberger 2009-02-28 15:58:06 +00:00
parent f4416eba1f
commit 7fc6677f7e

360
lsyncd.c
View File

@ -31,6 +31,7 @@
#include <dirent.h> #include <dirent.h>
#include <getopt.h> #include <getopt.h>
#include <assert.h> #include <assert.h>
#include <syslog.h>
#ifdef XML_CONFIG #ifdef XML_CONFIG
#include <libxml/parser.h> #include <libxml/parser.h>
@ -39,15 +40,16 @@
#define INOTIFY_BUF_LEN (512 * (sizeof(struct inotify_event) + 16)) #define INOTIFY_BUF_LEN (512 * (sizeof(struct inotify_event) + 16))
#define LOG_DEBUG 1 enum log_code {
#define LOG_NORMAL 2 DEBUG = 1,
#define LOG_ERROR 3 NORMAL = 2,
ERROR = 3,
};
/** /**
* Possible Exit codes for this application * Possible Exit codes for this application
*/ */
enum lsyncd_exit_code enum lsyncd_exit_code {
{
LSYNCD_SUCCESS = 0, LSYNCD_SUCCESS = 0,
/* out-of memory */ /* out-of memory */
@ -186,7 +188,7 @@ struct inotify_mask_text {
/** /**
* Global Option: The loglevel is how eloquent lsyncd will be. * Global Option: The loglevel is how eloquent lsyncd will be.
*/ */
int loglevel = LOG_NORMAL; int loglevel = NORMAL;
/** /**
* Global Option: if true no action will actually be called. * Global Option: if true no action will actually be called.
@ -308,16 +310,21 @@ int dir_watch_size = 0;
int dir_watch_num = 0; int dir_watch_num = 0;
/** /**
* lsyncd will log into this file/stream. * If not NULL, this file will be accessed directly to write log messages to.
* If NULL, syslog will be used.
*
* Not as difference that the output of child processes (rsync) will be redirected
* to the logfile if specified, if syslogging the child-output will run into /dev/null.
*
* If flag_nodaemon is present stdout/stderr will be used.
*/ */
char * logfile = "/var/log/lsyncd"; char * logfile = NULL;
/** /**
* 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.
@ -346,58 +353,121 @@ void catch_alarm(int sig)
} }
/** /**
* Prints a message to the log stream, preceding a timestamp. * Just like exit, but logs the exit.
*
* Does not return!
*/
void terminate(int status)
{
if (!flag_nodaemon) {
if (logfile) {
FILE * flog;
flog = fopen(logfile, "a");
if (flog) {
fprintf(flog, "exit!");
fclose(flog);
}
} else {
syslog(LOG_ERR, "exit!");
}
}
exit(status);
}
/**
* Prints a message to either the log stream, preceding a timestamp or
* forwards a message to syslogd.
*
* Otherwise it behaves like printf(); * Otherwise it behaves like printf();
*
* It will also always produce error messages on stderr.
* So when startup fails, the message will be logged
* _and_ displayed on screen. If lsyncd daemonized already,
* stderr will be run into the void of /dev/null.
*/ */
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;
FILE * flog; FILE * flog1 = NULL, * flog2 = NULL;
int sysp = 0;
if (level < loglevel) { if (level < loglevel) {
return; return;
} }
if (!flag_nodaemon) { if (!flag_nodaemon && logfile) {
flog = fopen(logfile, "a"); flog1 = fopen(logfile, "a");
if (flog == NULL) { if (flog1 == NULL) {
fprintf(stderr, "cannot open logfile [%s]!\n", logfile); fprintf(stderr, "cannot open logfile [%s]!\n", logfile);
exit(LSYNCD_FILENOTFOUND); terminate(LSYNCD_FILENOTFOUND);
} }
} else {
flog = stdout;
} }
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 linefeed
fprintf(flog, "%s: ", ct);
switch (level) { switch (level) {
case DEBUG :
case LOG_DEBUG : sysp = LOG_DEBUG;
if (flag_nodaemon) {
flog2 = stdout;
}
break; break;
case LOG_NORMAL : case NORMAL :
sysp = LOG_NOTICE;
if (flag_nodaemon) {
flog2 = stdout;
}
break; break;
case LOG_ERROR : case ERROR :
fprintf(flog, "ERROR: "); sysp = LOG_ERR;
// write on stderr even when daemon.
flog2 = stderr;
break; break;
} }
vfprintf(flog, fmt, ap); // write time on fileoutput
if (flog1) {
fprintf(flog1, "%s: ", ct);
}
fprintf(flog, "\n"); if (level == ERROR) {
if (flog1) {
fprintf(flog1, "ERROR: ");
}
if (flog2) {
fprintf(flog2, "ERROR: ");
}
}
if (flog1) {
vfprintf(flog1, fmt, ap);
} else {
vsyslog(sysp, fmt, ap);
}
if (flog2) {
vfprintf(flog2, fmt, ap);
}
if (flog1) {
fprintf(flog1, "\n");
}
if (flog2) {
fprintf(flog2, "\n");
}
va_end(ap); va_end(ap);
if (!flag_nodaemon) { if (flog1) {
fclose(flog); fclose(flog1);
} }
} }
@ -416,8 +486,8 @@ 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(ERROR, "Out of memory!");
exit(LSYNCD_OUTOFMEMORY); terminate(LSYNCD_OUTOFMEMORY);
} }
return r; return r;
@ -431,8 +501,8 @@ 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(ERROR, "Out of memory!");
exit(LSYNCD_OUTOFMEMORY); terminate(LSYNCD_OUTOFMEMORY);
} }
return r; return r;
@ -446,8 +516,8 @@ 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(ERROR, "Out of memory!");
exit(LSYNCD_OUTOFMEMORY); terminate(LSYNCD_OUTOFMEMORY);
} }
return r; return r;
@ -461,8 +531,8 @@ char *s_strdup(const char* src)
char *s = strdup(src); char *s = strdup(src);
if (s == NULL) { if (s == NULL) {
printlogf(LOG_ERROR, "Out of memory!"); printlogf(ERROR, "Out of memory!");
exit(LSYNCD_OUTOFMEMORY); terminate(LSYNCD_OUTOFMEMORY);
} }
return s; return s;
@ -559,7 +629,7 @@ void dir_conf_add_target(struct dir_conf * dir_conf, char *target)
bool append_tosync_watch(int watch) { bool append_tosync_watch(int watch) {
int i; int i;
printlogf(LOG_DEBUG, "append_tosync_watch(%d)", watch); printlogf(DEBUG, "append_tosync_watch(%d)", watch);
// look if its already in the tosync list. // look if its already in the tosync list.
for(i = 0; i < tosync_pos; i++) { for(i = 0; i < tosync_pos; i++) {
if (tosync[i] == watch) { if (tosync[i] == watch) {
@ -620,9 +690,9 @@ char *parse_option_text(char *text, bool recursive)
break; break;
case 0: // wtf, '%' was at the end of the string! case 0: // wtf, '%' was at the end of the string!
default : // unknown char default : // unknown char
printlogf(LOG_ERROR, printlogf(ERROR,
"don't know how to handle '\%' specifier in \"%s\"!", *text); "don't know how to handle '\%' specifier in \"%s\"!", *text);
exit(LSYNCD_BADPARAMETERS); terminate(LSYNCD_BADPARAMETERS);
} }
} }
return str; return str;
@ -679,7 +749,7 @@ bool action(struct dir_conf * dir_conf,
} }
if (argc >= MAX_ARGS) { if (argc >= MAX_ARGS) {
/* check for error condition */ /* check for error condition */
printlogf(LOG_ERROR, printlogf(ERROR,
"Internal error: too many (>%i) options passed", argc); "Internal error: too many (>%i) options passed", argc);
return false; return false;
} }
@ -688,7 +758,7 @@ bool action(struct dir_conf * dir_conf,
/* debug dump of command-line options */ /* debug dump of command-line options */
//for (i=0; i<argc; ++i) { //for (i=0; i<argc; ++i) {
// printlogf(LOG_DEBUG, "exec parameter %i:%s", i, argv[i]); // printlogf(DEBUG, "exec parameter %i:%s", i, argv[i]);
//} //}
if (flag_dryrun) { if (flag_dryrun) {
@ -699,18 +769,18 @@ bool action(struct dir_conf * dir_conf,
if (pid == 0) { if (pid == 0) {
char * binary = dir_conf->binary ? dir_conf->binary : default_binary; char * binary = dir_conf->binary ? dir_conf->binary : default_binary;
if (!flag_nodaemon) { if (!flag_nodaemon && logfile) {
if (!freopen(logfile, "a", stdout)) { if (!freopen(logfile, "a", stdout)) {
printlogf(LOG_ERROR, "cannot redirect stdout to [%s].", logfile); printlogf(ERROR, "cannot redirect stdout to [%s].", logfile);
} }
if (!freopen(logfile, "a", stderr)) { if (!freopen(logfile, "a", stderr)) {
printlogf(LOG_ERROR, "cannot redirect stderr to [%s].", logfile); printlogf(ERROR, "cannot redirect stderr to [%s].", logfile);
} }
} }
execv(binary, argv); // in a sane world does not return! execv(binary, argv); // in a sane world does not return!
printlogf(LOG_ERROR, "Failed executing [%s]", binary); printlogf(ERROR, "Failed executing [%s]", binary);
exit(LSYNCD_INTERNALFAIL); terminate(LSYNCD_INTERNALFAIL);
} }
for (i=0; i<argc; ++i) { for (i=0; i<argc; ++i) {
@ -722,18 +792,18 @@ bool action(struct dir_conf * dir_conf,
waitpid(pid, &status, 0); waitpid(pid, &status, 0);
assert(WIFEXITED(status)); assert(WIFEXITED(status));
if (WEXITSTATUS(status)==LSYNCD_INTERNALFAIL){ if (WEXITSTATUS(status)==LSYNCD_INTERNALFAIL){
printlogf(LOG_ERROR, printlogf(ERROR,
"Fork exit code of %i, execv failure", "Fork exit code of %i, execv failure",
WEXITSTATUS(status)); WEXITSTATUS(status));
return false; return false;
} else if (WEXITSTATUS(status)) { } else if (WEXITSTATUS(status)) {
printlogf(LOG_NORMAL, printlogf(NORMAL,
"Forked binary process returned non-zero return code: %i", "Forked binary process returned non-zero return code: %i",
WEXITSTATUS(status)); WEXITSTATUS(status));
return false; return false;
} }
printlogf(LOG_DEBUG, "Rsync of [%s] -> [%s] finished", src, dest); printlogf(DEBUG, "Rsync of [%s] -> [%s] finished", src, dest);
return true; return true;
} }
@ -761,7 +831,7 @@ int add_watch(char const * pathname,
IN_MOVED_TO | IN_DONT_FOLLOW | IN_ONLYDIR); IN_MOVED_TO | IN_DONT_FOLLOW | IN_ONLYDIR);
if (wd == -1) { if (wd == -1) {
printlogf(LOG_ERROR, "Cannot add watch %s (%d:%s)", printlogf(ERROR, "Cannot add watch %s (%d:%s)",
pathname, errno, strerror(errno)); pathname, errno, strerror(errno));
return -1; return -1;
} }
@ -856,17 +926,17 @@ bool buildpath(char *pathname,
{ {
int len = builddir(pathname, pathsize, watch, prefix); int len = builddir(pathname, pathsize, watch, prefix);
if (len < 0) { if (len < 0) {
printlogf(LOG_ERROR, "path too long!"); printlogf(ERROR, "path too long!");
return false; return false;
} }
if (dirname) { if (dirname) {
if (pathsize < len + strlen(dirname) + 1) { if (pathsize < len + strlen(dirname) + 1) {
printlogf(LOG_ERROR, "path too long!"); printlogf(ERROR, "path too long!");
return false; return false;
} }
strcat(pathname, dirname); strcat(pathname, dirname);
} }
printlogf(LOG_DEBUG, " BUILDPATH(%d, %s, %s) -> %s", watch, dirname, prefix, pathname); printlogf(DEBUG, " BUILDPATH(%d, %s, %s) -> %s", watch, dirname, prefix, pathname);
return true; return true;
} }
@ -893,11 +963,11 @@ bool rsync_dir(int watch)
status = false; status = false;
continue; continue;
} }
printlogf(LOG_NORMAL, "rsyncing %s --> %s", pathname, destname); printlogf(NORMAL, "rsyncing %s --> %s", pathname, destname);
// call rsync to propagate changes in the directory // call rsync to propagate changes in the directory
if (!action(dir_watches[watch].dir_conf, pathname, destname, false)) { if (!action(dir_watches[watch].dir_conf, pathname, destname, false)) {
printlogf(LOG_ERROR, "Rsync from %s to %s failed", pathname, destname); printlogf(ERROR, "Rsync from %s to %s failed", pathname, destname);
status = false; status = false;
} }
} }
@ -922,7 +992,7 @@ int add_dirwatch(char const * dirname, int parent, struct dir_conf * dir_conf)
int dw, i; int dw, i;
char pathname[PATH_MAX+1]; char pathname[PATH_MAX+1];
printlogf(LOG_DEBUG, "add_dirwatch(%s, p->dirname:%s, ...)", printlogf(DEBUG, "add_dirwatch(%s, p->dirname:%s, ...)",
dirname, dirname,
parent >= 0 ? dir_watches[parent].dirname : "NULL"); parent >= 0 ? dir_watches[parent].dirname : "NULL");
@ -942,14 +1012,14 @@ int add_dirwatch(char const * dirname, int parent, struct dir_conf * dir_conf)
} }
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(ERROR, "pathname too long %s//%s", pathname, dirname);
return -1; return -1;
} }
d = opendir(pathname); d = opendir(pathname);
if (d == NULL) { if (d == NULL) {
printlogf(LOG_ERROR, "cannot open dir %s.", dirname); printlogf(ERROR, "cannot open dir %s.", dirname);
return -1; return -1;
} }
@ -976,7 +1046,7 @@ int add_dirwatch(char const * dirname, int parent, struct dir_conf * dir_conf)
} }
if (isdir && strcmp(de->d_name, "..") && strcmp(de->d_name, ".")) { if (isdir && strcmp(de->d_name, "..") && strcmp(de->d_name, ".")) {
int ndw = add_dirwatch(de->d_name, dw, dir_conf); int ndw = add_dirwatch(de->d_name, dw, dir_conf);
printlogf(LOG_NORMAL, printlogf(NORMAL,
"found new directory: %s in %s -- added on tosync stack.", "found new directory: %s in %s -- added on tosync stack.",
de->d_name, dirname); de->d_name, dirname);
append_tosync_watch(ndw); append_tosync_watch(ndw);
@ -1011,7 +1081,7 @@ bool remove_dirwatch(const char * name, int parent)
} }
if (i >= dir_watch_num) { if (i >= dir_watch_num) {
printlogf(LOG_ERROR, "Cannot find entry for %s:/:%s :-(", printlogf(ERROR, "Cannot find entry for %s:/:%s :-(",
dir_watches[parent].dirname, name); dir_watches[parent].dirname, name);
return false; return false;
} }
@ -1064,11 +1134,11 @@ int get_dirwatch_offset(int wd) {
*/ */
bool process_tosync_stack() bool process_tosync_stack()
{ {
printlogf(LOG_DEBUG, "Processing through tosync stack."); printlogf(DEBUG, "Processing through tosync stack.");
while(tosync_pos > 0) { while(tosync_pos > 0) {
rsync_dir(tosync[--tosync_pos]); rsync_dir(tosync[--tosync_pos]);
} }
printlogf(LOG_DEBUG, "being done with tosync stack"); printlogf(DEBUG, "being done with tosync stack");
return true; return true;
} }
@ -1091,7 +1161,7 @@ bool handle_event(struct inotify_event *event)
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(ERROR, "bufferoverflow in handle_event");
return false; return false;
} }
@ -1102,7 +1172,7 @@ bool handle_event(struct inotify_event *event)
strcat(masktext, p->text); strcat(masktext, p->text);
} }
} }
printlogf(LOG_DEBUG, "inotfy event: %s:%s", masktext, event->name); printlogf(DEBUG, "inotfy event: %s:%s", masktext, event->name);
if (IN_IGNORED & event->mask) { if (IN_IGNORED & event->mask) {
return true; return true;
@ -1116,7 +1186,7 @@ bool handle_event(struct inotify_event *event)
watch = get_dirwatch_offset(event->wd); watch = get_dirwatch_offset(event->wd);
if (watch == -1) { if (watch == -1) {
printlogf(LOG_ERROR, printlogf(ERROR,
"received an inotify event that doesnt match any watched directory :-(%d,%d)", "received an inotify event that doesnt match any watched directory :-(%d,%d)",
event->mask, event->wd); event->mask, event->wd);
return false; return false;
@ -1133,13 +1203,13 @@ bool handle_event(struct inotify_event *event)
if ((IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | if ((IN_CREATE | IN_CLOSE_WRITE | IN_DELETE |
IN_MOVED_TO | IN_MOVED_FROM) & event->mask IN_MOVED_TO | IN_MOVED_FROM) & event->mask
) { ) {
printlogf(LOG_NORMAL, "event %s:%s triggered.", masktext, event->name); printlogf(NORMAL, "event %s:%s triggered.", masktext, event->name);
rsync_dir(watch); // TODO, worry about errors? rsync_dir(watch); // TODO, worry about errors?
if (subwatch >= 0) { // sync through the new created directory as well. if (subwatch >= 0) { // sync through the new created directory as well.
rsync_dir(subwatch); rsync_dir(subwatch);
} }
} else { } else {
printlogf(LOG_DEBUG, "... ignored this event."); printlogf(DEBUG, "... ignored this event.");
} }
process_tosync_stack(); process_tosync_stack();
return true; return true;
@ -1157,12 +1227,12 @@ bool master_loop()
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(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(ERROR, "eof?");
return false; return false;
} }
@ -1187,8 +1257,8 @@ void check_file_exists(const char* filename)
{ {
struct stat st; struct stat st;
if (-1==stat(filename, &st)) { if (-1==stat(filename, &st)) {
printlogf(LOG_ERROR, "File [%s] does not exist\n", filename); printlogf(ERROR, "File [%s] does not exist\n", filename);
exit (LSYNCD_FILENOTFOUND); terminate(LSYNCD_FILENOTFOUND);
} }
} }
@ -1201,8 +1271,8 @@ void check_file_exists(const char* filename)
void check_absolute_path(const char* filename) void check_absolute_path(const char* filename)
{ {
if (filename[0] != '/') { if (filename[0] != '/') {
printlogf(LOG_ERROR, "Filename [%s] is not an absolute path\n", filename); printlogf(ERROR, "Filename [%s] is not an absolute path\n", filename);
exit (LSYNCD_FILENOTFOUND); terminate(LSYNCD_FILENOTFOUND);
} }
} }
@ -1241,16 +1311,13 @@ void print_help(char *arg0)
printf(" --dryrun Do not call any actions, run dry only\n"); printf(" --dryrun Do not call any actions, run dry only\n");
printf(" --exclude-from FILE Exclude file handled to rsync (DEFAULT: None)\n"); printf(" --exclude-from FILE Exclude file handled to rsync (DEFAULT: None)\n");
printf(" --help Print this help text and exit.\n"); printf(" --help Print this help text and exit.\n");
printf(" --logfile FILE Put log here (DEFAULT: %s)\n", printf(" --logfile FILE Put log here (DEFAULT: uses syslog if not specified)\n");
logfile);
printf(" --no-daemon Do not detach, log to stdout/stderr\n"); printf(" --no-daemon Do not detach, log to stdout/stderr\n");
printf(" --pidfile FILE Create a file containing pid of the daemon\n"); printf(" --pidfile FILE Create a file containing pid of the daemon\n");
printf(" --scarce Only log errors\n"); printf(" --scarce Only log errors\n");
printf(" --stubborn Ignore rsync errors on startup.\n"); printf(" --stubborn Ignore rsync errors on startup.\n");
printf(" --version Print version an exit.\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("EXCLUDE FILE: \n");
printf(" The exclude file may have either filebased general masks like \"*.php\" without directory specifications,\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(" or exclude complete directories like \"Data/\". lsyncd will recognize directory excludes by the trailing '/'\n");
@ -1293,8 +1360,8 @@ struct call_option * parse_callopts(xmlNodePtr node) {
xmlStrcmp(cnode->name, BAD_CAST "source") && xmlStrcmp(cnode->name, BAD_CAST "source") &&
xmlStrcmp(cnode->name, BAD_CAST "destination") xmlStrcmp(cnode->name, BAD_CAST "destination")
) { ) {
fprintf(stderr, "error unknown call option type \"%s\"", cnode->name); printlogf(ERROR, "error unknown call option type \"%s\"", cnode->name);
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
opt_n++; opt_n++;
} }
@ -1311,8 +1378,8 @@ struct call_option * parse_callopts(xmlNodePtr node) {
if (!xmlStrcmp(cnode->name, BAD_CAST "option")) { if (!xmlStrcmp(cnode->name, BAD_CAST "option")) {
xc = xmlGetProp(cnode, BAD_CAST "text"); xc = xmlGetProp(cnode, BAD_CAST "text");
if (xc == NULL) { if (xc == NULL) {
fprintf(stderr, "error in config file: text attribute missing from <option/>\n"); printlogf(ERROR, "error in config file: text attribute missing from <option/>\n");
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
asw[opt_n].kind = CO_TEXT; asw[opt_n].kind = CO_TEXT;
asw[opt_n].text = s_strdup((char *) xc); asw[opt_n].text = s_strdup((char *) xc);
@ -1346,47 +1413,47 @@ bool parse_directory(xmlNodePtr node) {
if (!xmlStrcmp(dnode->name, BAD_CAST "source")) { if (!xmlStrcmp(dnode->name, BAD_CAST "source")) {
xc = xmlGetProp(dnode, BAD_CAST "path"); xc = xmlGetProp(dnode, BAD_CAST "path");
if (xc == NULL) { if (xc == NULL) {
fprintf(stderr, "error in config file: attribute path missing from <source>\n"); printlogf(ERROR, "error in config file: attribute path missing from <source>\n");
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
if (dc->source) { if (dc->source) {
fprintf(stderr, "error in config file: cannot have more than one source in one <directory>\n"); printlogf(ERROR, "error in config file: cannot have more than one source in one <directory>\n");
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
// TODO: use realdir() on xc // TODO: use realdir() on xc
dc->source = s_strdup((char *) xc); dc->source = s_strdup((char *) xc);
} else if (!xmlStrcmp(dnode->name, BAD_CAST "target")) { } else if (!xmlStrcmp(dnode->name, BAD_CAST "target")) {
xc = xmlGetProp(dnode, BAD_CAST "path"); xc = xmlGetProp(dnode, BAD_CAST "path");
if (xc == NULL) { if (xc == NULL) {
fprintf(stderr, "error in config file: attribute path missing from <target>\n"); printlogf(ERROR, "error in config file: attribute path missing from <target>\n");
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
dir_conf_add_target(dc, (char *) xc); dir_conf_add_target(dc, (char *) xc);
} else if (!xmlStrcmp(dnode->name, BAD_CAST "binary")) { } else if (!xmlStrcmp(dnode->name, BAD_CAST "binary")) {
xc = xmlGetProp(dnode, BAD_CAST "filename"); xc = xmlGetProp(dnode, BAD_CAST "filename");
if (xc == NULL) { if (xc == NULL) {
fprintf(stderr, "error in config file: attribute filename missing from <binary>\n"); printlogf(ERROR, "error in config file: attribute filename missing from <binary>\n");
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
dc->exclude_file = s_strdup((char *) xc); dc->exclude_file = s_strdup((char *) xc);
} else if (!xmlStrcmp(dnode->name, BAD_CAST "callopts")) { } else if (!xmlStrcmp(dnode->name, BAD_CAST "callopts")) {
if (dc->callopts) { if (dc->callopts) {
fprintf(stderr, "error in config file: there is more than one <callopts> in a <directory>\n"); printlogf(ERROR, "error in config file: there is more than one <callopts> in a <directory>\n");
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
dc->callopts = parse_callopts(dnode); dc->callopts = parse_callopts(dnode);
} else { } else {
fprintf(stderr, "error in config file: unknown node in <directory> \"%s\"\n", dnode->name); printlogf(ERROR, "error in config file: unknown node in <directory> \"%s\"\n", dnode->name);
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
} }
if (!dc->source) { if (!dc->source) {
fprintf(stderr, "error in config file: source missing from <directory>\n"); printlogf(ERROR, "error in config file: source missing from <directory>\n");
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
if (dc->targets[0] == NULL) { if (dc->targets[0] == NULL) {
fprintf(stderr, "error in config file: target missing from <directory>\n"); printlogf(ERROR, "error in config file: target missing from <directory>\n");
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
return true; return true;
} }
@ -1409,29 +1476,29 @@ bool parse_settings(xmlNodePtr node) {
} else if (!xmlStrcmp(snode->name, BAD_CAST "exclude-from")) { } else if (!xmlStrcmp(snode->name, BAD_CAST "exclude-from")) {
xc = xmlGetProp(snode, BAD_CAST "filename"); xc = xmlGetProp(snode, BAD_CAST "filename");
if (xc == NULL) { if (xc == NULL) {
fprintf(stderr, "error in config file: attribute filename missing from <exclude-from/>\n"); printlogf(ERROR, "error in config file: attribute filename missing from <exclude-from/>\n");
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
default_exclude_file = s_strdup((char *) xc); default_exclude_file = s_strdup((char *) xc);
} else if (!xmlStrcmp(snode->name, BAD_CAST "logfile")) { } else if (!xmlStrcmp(snode->name, BAD_CAST "logfile")) {
xc = xmlGetProp(snode, BAD_CAST "filename"); xc = xmlGetProp(snode, BAD_CAST "filename");
if (xc == NULL) { if (xc == NULL) {
fprintf(stderr, "error in config file: attribute filename missing from <logfile/>\n"); printlogf(ERROR, "error in config file: attribute filename missing from <logfile/>\n");
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
logfile = s_strdup((char *) xc); logfile = s_strdup((char *) xc);
} else if (!xmlStrcmp(snode->name, BAD_CAST "binary")) { } else if (!xmlStrcmp(snode->name, BAD_CAST "binary")) {
xc = xmlGetProp(snode, BAD_CAST "filename"); xc = xmlGetProp(snode, BAD_CAST "filename");
if (xc == NULL) { if (xc == NULL) {
fprintf(stderr, "error in config file: attribute filename missing from <binary/>\n"); printlogf(ERROR, "error in config file: attribute filename missing from <binary/>\n");
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
default_binary = s_strdup((char *) xc); default_binary = s_strdup((char *) xc);
} else if (!xmlStrcmp(snode->name, BAD_CAST "pidfile")) { } else if (!xmlStrcmp(snode->name, BAD_CAST "pidfile")) {
xc = xmlGetProp(snode, BAD_CAST "filename"); xc = xmlGetProp(snode, BAD_CAST "filename");
if (xc == NULL) { if (xc == NULL) {
fprintf(stderr, "error in config file: attribute filename missing from <pidfile/>\n"); printlogf(ERROR, "error in config file: attribute filename missing from <pidfile/>\n");
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
pidfile = s_strdup((char *) xc); pidfile = s_strdup((char *) xc);
} else if (!xmlStrcmp(snode->name, BAD_CAST "callopts")) { } else if (!xmlStrcmp(snode->name, BAD_CAST "callopts")) {
@ -1443,8 +1510,8 @@ bool parse_settings(xmlNodePtr node) {
} else if (!xmlStrcmp(snode->name, BAD_CAST "stubborn")) { } else if (!xmlStrcmp(snode->name, BAD_CAST "stubborn")) {
flag_stubborn = 1; flag_stubborn = 1;
} else { } else {
fprintf(stderr, "error unknown node in <settings> \"%s\"", snode->name); printlogf(ERROR, "error unknown node in <settings> \"%s\"", snode->name);
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
} }
return true; return true;
@ -1466,24 +1533,24 @@ bool parse_config(bool fullparse) {
doc = xmlReadFile(conf_filename, NULL, 0); doc = xmlReadFile(conf_filename, NULL, 0);
if (doc == NULL) { if (doc == NULL) {
fprintf(stderr, "error: could not parse config file \"%s\"\n", conf_filename); printlogf(ERROR, "error: could not parse config file \"%s\"\n", conf_filename);
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
root_element = xmlDocGetRootElement(doc); root_element = xmlDocGetRootElement(doc);
// check version specifier // check version specifier
if (xmlStrcmp(root_element->name, BAD_CAST "lsyncd")) { if (xmlStrcmp(root_element->name, BAD_CAST "lsyncd")) {
fprintf(stderr, "error in config file: root node is not \"lsyncd\".\n"); printlogf(ERROR, "error in config file: root node is not \"lsyncd\".\n");
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
xc = xmlGetProp(root_element, BAD_CAST "version"); xc = xmlGetProp(root_element, BAD_CAST "version");
if (xc == NULL) { if (xc == NULL) {
fprintf(stderr, "error in config file: version specifier missing in \"%s\",\n", conf_filename); printlogf(ERROR, "error in config file: version specifier missing in \"%s\",\n", conf_filename);
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
if (xmlStrcmp(xc, BAD_CAST "1") && xmlStrcmp(xc, BAD_CAST "1.25")) { //1.25, backward stuff if (xmlStrcmp(xc, BAD_CAST "1") && xmlStrcmp(xc, BAD_CAST "1.25")) { //1.25, backward stuff
fprintf(stderr, "error in config file: expected a \"1\" versioned file, found \"%s\"\n", xc); printlogf(ERROR, "error in config file: expected a \"1\" versioned file, found \"%s\"\n", xc);
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
for (node = root_element->children; node; node = node->next) { for (node = root_element->children; node; node = node->next) {
@ -1497,8 +1564,8 @@ bool parse_config(bool fullparse) {
parse_directory(node); parse_directory(node);
} }
} else { } else {
fprintf(stderr, "error in config file: unknown node in <lsyncd> \"%s\"\n", node->name); printlogf(ERROR, "error in config file: unknown node in <lsyncd> \"%s\"\n", node->name);
exit(LSYNCD_BADCONFIGFILE); terminate(LSYNCD_BADCONFIGFILE);
} }
} }
@ -1513,7 +1580,7 @@ bool parse_config(bool fullparse) {
/** /**
* Parses the command line options. * Parses the command line options.
* *
* exits() in some cases of badparameters, or on * terminates in some cases of badparameters, or on
* --version or --help * --version or --help
*/ */
void parse_options(int argc, char **argv) void parse_options(int argc, char **argv)
@ -1556,7 +1623,7 @@ void parse_options(int argc, char **argv)
break; break;
} }
if (c == '?') { if (c == '?') {
exit(LSYNCD_BADPARAMETERS); terminate(LSYNCD_BADPARAMETERS);
} }
if (c == 0) { // longoption if (c == 0) { // longoption
if (!strcmp("conf", long_options[oi].name)) { if (!strcmp("conf", long_options[oi].name)) {
@ -1573,7 +1640,7 @@ void parse_options(int argc, char **argv)
if (!strcmp("version", long_options[oi].name)) { if (!strcmp("version", long_options[oi].name)) {
// same here // same here
printf("Version: %s\n", VERSION); printf("Version: %s\n", VERSION);
exit(LSYNCD_SUCCESS); terminate(LSYNCD_SUCCESS);
} }
} }
} }
@ -1594,7 +1661,7 @@ void parse_options(int argc, char **argv)
} }
if (c == '?') { if (c == '?') {
exit(LSYNCD_BADPARAMETERS); terminate(LSYNCD_BADPARAMETERS);
} }
if (c == 0) { // longoption if (c == 0) { // longoption
@ -1620,7 +1687,7 @@ void parse_options(int argc, char **argv)
if (!strcmp("version", long_options[oi].name)) { if (!strcmp("version", long_options[oi].name)) {
printf("Version: %s\n", VERSION); printf("Version: %s\n", VERSION);
exit(LSYNCD_SUCCESS); terminate(LSYNCD_SUCCESS);
} }
} }
} }
@ -1632,27 +1699,27 @@ void parse_options(int argc, char **argv)
bool first_target = true; bool first_target = true;
if (optind + 2 > argc) { if (optind + 2 > argc) {
fprintf(stderr, "Error: please specify SOURCE and at least one TARGET (see --help)\n"); printlogf(ERROR, "Error: please specify SOURCE and at least one TARGET (see --help)\n");
#ifdef XML_CONFIG #ifdef XML_CONFIG
fprintf(stderr, " or at least one <directory> entry in the conf file.\n"); printlogf(ERROR, " or at least one <directory> entry in the conf file.\n");
#endif #endif
exit(LSYNCD_BADPARAMETERS); terminate(LSYNCD_BADPARAMETERS);
} }
odc = new_dir_conf(); odc = new_dir_conf();
/* Resolves relative source path, lsyncd might chdir to / later. */ /* Resolves relative source path, lsyncd might chdir to / later. */
odc->source = realdir(argv[optind]); odc->source = realdir(argv[optind]);
if (!odc->source) { if (!odc->source) {
fprintf(stderr, "Error: Source [%s] not found or not a directory.\n", argv[optind]); printlogf(ERROR, "Error: Source [%s] not found or not a directory.\n", argv[optind]);
exit(LSYNCD_FILENOTFOUND); terminate(LSYNCD_FILENOTFOUND);
} }
for (target = &argv[optind + 1]; *target; target++) { for (target = &argv[optind + 1]; *target; target++) {
dir_conf_add_target(odc, *target); dir_conf_add_target(odc, *target);
if (first_target) { if (first_target) {
printlogf(LOG_NORMAL, "command line options: syncing %s -> %s\n", printlogf(NORMAL, "command line options: syncing %s -> %s\n",
odc->source, *target); odc->source, *target);
first_target = false; first_target = false;
} else { } else {
printlogf(LOG_NORMAL, " and -> %s\n", printlogf(NORMAL, " and -> %s\n",
*target); *target);
} }
} }
@ -1678,8 +1745,8 @@ bool parse_exclude_file(char *filename) {
ef = fopen(filename, "r"); ef = fopen(filename, "r");
if (ef == NULL) { if (ef == NULL) {
printlogf(LOG_ERROR, "Meh, cannot open exclude file '%s'\n", filename); printlogf(ERROR, "Meh, cannot open exclude file '%s'\n", filename);
exit(LSYNCD_FILENOTFOUND); terminate(LSYNCD_FILENOTFOUND);
} }
while (1) { while (1) {
@ -1688,10 +1755,10 @@ bool parse_exclude_file(char *filename) {
fclose(ef); fclose(ef);
return true; return true;
} }
printlogf(LOG_ERROR, "Reading file '%s' (%d:%s)\n", printlogf(ERROR, "Reading file '%s' (%d:%s)\n",
filename, errno, strerror(errno)); filename, errno, strerror(errno));
exit(LSYNCD_FILENOTFOUND); terminate(LSYNCD_FILENOTFOUND);
} }
sl = strlen(line); sl = strlen(line);
@ -1711,10 +1778,10 @@ bool parse_exclude_file(char *filename) {
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, printlogf(ERROR,
"Too many directory excludes, can only have %d at the most", "Too many directory excludes, can only have %d at the most",
MAX_EXCLUDES); MAX_EXCLUDES);
exit(LSYNCD_TOOMANYDIRECTORYEXCLUDES); terminate(LSYNCD_TOOMANYDIRECTORYEXCLUDES);
} }
line[sl - 1] = 0; line[sl - 1] = 0;
@ -1725,7 +1792,7 @@ bool parse_exclude_file(char *filename) {
continue; continue;
} }
printlogf(LOG_NORMAL, "Excluding directories of the name '%s'", line); printlogf(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);
@ -1742,8 +1809,8 @@ bool parse_exclude_file(char *filename) {
void write_pidfile() { void write_pidfile() {
FILE* f = fopen(pidfile, "w"); FILE* f = fopen(pidfile, "w");
if (!f) { if (!f) {
printlogf(LOG_ERROR, "Error: cannot write pidfile [%s]\n", pidfile); printlogf(ERROR, "Error: cannot write pidfile [%s]\n", pidfile);
exit(LSYNCD_FILENOTFOUND); terminate(LSYNCD_FILENOTFOUND);
} }
fprintf(f, "%i\n", getpid()); fprintf(f, "%i\n", getpid());
@ -1756,6 +1823,7 @@ void write_pidfile() {
int main(int argc, char **argv) int main(int argc, char **argv)
{ {
int i; int i;
openlog("lsyncd", LOG_CONS | LOG_PID, LOG_DAEMON);
parse_options(argc, argv); parse_options(argc, argv);
@ -1765,7 +1833,7 @@ int main(int argc, char **argv)
inotf = inotify_init(); inotf = inotify_init();
if (inotf == -1) { if (inotf == -1) {
printlogf(LOG_ERROR, "Cannot create inotify instance! (%d:%s)", printlogf(ERROR, "Cannot create inotify instance! (%d:%s)",
errno, strerror(errno)); errno, strerror(errno));
return LSYNCD_NOINOTIFY; return LSYNCD_NOINOTIFY;
} }
@ -1775,13 +1843,13 @@ int main(int argc, char **argv)
// close stdin/stdout/stderr and // close stdin/stdout/stderr and
// chdir to / // chdir to /
if (daemon(0, 0)) { if (daemon(0, 0)) {
printlogf(LOG_ERROR, "Cannot daemonize! (%d:%s)", printlogf(ERROR, "Cannot daemonize! (%d:%s)",
errno, strerror(errno)); errno, strerror(errno));
return LSYNCD_INTERNALFAIL; return LSYNCD_INTERNALFAIL;
} }
} }
printlogf(LOG_NORMAL, "Starting up"); printlogf(NORMAL, "Starting up");
if (pidfile) { if (pidfile) {
write_pidfile(); write_pidfile();
@ -1795,13 +1863,13 @@ int main(int argc, char **argv)
// add all watches // add all watches
for (i = 0; i < dir_conf_n; i++) { for (i = 0; i < dir_conf_n; i++) {
printlogf(LOG_NORMAL, "watching %s", dir_confs[i].source); printlogf(NORMAL, "watching %s", dir_confs[i].source);
add_dirwatch(dir_confs[i].source, -1, &dir_confs[i]); add_dirwatch(dir_confs[i].source, -1, &dir_confs[i]);
} }
// clears tosync stack again, because the startup // clears tosync stack again, because the startup
// super recursive rsync will handle it eitherway. // super recursive rsync will handle it eitherway.
printlogf(LOG_DEBUG, "dumped tosync stack."); printlogf(DEBUG, "dumped tosync stack.");
tosync_pos = 0; tosync_pos = 0;
// startup recursive sync. // startup recursive sync.
@ -1809,17 +1877,17 @@ int main(int argc, char **argv)
char **target; char **target;
for (target = dir_confs[i].targets; *target; ++target) { for (target = dir_confs[i].targets; *target; ++target) {
if (!action(&dir_confs[i], dir_confs[i].source, *target, true)) { if (!action(&dir_confs[i], dir_confs[i].source, *target, true)) {
printlogf(LOG_ERROR, "Initial rsync from %s to %s failed%s", printlogf(ERROR, "Initial rsync from %s to %s failed%s",
dir_confs[i].source, *target, dir_confs[i].source, *target,
flag_stubborn ? ", but continuing because being stubborn." : "."); flag_stubborn ? ", but continuing because being stubborn." : ".");
if (!flag_stubborn) { if (!flag_stubborn) {
exit(LSYNCD_EXECFAIL); terminate(LSYNCD_EXECFAIL);
} }
} }
} }
} }
printlogf(LOG_NORMAL, printlogf(NORMAL,
"--- Entering normal operation with [%d] monitored directories ---", "--- Entering normal operation with [%d] monitored directories ---",
dir_watch_num); dir_watch_num);