From 7fc6677f7e436e9de131db0f335c1d94f1f4aa30 Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Sat, 28 Feb 2009 15:58:06 +0000 Subject: [PATCH] log to syslog (or alternatively to file). --- lsyncd.c | 360 +++++++++++++++++++++++++++++++++---------------------- 1 file changed, 214 insertions(+), 146 deletions(-) diff --git a/lsyncd.c b/lsyncd.c index 70b3359..58b542b 100644 --- a/lsyncd.c +++ b/lsyncd.c @@ -31,6 +31,7 @@ #include #include #include +#include #ifdef XML_CONFIG #include @@ -39,15 +40,16 @@ #define INOTIFY_BUF_LEN (512 * (sizeof(struct inotify_event) + 16)) -#define LOG_DEBUG 1 -#define LOG_NORMAL 2 -#define LOG_ERROR 3 +enum log_code { + DEBUG = 1, + NORMAL = 2, + ERROR = 3, +}; /** * Possible Exit codes for this application */ -enum lsyncd_exit_code -{ +enum lsyncd_exit_code { LSYNCD_SUCCESS = 0, /* out-of memory */ @@ -186,7 +188,7 @@ struct inotify_mask_text { /** * 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. @@ -308,16 +310,21 @@ int dir_watch_size = 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. */ int inotf; - /** * Array of strings of directory names to include. * 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(); + * + * 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, ...) { va_list ap; char * ct; time_t mtime; - FILE * flog; + FILE * flog1 = NULL, * flog2 = NULL; + int sysp = 0; if (level < loglevel) { return; } - if (!flag_nodaemon) { - flog = fopen(logfile, "a"); + if (!flag_nodaemon && logfile) { + flog1 = fopen(logfile, "a"); - if (flog == NULL) { + if (flog1 == NULL) { fprintf(stderr, "cannot open logfile [%s]!\n", logfile); - exit(LSYNCD_FILENOTFOUND); + terminate(LSYNCD_FILENOTFOUND); } - } else { - flog = stdout; } va_start(ap, fmt); time(&mtime); ct = ctime(&mtime); - ct[strlen(ct) - 1] = 0; // cut trailing \n - fprintf(flog, "%s: ", ct); + ct[strlen(ct) - 1] = 0; // cut trailing linefeed switch (level) { - - case LOG_DEBUG : + case DEBUG : + sysp = LOG_DEBUG; + if (flag_nodaemon) { + flog2 = stdout; + } break; - case LOG_NORMAL : + case NORMAL : + sysp = LOG_NOTICE; + if (flag_nodaemon) { + flog2 = stdout; + } break; - case LOG_ERROR : - fprintf(flog, "ERROR: "); + case ERROR : + sysp = LOG_ERR; + // write on stderr even when daemon. + flog2 = stderr; 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); - if (!flag_nodaemon) { - fclose(flog); + if (flog1) { + fclose(flog1); } } @@ -416,8 +486,8 @@ void *s_malloc(size_t size) void *r = malloc(size); if (r == NULL) { - printlogf(LOG_ERROR, "Out of memory!"); - exit(LSYNCD_OUTOFMEMORY); + printlogf(ERROR, "Out of memory!"); + terminate(LSYNCD_OUTOFMEMORY); } return r; @@ -431,8 +501,8 @@ void *s_calloc(size_t nmemb, size_t size) void *r = calloc(nmemb, size); if (r == NULL) { - printlogf(LOG_ERROR, "Out of memory!"); - exit(LSYNCD_OUTOFMEMORY); + printlogf(ERROR, "Out of memory!"); + terminate(LSYNCD_OUTOFMEMORY); } return r; @@ -446,8 +516,8 @@ void *s_realloc(void *ptr, size_t size) void *r = realloc(ptr, size); if (r == NULL) { - printlogf(LOG_ERROR, "Out of memory!"); - exit(LSYNCD_OUTOFMEMORY); + printlogf(ERROR, "Out of memory!"); + terminate(LSYNCD_OUTOFMEMORY); } return r; @@ -461,8 +531,8 @@ char *s_strdup(const char* src) char *s = strdup(src); if (s == NULL) { - printlogf(LOG_ERROR, "Out of memory!"); - exit(LSYNCD_OUTOFMEMORY); + printlogf(ERROR, "Out of memory!"); + terminate(LSYNCD_OUTOFMEMORY); } return s; @@ -559,7 +629,7 @@ void dir_conf_add_target(struct dir_conf * dir_conf, char *target) bool append_tosync_watch(int watch) { 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. for(i = 0; i < tosync_pos; i++) { if (tosync[i] == watch) { @@ -620,9 +690,9 @@ char *parse_option_text(char *text, bool recursive) break; case 0: // wtf, '%' was at the end of the string! default : // unknown char - printlogf(LOG_ERROR, + printlogf(ERROR, "don't know how to handle '\%' specifier in \"%s\"!", *text); - exit(LSYNCD_BADPARAMETERS); + terminate(LSYNCD_BADPARAMETERS); } } return str; @@ -679,7 +749,7 @@ bool action(struct dir_conf * dir_conf, } if (argc >= MAX_ARGS) { /* check for error condition */ - printlogf(LOG_ERROR, + printlogf(ERROR, "Internal error: too many (>%i) options passed", argc); return false; } @@ -688,7 +758,7 @@ bool action(struct dir_conf * dir_conf, /* debug dump of command-line options */ //for (i=0; ibinary ? dir_conf->binary : default_binary; - if (!flag_nodaemon) { + if (!flag_nodaemon && logfile) { 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)) { - 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! - printlogf(LOG_ERROR, "Failed executing [%s]", binary); - exit(LSYNCD_INTERNALFAIL); + printlogf(ERROR, "Failed executing [%s]", binary); + terminate(LSYNCD_INTERNALFAIL); } for (i=0; i [%s] finished", src, dest); + printlogf(DEBUG, "Rsync of [%s] -> [%s] finished", src, dest); return true; } @@ -761,7 +831,7 @@ int add_watch(char const * pathname, IN_MOVED_TO | IN_DONT_FOLLOW | IN_ONLYDIR); if (wd == -1) { - printlogf(LOG_ERROR, "Cannot add watch %s (%d:%s)", + printlogf(ERROR, "Cannot add watch %s (%d:%s)", pathname, errno, strerror(errno)); return -1; } @@ -856,17 +926,17 @@ bool buildpath(char *pathname, { int len = builddir(pathname, pathsize, watch, prefix); if (len < 0) { - printlogf(LOG_ERROR, "path too long!"); + printlogf(ERROR, "path too long!"); return false; } if (dirname) { if (pathsize < len + strlen(dirname) + 1) { - printlogf(LOG_ERROR, "path too long!"); + printlogf(ERROR, "path too long!"); return false; } 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; } @@ -893,11 +963,11 @@ bool rsync_dir(int watch) status = false; continue; } - printlogf(LOG_NORMAL, "rsyncing %s --> %s", pathname, destname); + printlogf(NORMAL, "rsyncing %s --> %s", pathname, destname); // call rsync to propagate changes in the directory 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; } } @@ -922,7 +992,7 @@ int add_dirwatch(char const * dirname, int parent, struct dir_conf * dir_conf) int dw, i; char pathname[PATH_MAX+1]; - printlogf(LOG_DEBUG, "add_dirwatch(%s, p->dirname:%s, ...)", + printlogf(DEBUG, "add_dirwatch(%s, p->dirname:%s, ...)", dirname, 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)) { - printlogf(LOG_ERROR, "pathname too long %s//%s", pathname, dirname); + printlogf(ERROR, "pathname too long %s//%s", pathname, dirname); return -1; } d = opendir(pathname); if (d == NULL) { - printlogf(LOG_ERROR, "cannot open dir %s.", dirname); + printlogf(ERROR, "cannot open dir %s.", dirname); 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, ".")) { 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.", de->d_name, dirname); append_tosync_watch(ndw); @@ -1011,7 +1081,7 @@ bool remove_dirwatch(const char * name, int parent) } 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); return false; } @@ -1064,11 +1134,11 @@ int get_dirwatch_offset(int wd) { */ bool process_tosync_stack() { - printlogf(LOG_DEBUG, "Processing through tosync stack."); + printlogf(DEBUG, "Processing through tosync stack."); while(tosync_pos > 0) { rsync_dir(tosync[--tosync_pos]); } - printlogf(LOG_DEBUG, "being done with tosync stack"); + printlogf(DEBUG, "being done with tosync stack"); return true; } @@ -1091,7 +1161,7 @@ bool handle_event(struct inotify_event *event) 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"); + printlogf(ERROR, "bufferoverflow in handle_event"); return false; } @@ -1102,7 +1172,7 @@ bool handle_event(struct inotify_event *event) 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) { return true; @@ -1116,7 +1186,7 @@ bool handle_event(struct inotify_event *event) watch = get_dirwatch_offset(event->wd); if (watch == -1) { - printlogf(LOG_ERROR, + printlogf(ERROR, "received an inotify event that doesnt match any watched directory :-(%d,%d)", event->mask, event->wd); return false; @@ -1133,13 +1203,13 @@ bool handle_event(struct inotify_event *event) if ((IN_CREATE | IN_CLOSE_WRITE | IN_DELETE | 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? if (subwatch >= 0) { // sync through the new created directory as well. rsync_dir(subwatch); } } else { - printlogf(LOG_DEBUG, "... ignored this event."); + printlogf(DEBUG, "... ignored this event."); } process_tosync_stack(); return true; @@ -1157,12 +1227,12 @@ bool master_loop() len = read (inotf, buf, INOTIFY_BUF_LEN); 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; } if (len == 0) { - printlogf(LOG_ERROR, "eof?"); + printlogf(ERROR, "eof?"); return false; } @@ -1187,8 +1257,8 @@ void check_file_exists(const char* filename) { struct stat st; if (-1==stat(filename, &st)) { - printlogf(LOG_ERROR, "File [%s] does not exist\n", filename); - exit (LSYNCD_FILENOTFOUND); + printlogf(ERROR, "File [%s] does not exist\n", filename); + terminate(LSYNCD_FILENOTFOUND); } } @@ -1201,8 +1271,8 @@ void check_file_exists(const char* filename) void check_absolute_path(const char* filename) { if (filename[0] != '/') { - printlogf(LOG_ERROR, "Filename [%s] is not an absolute path\n", filename); - exit (LSYNCD_FILENOTFOUND); + printlogf(ERROR, "Filename [%s] is not an absolute path\n", filename); + terminate(LSYNCD_FILENOTFOUND); } } @@ -1241,16 +1311,13 @@ void print_help(char *arg0) printf(" --dryrun Do not call any actions, run dry only\n"); printf(" --exclude-from FILE Exclude file handled to rsync (DEFAULT: None)\n"); printf(" --help Print this help text and exit.\n"); - printf(" --logfile FILE Put log here (DEFAULT: %s)\n", - logfile); + printf(" --logfile FILE Put log here (DEFAULT: uses syslog if not specified)\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(" --scarce Only log errors\n"); printf(" --stubborn Ignore rsync errors on startup.\n"); printf(" --version Print version an exit.\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"); @@ -1293,8 +1360,8 @@ struct call_option * parse_callopts(xmlNodePtr node) { xmlStrcmp(cnode->name, BAD_CAST "source") && xmlStrcmp(cnode->name, BAD_CAST "destination") ) { - fprintf(stderr, "error unknown call option type \"%s\"", cnode->name); - exit(LSYNCD_BADCONFIGFILE); + printlogf(ERROR, "error unknown call option type \"%s\"", cnode->name); + terminate(LSYNCD_BADCONFIGFILE); } opt_n++; } @@ -1311,8 +1378,8 @@ struct call_option * parse_callopts(xmlNodePtr node) { if (!xmlStrcmp(cnode->name, BAD_CAST "option")) { xc = xmlGetProp(cnode, BAD_CAST "text"); if (xc == NULL) { - fprintf(stderr, "error in config file: text attribute missing from