This commit is contained in:
Axel Kittenberger 2010-10-27 19:34:56 +00:00
parent 844ae1b65e
commit 9976269bfb
3 changed files with 295 additions and 159 deletions

View File

@ -6,7 +6,7 @@
settings = { settings = {
-- logfile = "/tmp/lsyncd", -- logfile = "/tmp/lsyncd",
nodaemon, nodaemon,
status = "/tmp/lsyncd.stat", statuspipe = "/tmp/lsyncd.stat",
loglevel = DEBUG, loglevel = DEBUG,
} }
@ -55,23 +55,6 @@ slowbash = {
-- end, -- end,
} }
-----
-- lsyncd classic - sync with rsync
--
-- All functions return the pid of a spawned process
-- or 0 if they didn't exec something.
rsync = {
----
-- Called for every sync/target pair on startup
startup = function(source, target)
log(NORMAL, "startup recursive rsync: " .. source .. " -> " .. target)
return exec("/usr/bin/rsync", "-ltrs", source, target)
end,
default = function(source, target, path)
return exec("/usr/bin/rsync", "--delete", "-ltds", source .. "/" .. path, target .. "/" .. path)
end
}
sync("s", "d/", slowbash) sync("s", "d/", slowbash)

314
lsyncd.c
View File

@ -15,7 +15,7 @@
#ifdef HAVE_SYS_INOTIFY_H #ifdef HAVE_SYS_INOTIFY_H
# include <sys/inotify.h> # include <sys/inotify.h>
#else #else
# error Missing <sys/inotify.h> please supply kernel-headers and rerun configure # error Missing <sys/inotify.h>; supply kernel-headers and rerun configure.
#endif #endif
#include <sys/stat.h> #include <sys/stat.h>
@ -42,12 +42,14 @@
* Extended debugging, undefine these to enable respective parts. * Extended debugging, undefine these to enable respective parts.
*/ */
#define DBG_MASTERLOOP(x) #define DBG_MASTERLOOP(x)
#define DBG_STARTUP(x)
/** /**
* Debugging definitions * Debugging definitions
*/ */
#ifndef DBG_MASTERLOOP #ifndef DBG_MASTERLOOP
#define DBG_MASTERLOOP(x) { logstring(DEBUG, x); } #define DBG_MASTERLOOP(x) { logstring(DEBUG, x); }
#define DBG_STARTUP(x) { logstring(DEBUG, x); }
#endif #endif
/** /**
@ -117,18 +119,43 @@ bool logsyslog = false;
/** /**
* lsyncd log level * lsyncd log level
*/ */
static enum loglevel { enum loglevel {
DEBUG = 1, /* Log debug messages */ DEBUG = 1, /* Log debug messages */
VERBOSE = 2, /* Log more */ VERBOSE = 2, /* Log more */
NORMAL = 3, /* Log short summeries */ NORMAL = 3, /* Log short summeries */
ERROR = 4, /* Log severe errors only */ ERROR = 4, /* Log severe errors only */
CORE = 0x80 /* Indicates a core message */ CORE = 0x80 /* Indicates a core message */
} loglevel = DEBUG; // TODO };
/**
* configuration parameters
*/
static struct settings {
enum loglevel loglevel;
char * statuspipe;
} settings = {
.loglevel = DEBUG,
.statuspipe = NULL,
};
/** /**
* True when lsyncd daemonized itself. * True when lsyncd daemonized itself.
*/ */
static bool is_daemon; static bool is_daemon = false;
/**
* True after first configuration phase. This is to write configuration error
* messages to stdout/stderr after being first started. Then it uses whatever
* it has been configured to. This survives a reset by HUP signal or
* inotify OVERFLOW!
*/
static bool running = false;
/**
* loglevel before user configuration took place
* set to DEBUG for upstart debugging, Normally its NORMAL.
*/
static const enum loglevel startup_loglevel = DEBUG;
/** /**
* Set to TERM or HUP in signal handler, when lsyncd should end or reset ASAP. * Set to TERM or HUP in signal handler, when lsyncd should end or reset ASAP.
@ -238,8 +265,19 @@ logstring0(enum loglevel level,
/* strip flags from level */ /* strip flags from level */
level &= 0x0F; level &= 0x0F;
if (!running) {
/* lsyncd is in intial configuration.
* thus just print to normal stdout/stderr. */
if (level == ERROR) {
fprintf(stderr, "%s\n", message);
} else if (level >= startup_loglevel) {
printf("%s\n", message);
}
return;
}
/* skips filtered messagaes */ /* skips filtered messagaes */
if (level < loglevel) { if (level < settings.loglevel) {
return; return;
} }
@ -247,7 +285,7 @@ logstring0(enum loglevel level,
(coremsg ? "CORE ERROR: " : "ERROR: ") : (coremsg ? "CORE ERROR: " : "ERROR: ") :
(coremsg ? "core: " : ""); (coremsg ? "core: " : "");
/* writes on console */ /* writes on console if not daemon */
if (!is_daemon) { if (!is_daemon) {
char ct[255]; char ct[255];
/* gets current timestamp hour:minute:second */ /* gets current timestamp hour:minute:second */
@ -258,7 +296,7 @@ logstring0(enum loglevel level,
fprintf(flog, "%s %s%s\n", ct, prefix, message); fprintf(flog, "%s %s%s\n", ct, prefix, message);
} }
/* writes on file */ /* writes to file if configured so */
if (logfile) { if (logfile) {
FILE * flog = fopen(logfile, "a"); FILE * flog = fopen(logfile, "a");
/* gets current timestamp day-time-year */ /* gets current timestamp day-time-year */
@ -277,7 +315,7 @@ logstring0(enum loglevel level,
fclose(flog); fclose(flog);
} }
/* sends to syslog */ /* sends to syslog if configured so */
if (logsyslog) { if (logsyslog) {
int sysp; int sysp;
switch (level) { switch (level) {
@ -339,7 +377,7 @@ l_log(lua_State *L)
/* log level */ /* log level */
int level = luaL_checkinteger(L, 1); int level = luaL_checkinteger(L, 1);
/* skips filtered messages early */ /* skips filtered messages early */
if ((level & 0x0F) < loglevel) { if ((level & 0x0F) < settings.loglevel) {
return 0; return 0;
} }
message = luaL_checkstring(L, 2); message = luaL_checkstring(L, 2);
@ -379,7 +417,8 @@ l_earlier(lua_State *L)
} }
/** /**
* Returns (on Lua stack) the current kernels clock state (jiffies, times() call) * Returns (on Lua stack) the current kernels
* clock state (jiffies)
*/ */
static int static int
l_now(lua_State *L) l_now(lua_State *L)
@ -429,6 +468,7 @@ l_exec(lua_State *L)
pid = fork(); pid = fork();
if (pid == 0) { if (pid == 0) {
// TODO
//if (!log->flag_nodaemon && log->logfile) { //if (!log->flag_nodaemon && log->logfile) {
// if (!freopen(log->logfile, "a", stdout)) { // if (!freopen(log->logfile, "a", stdout)) {
// printlogf(log, ERROR, "cannot redirect stdout to [%s].", log->logfile); // printlogf(log, ERROR, "cannot redirect stdout to [%s].", log->logfile);
@ -473,7 +513,8 @@ l_real_dir(lua_State *L)
struct stat st; struct stat st;
stat(cbuf, &st); stat(cbuf, &st);
if (!S_ISDIR(st.st_mode)) { if (!S_ISDIR(st.st_mode)) {
printlogf(L, ERROR, "failure in real_dir [%s] is not a directory", rdir); printlogf(L, ERROR,
"failure in real_dir '%s' is not a directory", rdir);
free(cbuf); free(cbuf);
return 0; return 0;
} }
@ -496,21 +537,25 @@ l_stackdump(lua_State* L)
{ {
int i; int i;
int top = lua_gettop(L); int top = lua_gettop(L);
printlogf(L, DEBUG, "total in stack %d\n",top); printlogf(L, DEBUG, "total in stack %d",top);
for (i = 1; i <= top; i++) { for (i = 1; i <= top; i++) {
int t = lua_type(L, i); int t = lua_type(L, i);
switch (t) { switch (t) {
case LUA_TSTRING: case LUA_TSTRING:
printlogf(L, DEBUG, "%d string: '%s'\n", i, lua_tostring(L, i)); printlogf(L, DEBUG, "%d string: '%s'",
i, lua_tostring(L, i));
break; break;
case LUA_TBOOLEAN: case LUA_TBOOLEAN:
printlogf(L, DEBUG, "%d boolean %s\n", i, lua_toboolean(L, i) ? "true" : "false"); printlogf(L, DEBUG, "%d boolean %s",
i, lua_toboolean(L, i) ? "true" : "false");
break; break;
case LUA_TNUMBER: case LUA_TNUMBER:
printlogf(L, DEBUG, "%d number: %g\n", i, lua_tonumber(L, i)); printlogf(L, DEBUG, "%d number: %g",
i, lua_tonumber(L, i));
break; break;
default: default:
printlogf(L, DEBUG, "%d %s\n", i, lua_typename(L, t)); printlogf(L, DEBUG, "%d %s",
i, lua_typename(L, t));
break; break;
} }
} }
@ -558,8 +603,9 @@ l_sub_dirs (lua_State *L)
/* readdir can trusted */ /* readdir can trusted */
isdir = de->d_type == DT_DIR; isdir = de->d_type == DT_DIR;
} }
if (!isdir || !strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) { if (!isdir || !strcmp(de->d_name, ".") ||
/* ignore non directories and . and .. */ !strcmp(de->d_name, ".."))
{ /* ignore non directories and . and .. */
continue; continue;
} }
@ -675,7 +721,8 @@ l_wait_pids(lua_State *L)
if (newp == 0) { if (newp == 0) {
remaining--; remaining--;
} }
/* does not break, in case there are duplicate pids (whyever) */ /* does not break!
* in case there are duplicate pids (why-ever) */
} }
} }
} }
@ -683,11 +730,45 @@ l_wait_pids(lua_State *L)
return 0; return 0;
} }
/**
* Configures core parameters.
*
* @param (Lua stack) a string for a core configuratoin
* @param (Lua stack) --differes depending on string.
*/
static int
l_configure(lua_State *L)
{
const char * command = luaL_checkstring(L, 1);
if (!strcmp(command, "statuspipe")) {
/* configures the status pipe lsyncd will dump
* its status to if opened*/
if (settings.statuspipe) {
free(settings.statuspipe);
}
settings.statuspipe = s_strdup(luaL_checkstring(L, 2));
} else if (!strcmp(command, "loglevel")) {
settings.loglevel = luaL_checkinteger(L, 2);
} else if (!strcmp(command, "running")) {
/* set by runner after first initialize
* from this on log to configurated log end instead of
* stdout/stderr */
running = true;
} else {
printlogf(L, ERROR,
"Internal error, unknown parameter in l_configure(%s)",
command);
exit(-1); //ERRNO
}
return 0;
}
static const luaL_reg lsyncdlib[] = { static const luaL_reg lsyncdlib[] = {
{"add_watch", l_add_watch }, {"add_watch", l_add_watch },
{"addto_clock", l_addto_clock }, {"addto_clock", l_addto_clock },
{"before_eq", l_before_eq }, {"before_eq", l_before_eq },
{"configure", l_configure },
{"earlier", l_earlier }, {"earlier", l_earlier },
{"exec", l_exec }, {"exec", l_exec },
{"log", l_log }, {"log", l_log },
@ -715,7 +796,7 @@ printlogf(lua_State *L,
{ {
va_list ap; va_list ap;
/* skips filtered messages early */ /* skips filtered messages early */
if (level < loglevel) { if (level < settings.loglevel) {
return; return;
} }
lua_pushcfunction(L, l_log); lua_pushcfunction(L, l_log);
@ -727,38 +808,6 @@ printlogf(lua_State *L,
return; return;
} }
/**
* Transfers the core relevant settings from lua's global "settings" into core.
* This saves time in normal operation instead of bothering lua all the time.
*/
/*
void
get_settings(lua_State *L)
{
if (settings.logfile) {
free(settings.logfile);
settings.logfile = NULL;
}
lua_getglobal(L, "settings");
if (!lua_istable(L, -1)) {
return;
}
lua_pushstring(L, "logfile");
lua_gettable(L, -2);
if (settings.logfile) {
free(settings.logfile);
settings.logfile = NULL;
}
if (lua_isstring(L, -1)) {
settings.logfile = s_strdup(luaL_checkstring(L, -1));
}
lua_pop(L, 1);
lua_pop(L, 1);
}*/
/** /**
* Buffer for MOVE_FROM events. * Buffer for MOVE_FROM events.
* Lsyncd buffers MOVE_FROM events to check if * Lsyncd buffers MOVE_FROM events to check if
@ -814,8 +863,9 @@ void handle_event(lua_State *L, struct inotify_event *event) {
event_type = MOVE; event_type = MOVE;
move_event = false; move_event = false;
} else if (IN_MOVED_FROM & event->mask) { } else if (IN_MOVED_FROM & event->mask) {
/* just the MOVE_FROM, buffers this event, and wait if next event is a matching /* just the MOVE_FROM, buffers this event, and wait if next event is
* MOVED_TO of this was an unary move out of the watched tree. */ * a matching MOVED_TO of this was an unary move out of the watched
* tree. */
size_t el = sizeof(struct inotify_event) + event->len; size_t el = sizeof(struct inotify_event) + event->len;
if (move_event_buf_size < el) { if (move_event_buf_size < el) {
move_event_buf_size = el; move_event_buf_size = el;
@ -891,8 +941,8 @@ masterloop(lua_State *L)
lua_pop(L, 2); lua_pop(L, 2);
if (have_alarm && time_before(alarm_time, now)) { if (have_alarm && time_before(alarm_time, now)) {
/* there is a delay that wants to be handled already /* there is a delay that wants to be handled already thus do not
* thus do not read from inotify_fd and jump directly to its handling */ * read from inotify_fd and jump directly to its handling */
DBG_MASTERLOOP("immediately handling delays."); DBG_MASTERLOOP("immediately handling delays.");
do_read = 0; do_read = 0;
} else { } else {
@ -908,7 +958,8 @@ masterloop(lua_State *L)
if (have_alarm) { if (have_alarm) {
DBG_MASTERLOOP("going into timed select."); DBG_MASTERLOOP("going into timed select.");
tv.tv_sec = (alarm_time - now) / clocks_per_sec; tv.tv_sec = (alarm_time - now) / clocks_per_sec;
tv.tv_nsec = (alarm_time - now) * 1000000000 / clocks_per_sec % 1000000000; tv.tv_nsec = (alarm_time - now) *
1000000000 / clocks_per_sec % 1000000000;
} else { } else {
DBG_MASTERLOOP("going into blocking select."); DBG_MASTERLOOP("going into blocking select.");
} }
@ -916,14 +967,12 @@ masterloop(lua_State *L)
* on zero the timemout occured. */ * on zero the timemout occured. */
FD_ZERO(&readfds); FD_ZERO(&readfds);
FD_SET(inotify_fd, &readfds); FD_SET(inotify_fd, &readfds);
do_read = pselect(inotify_fd + 1, &readfds, NULL, NULL, have_alarm ? &tv : NULL, do_read = pselect(inotify_fd + 1, &readfds, NULL, NULL,
&sigset); have_alarm ? &tv : NULL, &sigset);
if (do_read > 0) { DBG_MASTERLOOP(do_read > 0 ?
DBG_MASTERLOOP("theres data on inotify."); "theres data on inotify." :
} else { "core: select() timeout or signal.");
DBG_MASTERLOOP("core: select() timeout or signal.");
}
} }
/* reads possible events from inotify stream */ /* reads possible events from inotify stream */
@ -943,7 +992,8 @@ masterloop(lua_State *L)
} }
} while(0); } while(0);
while (i < len && !reset) { while (i < len && !reset) {
struct inotify_event *event = (struct inotify_event *) &readbuf[i]; struct inotify_event *event =
(struct inotify_event *) &readbuf[i];
handle_event(L, event); handle_event(L, event);
i += sizeof(struct inotify_event) + event->len; i += sizeof(struct inotify_event) + event->len;
} }
@ -953,7 +1003,8 @@ masterloop(lua_State *L)
fd_set readfds; fd_set readfds;
FD_ZERO(&readfds); FD_ZERO(&readfds);
FD_SET(inotify_fd, &readfds); FD_SET(inotify_fd, &readfds);
do_read = pselect(inotify_fd + 1, &readfds, NULL, NULL, &tv, NULL); do_read = pselect(inotify_fd + 1, &readfds,
NULL, NULL, &tv, NULL);
if (do_read > 0) { if (do_read > 0) {
DBG_MASTERLOOP("there is more data on inotify."); DBG_MASTERLOOP("there is more data on inotify.");
} }
@ -987,7 +1038,7 @@ masterloop(lua_State *L)
/** /**
* Prints a minimal help if e.g. config_file is missing * Prints a minimal help if e.g. config_file is missing
*/ */
void mini_help(char *arg0) void minihelp(char *arg0)
{ {
fprintf(stderr, "Missing config file\n"); fprintf(stderr, "Missing config file\n");
fprintf(stderr, "Minimal Usage: %s CONFIG_FILE\n", arg0); fprintf(stderr, "Minimal Usage: %s CONFIG_FILE\n", arg0);
@ -1001,24 +1052,23 @@ void mini_help(char *arg0)
int int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
/* the Lua interpreter */
lua_State* L;
/* position at cores (minimal) argument parsing * /* position at cores (minimal) argument parsing *
* most arguments are parsed in the lua runner */ * most arguments are parsed in the lua runner */
int argp = 1; int argp = 1;
if (argc <= 1) { if (argc <= 1) {
mini_help(argv[0]); minihelp(argv[0]);
return -1; return -1;
} }
/* kernel parameters */ /* kernel parameters */
clocks_per_sec = sysconf(_SC_CLK_TCK); clocks_per_sec = sysconf(_SC_CLK_TCK);
/* the Lua interpreter */
lua_State* L;
/* TODO check lua version */
/* load Lua */ /* load Lua */
L = lua_open(); L = lua_open();
/* TODO check lua version */
luaL_openlibs(L); luaL_openlibs(L);
luaL_register(L, "lsyncd", lsyncdlib); luaL_register(L, "lsyncd", lsyncdlib);
lua_setglobal(L, "lysncd"); lua_setglobal(L, "lysncd");
@ -1038,52 +1088,73 @@ main(int argc, char *argv[])
lua_pushinteger(L, NORMAL); lua_setglobal(L, "NORMAL"); lua_pushinteger(L, NORMAL); lua_setglobal(L, "NORMAL");
lua_pushinteger(L, ERROR); lua_setglobal(L, "ERROR"); lua_pushinteger(L, ERROR); lua_setglobal(L, "ERROR");
#ifdef LSYNCD_DEFAULT_RUNNER_FILE
/* checks if the user overrode default runner file */ /* checks if the user overrode default runner file */
if (!strcmp(argv[argp], "--runner")) { if (!strcmp(argv[argp], "--runner")) {
if (argc < 3) { if (argc < 3) {
fprintf(stderr, "Lsyncd Lua-runner file missing after --runner.\n"); logstring(ERROR, "Lsyncd Lua-runner file missing after --runner.");
#ifdef LSYNCD_DEFAULT_RUNNER_FILE
printlogf(L, ERROR,
"Using '%s' as default location for runner.",
LSYNCD_DEFAULT_RUNNER_FILE);
#else
logstring(ERROR,
"Using a staticly included runner as default.");
#endif
return -1; //ERRNO return -1; //ERRNO
} }
lsyncd_runner_file = argv[argp + 1]; lsyncd_runner_file = argv[argp + 1];
argp += 2; argp += 2;
} else { } else {
#ifdef LSYNCD_DEFAULT_RUNNER_FILE
lsyncd_runner_file = LSYNCD_DEFAULT_RUNNER_FILE; lsyncd_runner_file = LSYNCD_DEFAULT_RUNNER_FILE;
#endif
} }
{ if (lsyncd_runner_file) {
/* checks if the runne file exists */ /* checks if the runner file exists */
struct stat st; struct stat st;
if (stat(lsyncd_runner_file, &st)) { if (stat(lsyncd_runner_file, &st)) {
fprintf(stderr, "Cannot find Lsyncd Lua-runner at '%s'.\n", lsyncd_runner_file); printlogf(L, ERROR,
fprintf(stderr, "Maybe specify another place? %s --runner RUNNER_FILE CONFIG_FILE\n", argv[0]); "Cannot find Lsyncd Lua-runner at '%s'.", lsyncd_runner_file);
printlogf(L, ERROR,
"Maybe specify another place?");
printlogf(L, ERROR,
"%s --runner RUNNER_FILE CONFIG_FILE", argv[0]);
return -1; // ERRNO
}
/* loads the runner file */
if (luaL_loadfile(L, lsyncd_runner_file)) {
printlogf(L, ERROR,
"error loading '%s': %s",
lsyncd_runner_file, lua_tostring(L, -1));
return -1; // ERRNO return -1; // ERRNO
} }
} }
/* loads the runner file */ #ifndef LSYNCD_DEFAULT_RUNNER_FILE
if (luaL_loadfile(L, lsyncd_runner_file)) { else {
fprintf(stderr, "error loading '%s': %s\n", /* loads the runner from binary */
lsyncd_runner_file, lua_tostring(L, -1)); if (luaL_loadbuffer(L, &_binary_luac_out_start,
return -1; // ERRNO &_binary_luac_out_end - &_binary_luac_out_start, "lsyncd.lua"))
{
printlogf(L, ERROR,
"error loading precompiled lsyncd.lua runner: %s",
lua_tostring(L, -1));
return -1; // ERRNO
}
} }
#else #else
/* User cannot override runner file in a static compile */ else {
if (!strcmp(argv[argp], "--runner")) { /* this should never be possible, security code nevertheless */
fprintf(stderr, "This lsyncd binary has its lua runner staticly compiled.\n"); logstring(ERROR,
fprintf(stderr, "Configure and compile with --with-runner=FILE to use the lsyncd.lua file.\n"); "Internal fail: lsyncd_runner is NULL with non-static runner");
return -1; // ERRNO
}
/* loads the runner from binary */
if (luaL_loadbuffer(L, &_binary_luac_out_start,
&_binary_luac_out_end - &_binary_luac_out_start, "lsyncd.lua")) {
fprintf(stderr, "error loading precompiled lsyncd.lua runner: %s\n",
lua_tostring(L, -1));
return -1; // ERRNO return -1; // ERRNO
} }
#endif #endif
/* execute the runner defining all its functions */ /* execute the runner defining all its functions */
if (lua_pcall(L, 0, LUA_MULTRET, 0)) { if (lua_pcall(L, 0, LUA_MULTRET, 0)) {
fprintf(stderr, "error preparing '%s': %s\n", printlogf(L, ERROR,
lsyncd_runner_file, lua_tostring(L, -1)); "error preparing '%s': %s",
lsyncd_runner_file ? lsyncd_runner_file : "internal runner",
lua_tostring(L, -1));
return -1; // ERRNO return -1; // ERRNO
} }
@ -1094,14 +1165,16 @@ main(int argc, char *argv[])
lversion = luaL_checkstring(L, -1); lversion = luaL_checkstring(L, -1);
lua_pop(L, 1); lua_pop(L, 1);
if (strcmp(lversion, PACKAGE_VERSION)) { if (strcmp(lversion, PACKAGE_VERSION)) {
fprintf(stderr, "Version mismatch '%s' is '%s', but core is '%s'\n", printlogf(L, ERROR,
lsyncd_runner_file, lversion, PACKAGE_VERSION); "Version mismatch '%s' is '%s', but core is '%s'",
lsyncd_runner_file ? lsyncd_runner_file : "internal runner",
lversion, PACKAGE_VERSION);
return -1; // ERRNO return -1; // ERRNO
} }
} }
{ {
/* checks if there is a "-help" or "--help" in the args before anything else */ /* checks if there is a "-help" or "--help" before anything else */
int i; int i;
for(i = argp; i < argc; i++) { for(i = argp; i < argc; i++) {
if (!strcmp(argv[i],"-help") || !strcmp(argv[i],"--help")) { if (!strcmp(argv[i],"-help") || !strcmp(argv[i],"--help")) {
@ -1111,39 +1184,50 @@ main(int argc, char *argv[])
} }
} }
} }
if (argp + 1 < argc) {
mini_help(argv[0]);
return -1; // ERRNO
}
lsyncd_config_file = argv[argp++];
{ {
/* checks for the existence of the config file */ /* checks for the configuration and existence of the config file */
struct stat st; struct stat st;
if (argp + 1 > argc) {
minihelp(argv[0]);
return -1; // ERRNO
}
lsyncd_config_file = argv[argp++];
if (stat(lsyncd_config_file, &st)) { if (stat(lsyncd_config_file, &st)) {
fprintf(stderr, "Cannot find config file at '%s'.\n", lsyncd_config_file); printlogf(L, ERROR,
"Cannot find config file at '%s'.",
lsyncd_config_file);
return -1; // ERRNO return -1; // ERRNO
} }
} }
/* loads and executes the config file */
if (luaL_loadfile(L, lsyncd_config_file)) { if (luaL_loadfile(L, lsyncd_config_file)) {
fprintf(stderr, "error loading %s: %s\n", lsyncd_config_file, lua_tostring(L, -1)); printlogf(L, ERROR,
"error loading %s: %s",
lsyncd_config_file, lua_tostring(L, -1));
return -1; // ERRNO return -1; // ERRNO
} }
if (lua_pcall(L, 0, LUA_MULTRET, 0)) { if (lua_pcall(L, 0, LUA_MULTRET, 0)) {
fprintf(stderr, "error preparing %s: %s\n", lsyncd_config_file, lua_tostring(L, -1)); printlogf(L, ERROR,
"error preparing %s: %s",
lsyncd_config_file, lua_tostring(L, -1));
return -1; // ERRNO return -1; // ERRNO
} }
/* open inotify */ /* opens inotify */
inotify_fd = inotify_init(); inotify_fd = inotify_init();
if (inotify_fd == -1) { if (inotify_fd == -1) {
fprintf(stderr, "Cannot create inotify instance! (%d:%s)\n", errno, strerror(errno)); printlogf(L, ERROR,
"Cannot create inotify instance! (%d:%s)",
errno, strerror(errno));
return -1; // ERRNO return -1; // ERRNO
} }
/* add signal handlers */
{ {
/* adds signal handlers *
* listens to SIGCHLD, but blocks it until pselect()
* opens up*/
sigset_t set; sigset_t set;
sigemptyset(&set); sigemptyset(&set);
sigaddset(&set, SIGCHLD); sigaddset(&set, SIGCHLD);
@ -1151,11 +1235,11 @@ main(int argc, char *argv[])
sigprocmask(SIG_BLOCK, &set, NULL); sigprocmask(SIG_BLOCK, &set, NULL);
} }
/* initialize */
/* lua code will set configuration and add watches */
{ {
/* runs initialitions from runner *
* lua code will set configuration and add watches */
int idx = 1; int idx = 1;
/* creates a table with all option arguments */ /* creates a table with all remaining argv option arguments */
lua_getglobal(L, "lsyncd_initialize"); lua_getglobal(L, "lsyncd_initialize");
lua_newtable(L); lua_newtable(L);
while(argp < argc) { while(argp < argc) {

View File

@ -16,7 +16,8 @@
-- --
if lsyncd_version then if lsyncd_version then
-- checks if the runner is being loaded twice -- checks if the runner is being loaded twice
io.stderr:write("You cannot use the lsyncd runner as configuration file!\n") io.stderr:write(
"You cannot use the lsyncd runner as configuration file!\n")
os.exit(-1) os.exit(-1)
end end
lsyncd_version = "2.0beta1" lsyncd_version = "2.0beta1"
@ -26,6 +27,7 @@ lsyncd_version = "2.0beta1"
-- --
log = lsyncd.log log = lsyncd.log
exec = lsyncd.exec exec = lsyncd.exec
terminate = lsyncd.terminate
--============================================================================ --============================================================================
-- Coding checks, ensure termination on some easy to do coding errors. -- Coding checks, ensure termination on some easy to do coding errors.
@ -87,13 +89,13 @@ local meta_check_count_array = {
local meta_check_prototype = { local meta_check_prototype = {
__index = function(t, k) __index = function(t, k)
if not t.prototype[k] then if not t.prototype[k] then
error("This table does not have key '"..k.."' in its prototype.", 2) error("tables prototype doesn't have key '"..k.."'.", 2)
end end
return rawget(t, k) return rawget(t, k)
end, end,
__newindex = function(t, k, v) __newindex = function(t, k, v)
if not t.prototype[k] then if not t.prototype[k] then
error("This table does not have key '"..k.."' in its prototype.", 2) error("tables prototype doesn't have key '"..k.."'.", 2)
end end
rawset(t, k, v) rawset(t, k, v)
end end
@ -105,7 +107,7 @@ local meta_check_prototype = {
local function set_array(t) local function set_array(t)
for k, _ in pairs(t) do for k, _ in pairs(t) do
if type(k) ~= "number" then if type(k) ~= "number" then
error("This table cannot be set as array, it has non-numberic key '"..k.."'", 2) error("table can't become an array, since it has key '"..k.."'", 2)
end end
end end
setmetatable(t, meta_check_array) setmetatable(t, meta_check_array)
@ -138,7 +140,7 @@ local function set_prototype(t, prototype)
t.prototype = prototype t.prototype = prototype
for k, _ in pairs(t) do for k, _ in pairs(t) do
if not t.prototype[k] and k ~= "prototype" then if not t.prototype[k] and k ~= "prototype" then
error("Cannot set prototype of table, conflicting key: '"..k.."'.", 2) error("Cannot set prototype, conflicting key: '"..k.."'.", 2)
end end
end end
setmetatable(t, proto_check_table) setmetatable(t, proto_check_table)
@ -205,7 +207,8 @@ end
-- .filename .. filename or nil (=dir itself) -- .filename .. filename or nil (=dir itself)
-- (.movepeer) .. for MOVEFROM/MOVETO link to other delay -- (.movepeer) .. for MOVEFROM/MOVETO link to other delay
-- } -- }
-- .delaywd [wd] = [#] .. a list of lists of all delays from a watch descriptor. -- .delaywd [wd] = [#] .. a list of lists of all delays from a
-- watch descriptor.
-- } -- }
-- --
local origins = new_array() local origins = new_array()
@ -387,7 +390,9 @@ function lsyncd_collect_process(pid, exitcode)
end end
local sync = process.sync local sync = process.sync
local o = sync.origin local o = sync.origin
print("collected ", pid, ": ", event_names[process.atype], o.source, "/", sync.path , process.filename, " = ", exitcode) -- TODO
print("collected ", pid, ": ", event_names[process.atype], o.source,
"/", sync.path , process.filename, " = ", exitcode)
processes[pid] = nil processes[pid] = nil
o.processes[pid] = nil o.processes[pid] = nil
end end
@ -398,10 +403,10 @@ end
-- --
local function invoke_action(delay) local function invoke_action(delay)
local sync = delay.sync local sync = delay.sync
local origin = sync.origin local o = sync.origin
local actions = origin.actions local actions = o.actions
local func = nil local func = nil
local atype = delay.atype local atype = delay.atype
if atype == NONE then if atype == NONE then
-- a removed action -- a removed action
return return
@ -418,7 +423,7 @@ local function invoke_action(delay)
end end
if func then if func then
local pid = func(origin.source, sync.path, delay.filename, origin.targetident) local pid = func(o.source, sync.path, delay.filename, o.targetident)
if pid and pid > 0 then if pid and pid > 0 then
local process = {pid = pid, local process = {pid = pid,
atype = delay.atype, atype = delay.atype,
@ -428,7 +433,7 @@ local function invoke_action(delay)
} }
set_prototype(process, proto_process) set_prototype(process, proto_process)
processes[pid] = process processes[pid] = process
origin.processes[pid] = process o.processes[pid] = process
end end
end end
end end
@ -462,7 +467,7 @@ end
-- the arguments. -- the arguments.
-- --
function lsyncd_help() function lsyncd_help()
io.stderr:write( io.stdout:write(
[[TODO this is a multiline [[TODO this is a multiline
help help
]]) ]])
@ -474,13 +479,18 @@ end
-- Called from core on init or restart after user configuration. -- Called from core on init or restart after user configuration.
-- --
function lsyncd_initialize(args) function lsyncd_initialize(args)
-- creates settings if user didnt
settings = settings or {}
-- From this point on, no globals may be created anymore -- From this point on, no globals may be created anymore
GLOBAL_lock(_G) GLOBAL_lock(_G)
-- parses all arguments
for i = 1, #args do for i = 1, #args do
local a = args[i] local a = args[i]
if a:sub(1, 1) ~= "-" then if a:sub(1, 1) ~= "-" then
io.stderr:write("Unknown option "..a..". Options must start with '-' or '--'.\n") log(ERROR, "Unknown option "..a..
". Options must start with '-' or '--'.")
os.exit(-1) -- ERRNO os.exit(-1) -- ERRNO
end end
if a:sub(1, 2) == "--" then if a:sub(1, 2) == "--" then
@ -488,15 +498,49 @@ function lsyncd_initialize(args)
else else
a = a:sub(2) a = a:sub(2)
end end
print(i, a) --TOTO
end
-- all valid settings, first value is 1 if it needs a parameter
local configure_settings = {
loglevel = {1,
function(param)
if not (param == DEBUG or param == NORMAL or
param == VERBOSE or param == ERROR) then
log(ERROR, "unknown settings.loglevel '"..param.."'")
terminate(-1); -- ERRNO
end
end},
statuspipe = {1, nil},
}
-- check all entries in the settings table
for c, p in pairs(settings) do
local cs = configure_settings[c]
if not cs then
log(ERROR, "unknown setting '"..c.."'")
terminate(-1) -- ERRNO
end
if cs[1] == 1 and not p then
log(ERROR, "setting '"..c.."' needs a parameter")
end
-- calls the check function if its not nil
if cs[2] then
cs[2](p)
end
lsyncd.configure(c, p)
end end
-- makes sure the user gave lsyncd anything to do -- makes sure the user gave lsyncd anything to do
if #origins == 0 then if #origins == 0 then
log(ERROR, "nothing to watch. Use directory(SOURCEDIR, TARGET) in your config file."); log(ERROR, "Nothing to watch!")
lsyncd.terminate(-1) -- ERRNO log(ERROR, "Use sync(SOURCE, TARGET, BEHAVIOR) in your config file.");
terminate(-1) -- ERRNO
end end
-- from this point on use logging facilities as configured.
lsyncd.configure("running");
-- set to true if at least one origin has a startup function -- set to true if at least one origin has a startup function
local have_startup = false local have_startup = false
-- runs through the origins table filled by user calling directory() -- runs through the origins table filled by user calling directory()
@ -506,7 +550,7 @@ function lsyncd_initialize(args)
local actions = o.actions local actions = o.actions
if not asrc then if not asrc then
print("Cannot resolve source path: ", o.source) print("Cannot resolve source path: ", o.source)
lsyncd.terminate(-1) -- ERRNO terminate(-1) -- ERRNO
end end
o.source = asrc o.source = asrc
o.delays = new_count_array() o.delays = new_count_array()
@ -526,7 +570,7 @@ function lsyncd_initialize(args)
if actions.startup then if actions.startup then
have_startup = true have_startup = true
end end
-- TODO move above to be before "running"
-- and add the dir watch inclusively all subdirs -- and add the dir watch inclusively all subdirs
attend_dir(o, "", nil) attend_dir(o, "", nil)
end end
@ -542,9 +586,11 @@ function lsyncd_initialize(args)
end end
end end
lsyncd.wait_pids(pids, "startup_collector") lsyncd.wait_pids(pids, "startup_collector")
log(NORMAL, "--- Entering normal operation with "..watches.size.." monitored directories ---") log(NORMAL, "--- Entering normal operation with "..watches.size..
" monitored directories ---")
else else
log(NORMAL, "--- Warmstart into normal operation with "..watches.size.." monitored directories ---") log(NORMAL, "--- Warmstart into normal operation with "..watches.size..
" monitored directories ---")
end end
end end
@ -626,7 +672,7 @@ end
function startup_collector(pid, exitcode) function startup_collector(pid, exitcode)
if exitcode ~= 0 then if exitcode ~= 0 then
log(ERROR, "Startup process", pid, " failed") log(ERROR, "Startup process", pid, " failed")
lsyncd.terminate(-1) -- ERRNO terminate(-1) -- ERRNO
end end
return 0 return 0
end end
@ -661,8 +707,7 @@ end
-- --
function default_overflow() function default_overflow()
log(ERROR, "--- OVERFLOW on inotify event queue ---") log(ERROR, "--- OVERFLOW on inotify event queue ---")
lsyncd.terminate(-1) -- TODO reset instead. terminate(-1) -- TODO reset instead.
end end
overflow = default_overflow overflow = default_overflow
@ -677,11 +722,33 @@ end
-- lsyncd default settings -- lsyncd default settings
--============================================================================ --============================================================================
-----
-- lsyncd classic - sync with rsync
--
local default_rsync = {
----
-- Called for every sync/target pair on startup
startup = function(source, target)
log(NORMAL, "startup recursive rsync: "..source.." -> "..target)
return exec("/usr/bin/rsync", "-ltrs",
source, target)
end,
default = function(source, target, path)
return exec("/usr/bin/rsync", "--delete", "-ltds",
source.."/".. path, target .. "/" .. path)
end
}
-----
-- The defaults table for the user to access
--
defaults = { defaults = {
----- -----
-- TODO -- TODO
-- --
max_processes = 1, max_processes = 1,
------ ------
-- TODO -- TODO
-- --
@ -690,7 +757,9 @@ defaults = {
[MODIFY] = { [ATTRIB] = MODIFY, [MODIFY] = MODIFY, [CREATE] = CREATE, [DELETE] = DELETE }, [MODIFY] = { [ATTRIB] = MODIFY, [MODIFY] = MODIFY, [CREATE] = CREATE, [DELETE] = DELETE },
[CREATE] = { [ATTRIB] = CREATE, [MODIFY] = CREATE, [CREATE] = CREATE, [DELETE] = -1 }, [CREATE] = { [ATTRIB] = CREATE, [MODIFY] = CREATE, [CREATE] = CREATE, [DELETE] = -1 },
[DELETE] = { [ATTRIB] = DELETE, [MODIFY] = DELETE, [CREATE] = MODIFY, [DELETE] = DELETE }, [DELETE] = { [ATTRIB] = DELETE, [MODIFY] = DELETE, [CREATE] = MODIFY, [DELETE] = DELETE },
} },
rsync = default_rsync
} }