diff --git a/AUTHORS b/AUTHORS index 5efb754..dc76283 100644 --- a/AUTHORS +++ b/AUTHORS @@ -1,2 +1,4 @@ Axel Kittenberger Jürgen "README" Mangler +Eugene Sanivsky +Junichi Uekawa diff --git a/Makefile.am b/Makefile.am index c478a2a..1a6f9a8 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,10 @@ ## Makefile.am -- Process this file with automake to produce Makefile.in bin_PROGRAMS = lsyncd lsyncd_SOURCES = lsyncd.c +TESTS = tests/help.sh \ + tests/version.sh \ + tests/wrong-logfile.sh \ + tests/wrong-rsync.sh datarootdir = @datarootdir@ diff --git a/lsyncd.c b/lsyncd.c index 8f63ac4..30f88ba 100644 --- a/lsyncd.c +++ b/lsyncd.c @@ -7,6 +7,7 @@ * Eugene Sanivsky */ #include "config.h" +#define _GNU_SOURCE #include #include @@ -292,6 +293,49 @@ void *s_realloc(void *ptr, size_t size) return r; } +/** + * "secured" strdup. + */ +char *s_strdup(const char* src) +{ + char *s = strdup(src); + + if (s == NULL) { + printlogf(LOG_ERROR, "Out of memory!"); + exit(-1); + } + + return s; +} + +/** + * Returns the canonicalized path of a directory with a final '/', + * Makes sure it is a directory. + */ +char *realdir(const char * dir) +{ + char* cs = canonicalize_file_name(dir); + + if (cs == NULL) { + return NULL; + } + + if (strlen(cs) + 2 >= MAX_PATH) { + // at systems maxpath already, we cannot add a '/' anyway. + return NULL; + } + + struct stat st; + stat(cs, &st); + if (!S_ISDIR(st.st_mode)) { + free(cs); + return NULL; + } + + strcat(cs, '/'); + return cs; +} + /** * Calls rsync to sync from src to dest. * Returns after rsync has finished. @@ -338,9 +382,10 @@ bool rsync(char const * src, const char * dest, bool recursive) waitpid(pid, &status, 0); assert(WIFEXITED(status)); if (WEXITSTATUS(status)){ - printlogf(LOG_ERROR, "Forked rsync process returned non-zero return code"); - //TODO: dispute, to we really want to terminate in this case? - //exit(-1); + printlogf(LOG_ERROR, "Forked rsync process returned non-zero return code: %i", WEXITSTATUS(status)); + //TODO: really philosophize a little more what to do when rsync fails. + // this could also be just a temp. network error while running. + exit(-1); } printlogf(LOG_DEBUG, "Rsync of [%s] -> [%s] finished", src, dest); @@ -833,14 +878,17 @@ bool parse_options(int argc, char **argv) } if (!strcmp("logfile", long_options[oi].name)) { - logfile = s_malloc(strlen(optarg) + 1); - strcpy(logfile, optarg); + logfile = s_strdup(optarg); } if (!strcmp("exclude-from", long_options[oi].name)) { - exclude_file = s_malloc(strlen(optarg) + 1); - strcpy(exclude_file, optarg); + exclude_file = s_strdup(optarg); } + + if (!strcmp("rsync-binary", long_options[oi].name)) { + rsync_binary = s_strdup(optarg); + } + } } @@ -849,10 +897,16 @@ bool parse_options(int argc, char **argv) exit(-1); } - option_source = argv[optind]; - + /* Resolves relative source path, lsyncd might chdir to / later. */ + option_source = realdir(argv[optind]); option_target = argv[optind + 1]; - printf("syncing %s -> %s\n", option_source, option_target); + + if (!option_source) { + printf("Error: Source [%s] not found or not a directory.\n", argv[optind]); + exit(-1); + } + + printlogf(LOG_NORMAL, "syncing %s -> %s\n", option_source, option_target); return true; } @@ -963,7 +1017,7 @@ int main(int argc, char **argv) rsync(option_source, option_target, true); } - printlogf(LOG_NORMAL, "--- Entering normal operation with [%d] monitored directories ---", dir_watch_num); + printlogf(LOG_NORMAL, "--- Entering normal operation with [%d] monitored directories ---", dir_watch_num); signal(SIGTERM, catch_alarm); master_loop(); diff --git a/tests/help.sh b/tests/help.sh new file mode 100644 index 0000000..c1e8747 --- /dev/null +++ b/tests/help.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# copyright 2008 Junichi Uekawa +# licensed under GPLv2 or later, see the file ../COPYING for details. + +# test that --help outputs help message and exit code of 0 +set -e +set -o pipefail + +# assume that USAGE being in output is good enough. + +./lsyncd --help | grep '^USAGE:' diff --git a/tests/version.sh b/tests/version.sh new file mode 100644 index 0000000..4fcc8d8 --- /dev/null +++ b/tests/version.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# copyright 2008 Junichi Uekawa +# licensed under GPLv2 or later, see the file ../COPYING for details. + +# test that --version outputs some kind of version message and exit code of 0 +set -e +set -o pipefail + +./lsyncd --version | grep '^Version: ' + + diff --git a/tests/wrong-logfile.sh b/tests/wrong-logfile.sh new file mode 100644 index 0000000..563844c --- /dev/null +++ b/tests/wrong-logfile.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# copyright 2008 Junichi Uekawa +# licensed under GPLv2 or later, see the file ../COPYING for details. + +# make sure wrong logfile specification gives a reasonable error +# message + +WORKTARGET=$(mktemp -d) +if [[ $( ./lsyncd --logfile /nonexisting/path/name . "${WORKTARGET}" ) =~ "cannot open logfile" ]]; then + rmdir "${WORKTARGET}" + exit 0; +else + exit 1; +fi diff --git a/tests/wrong-rsync.sh b/tests/wrong-rsync.sh new file mode 100644 index 0000000..856bca9 --- /dev/null +++ b/tests/wrong-rsync.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# copyright 2008 Junichi Uekawa +# licensed under GPLv2 or later, see the file ../COPYING for details. + +# make sure that program exits with exit code of -1 when rsync path is +# wrong. + +WORKTARGET=$(mktemp -d) +./lsyncd --no-daemon --rsync-binary /wrong/path/to/rsync . "${WORKTARGET}" +if [[ $? = 255 ]]; then + rmdir "${WORKTARGET}" + exit 0; +else + exit 1; +fi +