From ab41f0159fa01339d18e71d844e31596a74e5718 Mon Sep 17 00:00:00 2001 From: Dennis Schridde Date: Tue, 2 Oct 2012 15:30:05 +0200 Subject: [PATCH 01/28] Use modern AM_INIT_AUTOMAKE and store aclocal stuff in separate m4/ dir See http://www.gnu.org/software/automake/manual/automake.html#Public-Macros --- Makefile.am | 3 ++- configure.ac | 5 ++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Makefile.am b/Makefile.am index ff279cb..95c9e6b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,4 +1,5 @@ -AUTOMAKE_OPTIONS = foreign +ACLOCAL_AMFLAGS = -I m4 + CFLAGS += -Wall $(LUA_CFLAGS) bin_PROGRAMS = lsyncd lsyncd_SOURCES = lsyncd.h lsyncd.c lsyncd.lua default-rsync.lua diff --git a/configure.ac b/configure.ac index 4e4f31d..6da00f0 100644 --- a/configure.ac +++ b/configure.ac @@ -1,10 +1,13 @@ # -*- Autoconf -*- # Process this file with autoconf to produce a configure script. + #AC_PREREQ(2.60) AC_INIT(lsyncd, 2.0.7, axkibe@gmail.com) +AM_INIT_AUTOMAKE([foreign]) + +AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([lsyncd.c]) AC_CONFIG_HEADER([config.h]) -AM_INIT_AUTOMAKE(AC_PACKAGE_NAME, AC_PACKAGE_VERSION) # Checks for programs. AC_PROG_CC From 30280bcc507f32a117c2e46d8cabebfb220c3a23 Mon Sep 17 00:00:00 2001 From: Dennis Schridde Date: Tue, 2 Oct 2012 15:39:22 +0200 Subject: [PATCH 02/28] Minor beautification of configure.ac --- configure.ac | 1 + 1 file changed, 1 insertion(+) diff --git a/configure.ac b/configure.ac index 6da00f0..d7deb1c 100644 --- a/configure.ac +++ b/configure.ac @@ -9,6 +9,7 @@ AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_SRCDIR([lsyncd.c]) AC_CONFIG_HEADER([config.h]) +### # Checks for programs. AC_PROG_CC AC_PROG_INSTALL From 6034750db34c52f08f39412c9ba60f193e8fb7c4 Mon Sep 17 00:00:00 2001 From: Dennis Schridde Date: Tue, 2 Oct 2012 15:42:05 +0200 Subject: [PATCH 03/28] Try harder to find matching Lua program/library pairs Uses an AX_SUBST_L macro I copied from another project of mine. Originally found somewhere on the net. --- configure.ac | 66 ++++++++++++++++++++++++++++++++++++------------ m4/ax_subst_l.m4 | 15 +++++++++++ 2 files changed, 65 insertions(+), 16 deletions(-) create mode 100644 m4/ax_subst_l.m4 diff --git a/configure.ac b/configure.ac index d7deb1c..05ce717 100644 --- a/configure.ac +++ b/configure.ac @@ -21,24 +21,55 @@ if test x${A2X} = xno ; then fi ### -# Checks for lua -PKG_CHECK_MODULES([LUA], [lua5.1 >= 5.1.3],,[ -PKG_CHECK_MODULES([LUA], [lua51 >= 5.1.3],,[ -PKG_CHECK_MODULES([LUA], [lua-5.1 >= 5.1.3],,[ -PKG_CHECK_MODULES([LUA], [lua >= 5.1.3]) -]) -]) -]) +# Checks for Lua -AC_PATH_PROGS([LUA], [lua5.1 lua51 lua], [no]) -if test x${LUA} = xno ; then - AC_MSG_ERROR([Program 'lua' is required]) +# Try versioned Lua 5.2 first +PKG_CHECK_MODULES([LUA52], [lua5.2],,[ + PKG_CHECK_MODULES([LUA52], [lua52],,[ + PKG_CHECK_MODULES([LUA52], [lua-5.2],,[:]) + ]) +]) +AC_PATH_PROGS([LUA52], [lua5.2 lua52], [no]) +AC_PATH_PROGS([LUAC52], [luac5.2 luac52], [no]) + +if test -z "${LUA52_PKG_ERRORS}" -a "${LUA52}" != no -a "${LUAC52}" != no ; then + LUA_VERSION="5.2" + LUA_CFLAGS="${LUA52_CFLAGS}" + LUA_LIBS="${LUA52_LIBS}" + LUA="${LUA52}" + LUAC="${LUAC52}" +else + # Fall back to versioned Lua 5.1 + PKG_CHECK_MODULES([LUA51], [lua5.1 >= 5.1.3],,[ + PKG_CHECK_MODULES([LUA51], [lua51 >= 5.1.3],,[ + PKG_CHECK_MODULES([LUA51], [lua-5.1 >= 5.1.3],,[:]) + ]) + ]) + AC_PATH_PROGS([LUA51], [lua5.1 lua51], [no]) + AC_PATH_PROGS([LUAC51], [luac5.1 luac51], [no]) + + if test -z "${LUA51_PKG_ERRORS}" -a "${LUA51}" != no -a "${LUAC51}" != no ; then + LUA_VERSION="5.1" + LUA_CFLAGS="${LUA51_CFLAGS}" + LUA_LIBS="${LUA51_LIBS}" + LUA="${LUA51}" + LUAC="${LUAC51}" + else + # Try any Lua now + PKG_CHECK_MODULES([LUA], [lua >= 5.1.3],,[:]) + AC_PATH_PROG([LUA], [lua], [no]) + AC_PATH_PROG([LUAC], [luac], [no]) + + if test -z "${LUA_PKG_ERRORS}" -a "${LUA}" != no -a "${LUAC}" != no ; then + LUA_VERSION="(unknown version)" + else + AC_MSG_ERROR([Need a Lua toolchain with matching versions ('lua' library and 'lua' and 'luac' programs)]) + fi + fi fi -AC_PATH_PROGS([LUAC], [luac5.1 luac51 luac], [no]) -if test x${LUAC} = xno ; then - AC_MSG_ERROR([Program 'luac' is required]) -fi +AX_SUBST_L([LUA_CFLAGS], [LUA_LIBS], [LUA], [LUAC]) + ### # Checks for header files. @@ -75,4 +106,7 @@ AM_CONDITIONAL([FSEVENTS], AC_CONFIG_FILES([Makefile]) AC_OUTPUT - +AC_MSG_NOTICE([ +Summary: + Using Lua ${LUA_VERSION} +]) diff --git a/m4/ax_subst_l.m4 b/m4/ax_subst_l.m4 new file mode 100644 index 0000000..a7095c4 --- /dev/null +++ b/m4/ax_subst_l.m4 @@ -0,0 +1,15 @@ + +# ax_subst_l.m4 - Substitute every var in the given comma seperated list -*-Autoconf-*- +# +# Copyright (C) 2012 Dennis Schridde +# +# This file is free software; the authors give +# unlimited permission to copy and/or distribute it, with or without +# modifications, as long as this notice is preserved. + +# serial 1 + +# Substitute every var in the given comma seperated list +AC_DEFUN([AX_SUBST_L],[ + m4_foreach([__var__], [$@], [AC_SUBST(__var__)]) +]) From 023f0efa3c373249b2034030e07b29081901e1b8 Mon Sep 17 00:00:00 2001 From: Dennis Schridde Date: Tue, 2 Oct 2012 18:53:27 +0200 Subject: [PATCH 04/28] Check whether Lua library was compiled with compat support --- configure.ac | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/configure.ac b/configure.ac index 05ce717..db72279 100644 --- a/configure.ac +++ b/configure.ac @@ -68,6 +68,27 @@ else fi fi +_LIBS="${LIBS}" +LIBS="${LUA_LIBS}" + +AC_MSG_CHECKING([whether Lua library was compiled with compat support]) +AC_LINK_IFELSE( + [AC_LANG_PROGRAM([ + #define LUA_COMPAT_ALL + #include + ],[luaL_register(0,0,0);])], + [lua_compat_support=yes], + [lua_compat_support=no] +) +AC_MSG_RESULT([${lua_compat_support}]) + +if test "x${lua_compat_support}" = xno ; then + AC_MSG_ERROR([Lua library needs to be compiled with compat support]) +fi + +LIBS="${_LIBS}" +unset _LIBS + AX_SUBST_L([LUA_CFLAGS], [LUA_LIBS], [LUA], [LUAC]) From 91a7a78b4fcceb9acab6f3ccc0410afe32f88365 Mon Sep 17 00:00:00 2001 From: Dennis Schridde Date: Tue, 2 Oct 2012 19:23:56 +0200 Subject: [PATCH 05/28] Fix Lua compat detection for when lauxlib.h is not in /usr/include --- configure.ac | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/configure.ac b/configure.ac index db72279..c9c491d 100644 --- a/configure.ac +++ b/configure.ac @@ -69,7 +69,11 @@ else fi _LIBS="${LIBS}" -LIBS="${LUA_LIBS}" +_CFLAGS="${CFLAGS}" +_CPPFLAGS="${CPPFLAGS}" +LIBS="${LIBS} ${LUA_LIBS}" +CFLAGS="${CFLAGS} ${LUA_CFLAGS}" +CPPFLAGS="${CPPFLAGS} ${LUA_CFLAGS}" AC_MSG_CHECKING([whether Lua library was compiled with compat support]) AC_LINK_IFELSE( @@ -87,7 +91,9 @@ if test "x${lua_compat_support}" = xno ; then fi LIBS="${_LIBS}" -unset _LIBS +CFLAGS="${_CFLAGS}" +CPPFLAGS="${_CPPFLAGS}" +unset _LIBS _CFLAGS _CPPFLAGS AX_SUBST_L([LUA_CFLAGS], [LUA_LIBS], [LUA], [LUAC]) From 5e34adc29bf14b08fca145fffe4b16b84fd5c277 Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Tue, 2 Oct 2012 21:15:49 +0200 Subject: [PATCH 06/28] Actually thinking about it, lets not clean the manpage, it comes precompiled in tarballs --- Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile.am b/Makefile.am index 95c9e6b..41aa2de 100644 --- a/Makefile.am +++ b/Makefile.am @@ -38,7 +38,7 @@ EXTRA_DIST = doc/lsyncd.1.txt inotify.c fsevents.c bin2carray.lua \ doc/lsyncd.1: doc/lsyncd.1.txt $(A2X) --format=manpage $< -CLEANFILES = runner.out defaults.out runner.c defaults.c doc/lsyncd.1 +CLEANFILES = runner.out defaults.out runner.c defaults.c # compiles the runner and the defaults into the binary lsyncd_LDADD += runner.o defaults.o From e0c1b25a1b5f905fe152065f8a109ef2f315b3fb Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Tue, 2 Oct 2012 22:02:09 +0200 Subject: [PATCH 07/28] code beautifications --- lsyncd.lua | 666 ++++++++++++++++++++++++++++++++--------------------- 1 file changed, 400 insertions(+), 266 deletions(-) diff --git a/lsyncd.lua b/lsyncd.lua index 6621fd2..99b090b 100644 --- a/lsyncd.lua +++ b/lsyncd.lua @@ -466,9 +466,9 @@ local Combiner = ( function( ) log( 'Delay', - d2.etype,':',d2.path, + d2.etype, ':', d2.path, ' replaces ', - d1.etype,':',d1.path + d1.etype, ':', d1.path ) return 'replace' @@ -482,9 +482,9 @@ local Combiner = ( function( ) log( 'Delay', - d2.etype,':',d2.path, + d2.etype, ':', d2.path, ' replaces ', - d1.etype,':',d1.path + d1.etype, ':', d1.path ) return 'replace' @@ -547,6 +547,7 @@ local Combiner = ( function( ) local function combine( d1, d2 ) if d1.etype == 'Init' or d1.etype == 'Blanket' then + -- everything is blocked by init or blanket delays. if d2.path2 then log( @@ -586,6 +587,7 @@ local Combiner = ( function( ) end return nil + end -- non-move event on a move. @@ -606,6 +608,7 @@ local Combiner = ( function( ) end -- the event does something with the move destination + if d1.path2 == d2.path then if d2.etype == 'Delete' or d2.etype == 'Create' then @@ -628,7 +631,8 @@ local Combiner = ( function( ) return 'stack' end - -- on 'Attrib' or 'Modify' simply let the move go first + -- on 'Attrib' or 'Modify' simply stack on moves + return 'stack' end @@ -668,7 +672,9 @@ local Combiner = ( function( ) return nil end + -- -- a move event upon a move event + -- if d1.etype == 'Move' and d2.etype == 'Move' then -- TODO combine moves, @@ -683,11 +689,16 @@ local Combiner = ( function( ) d2.path2:byte(-1) == 47 and string.starts(d1.path, d2.path2) or d2.path2:byte(-1) == 47 and string.starts(d1.path2, d2.path2) then - log('Delay', + log( + 'Delay', 'Move:', d2.path, '->', d1.path2, - ' splits on Move:',d1.path,'->',d1.path2) + ' splits on Move:', + d1.path, '->', d1.path2 + ) + return 'split' end + return nil end @@ -696,35 +707,41 @@ local Combiner = ( function( ) -- public interface return { combine = combine } + end )( ) ------ +-- -- Creates inlets for syncs: the user interface for events. -- -local InletFactory = (function() - ----- - -- table to receive the delay of an event +local InletFactory = ( function( ) + + -- + -- Table to receive the delay of an event -- or the delay list of an event list. -- -- Keys are events and values are delays. - local e2d = {} + -- + local e2d = { } - ----- - -- table to ensure the uniqueness of every event + -- + -- Table to ensure the uniqueness of every event -- related to a delay. -- -- Keys are delay and values are events. - local e2d2 = {} + -- + local e2d2 = { } - ----- - -- allows the garbage collector to remove not refrenced + -- + -- Allows the garbage collector to remove not refrenced -- events. - setmetatable(e2d, { __mode = 'k' }) - setmetatable(e2d2, { __mode = 'v' }) + -- + setmetatable( e2d, { __mode = 'k' } ) + setmetatable( e2d2, { __mode = 'v' } ) - ----- - -- removes the trailing slash from a path - local function cutSlash(path) + -- + -- Removes the trailing slash from a path. + -- + local function cutSlash( path ) if string.byte(path, -1) == 47 then return string.sub(path, 1, -2) else @@ -732,425 +749,490 @@ local InletFactory = (function() end end - local function getPath(event) + -- + -- Gets the path of an event. + -- + local function getPath( event ) if event.move ~= 'To' then - return e2d[event].path + return e2d[ event ].path else - return e2d[event].path2 + return e2d[ event ].path2 end end - ----- + -- -- Interface for user scripts to get event fields. -- local eventFields = { - ----- + + -- -- Returns a copy of the configuration as called by sync. -- But including all inherited data and default values. -- -- TODO give user a readonly version. -- - config = function(event) - return e2d[event].sync.config + config = function( event ) + return e2d[ event ].sync.config end, ----- -- Returns the inlet belonging to an event. -- - inlet = function(event) - return e2d[event].sync.inlet + inlet = function( event ) + return e2d[ event ].sync.inlet end, - ----- + -- -- Returns the type of the event. + -- -- Can be: 'Attrib', 'Create', 'Delete', 'Modify' or 'Move', -- - etype = function(event) - return e2d[event].etype + etype = function( event ) + return e2d[ event ].etype end, - ----- - -- Tells this isn't a list. -- - isList = function() + -- Events are not lists. + -- + isList = function( ) return false end, - ----- - -- Return the status of the event. + -- + -- Returns the status of the event. + -- -- Can be: -- 'wait', 'active', 'block'. -- - status = function(event) - return e2d[event].status + status = function( event ) + return e2d[ event ].status end, - ----- - -- Returns true if event relates to a directory. -- - isdir = function(event) - return string.byte(getPath(event), -1) == 47 + -- Returns true if event relates to a directory + -- + isdir = function( event ) + return string.byte( getPath( event ), -1 ) == 47 end, - ----- + -- -- Returns the name of the file/dir. + -- -- Includes a trailing slash for dirs. -- - name = function(event) - return string.match(getPath(event), '[^/]+/?$') + name = function( event ) + return string.match( getPath( event ), '[^/]+/?$' ) end, - ----- - -- Returns the name of the file/dir. - -- Excludes a trailing slash for dirs. -- - basename = function(event) - return string.match(getPath(event), '([^/]+)/?$') + -- Returns the name of the file/dir + -- excluding a trailing slash for dirs. + -- + basename = function( event ) + return string.match( getPath( event ), '([^/]+)/?$') end, - ----- + --- -- Returns the file/dir relative to watch root - -- Includes a trailing slash for dirs. + -- including a trailing slash for dirs. -- - path = function(event) - return getPath(event) + path = function( event ) + return getPath( event ) end, - ----- + -- -- Returns the directory of the file/dir relative to watch root -- Always includes a trailing slash. -- - pathdir = function(event) - return string.match(getPath(event), '^(.*/)[^/]+/?') or '' + pathdir = function( event ) + return string.match( getPath( event ), '^(.*/)[^/]+/?' ) or '' end, - ----- + -- -- Returns the file/dir relativ to watch root - -- Excludes a trailing slash for dirs. + -- excluding a trailing slash for dirs. -- - pathname = function(event) - return cutSlash(getPath(event)) + pathname = function( event ) + return cutSlash( getPath( event ) ) end, - ------ + --- -- Returns the absolute path of the watch root. - -- All symlinks will have been resolved. + -- All symlinks are resolved. -- - source = function(event) - return e2d[event].sync.source + source = function( event ) + return e2d[ event ].sync.source end, - ------ - -- Returns the absolute path of the file/dir. - -- Includes a trailing slash for dirs. -- - sourcePath = function(event) - return e2d[event].sync.source .. getPath(event) + -- Returns the absolute path of the file/dir + -- including a trailing slash for dirs. + -- + sourcePath = function( event ) + return e2d[ event ].sync.source .. getPath( event ) end, - ------ - -- Returns the absolute dir of the file/dir. - -- Includes a trailing slash. -- - sourcePathdir = function(event) + -- Returns the absolute dir of the file/dir + -- including a trailing slash. + -- + sourcePathdir = function( event ) return e2d[event].sync.source .. - (string.match(getPath(event), '^(.*/)[^/]+/?') or '') + ( string.match( getPath( event ), '^(.*/)[^/]+/?' ) or '' ) end, ------ - -- Returns the absolute path of the file/dir. - -- Excludes a trailing slash for dirs. + -- Returns the absolute path of the file/dir + -- excluding a trailing slash for dirs. -- - sourcePathname = function(event) - return e2d[event].sync.source .. cutSlash(getPath(event)) + sourcePathname = function( event ) + return e2d[ event ].sync.source .. cutSlash( getPath( event ) ) end, - ------ - -- Returns the target. - -- Just for user comfort -- - -- (except here, the lsyncd.runner does not care event about the - -- existance of 'target', this is up to the scripts.) + -- Returns the configured target -- - target = function(event) - return e2d[event].sync.config.target + target = function( event ) + return e2d[ event ].sync.config.target end, - ------ - -- Returns the relative dir/file appended to the target. - -- Includes a trailing slash for dirs. -- - targetPath = function(event) - return e2d[event].sync.config.target .. getPath(event) + -- Returns the relative dir/file appended to the target + -- including a trailing slash for dirs. + -- + targetPath = function( event ) + return e2d[ event ].sync.config.target .. getPath( event ) end, - ------ - -- Returns the dir of the dir/file appended to the target. - -- Includes a trailing slash. -- - targetPathdir = function(event) - return e2d[event].sync.config.target .. - (string.match(getPath(event), '^(.*/)[^/]+/?') or '') + -- Returns the dir of the dir/file appended to the target + -- including a trailing slash. + -- + targetPathdir = function( event ) + return e2d[ event ].sync.config.target .. + ( string.match( getPath( event ), '^(.*/)[^/]+/?' ) or '' ) end, - ------ - -- Returns the relative dir/file appended to the target. - -- Excludes a trailing slash for dirs. -- - targetPathname = function(event) - return e2d[event].sync.config.target .. - cutSlash(getPath(event)) + -- Returns the relative dir/file appended to the target + -- excluding a trailing slash for dirs. + -- + targetPathname = function( event ) + return e2d[ event ].sync.config.target .. + cutSlash( getPath( event ) ) end, } - ----- + -- -- Retrievs event fields for the user script. -- local eventMeta = { - __index = function(event, field) - local f = eventFields[field] + + __index = function( event, field ) + local f = eventFields[ field ] if not f then if field == 'move' then -- possibly undefined return nil end - error('event does not have field "'..field..'"', 2) + error( 'event does not have field "'..field..'"', 2 ) end - return f(event) + return f( event ) end + } - ----- - -- Interface for user scripts to get event fields. + -- + -- Interface for user scripts to get list fields. -- local eventListFuncs = { - ----- + + -- -- Returns a list of paths of all events in list. -- -- @param elist -- handle returned by getevents() -- @param mutator -- if not nil called with (etype, path, path2) -- returns one or two strings to add. -- - getPaths = function(elist, mutator) + getPaths = function( elist, mutator ) + local dlist = e2d[elist] + if not dlist then - error('cannot find delay list from event list.') + error( 'cannot find delay list from event list.' ) end - local result = {} + + local result = { } local resultn = 1 - for k, d in ipairs(dlist) do + + for k, d in ipairs( dlist ) do + local s1, s2 + if mutator then - s1, s2 = mutator(d.etype, d.path, d.path2) + s1, s2 = mutator( d.etype, d.path, d.path2 ) else s1, s2 = d.path, d.path2 end - result[resultn] = s1 + + result[ resultn ] = s1 resultn = resultn + 1 + if s2 then - result[resultn] = s2 + result[ resultn ] = s2 resultn = resultn + 1 end end + return result + end } - ----- - -- Retrievs event list fields for the user script. + -- + -- Retrievs event list fields for the user script -- local eventListMeta = { - __index = function(elist, func) - if func == 'isList' then return true end - if func == 'config' then return e2d[elist].sync.config end + __index = function( elist, func ) + + if func == 'isList' then + return true + end + + if func == 'config' then + return e2d[ elist ].sync.config + end + + local f = eventListFuncs[ func ] - local f = eventListFuncs[func] if not f then - error('event list does not have function "'..func..'"', 2) + error( + 'event list does not have function "' .. func .. '"', + 2 + ) end - return function(...) - return f(elist, ...) + return function( ... ) + return f( elist, ... ) end + end + } - ----- - -- table of all inlets with their syncs -- - local inlets = {} + -- Table of all inlets with their syncs. + -- + local inlets = { } - ----- - -- allows the garbage collector to remove entries. - -- TODO check memory use - setmetatable(inlets, { __mode = 'v' }) + -- + -- Allows the garbage collector to remove entries. + -- + setmetatable( inlets, { __mode = 'v' } ) - ----- + -- -- Encapsulates a delay into an event for the user script. -- - local function d2e(delay) + local function d2e( delay ) + -- already created? local eu = e2d2[delay] if delay.etype ~= 'Move' then - if eu then return eu end - local event = {} - setmetatable(event, eventMeta) - e2d[event] = delay - e2d2[delay] = event + if eu then + return eu + end + + local event = { } + setmetatable( event, eventMeta ) + e2d[ event ] = delay + e2d2[ delay ] = event + return event + else -- moves have 2 events - origin and destination - if eu then return eu[1], eu[2] end + if eu then + return eu[1], eu[2] + end local event = { move = 'Fr' } local event2 = { move = 'To' } - setmetatable(event, eventMeta) - setmetatable(event2, eventMeta) - e2d[event] = delay - e2d[event2] = delay - e2d2[delay] = { event, event2 } + + setmetatable( event, eventMeta ) + setmetatable( event2, eventMeta ) + + e2d[ event ] = delay + e2d[ event2 ] = delay + + e2d2[ delay ] = { event, event2 } + -- move events have a field 'move' return event, event2 + end end - ----- + -- -- Encapsulates a delay list into an event list for the user script. -- - local function dl2el(dlist) - local eu = e2d2[dlist] - if eu then return eu end + local function dl2el( dlist ) + + local eu = e2d2[ dlist ] + + if eu then + return eu + end + + local elist = { } + + setmetatable( elist, eventListMeta ) + + e2d [ elist ] = dlist + e2d2[ dlist ] = elist - local elist = {} - setmetatable(elist, eventListMeta) - e2d[elist] = dlist - e2d2[dlist] = elist return elist + end - ----- + -- -- The functions the inlet provides. -- local inletFuncs = { - ----- - -- adds an exclude. + -- - addExclude = function(sync, pattern) - sync:addExclude(pattern) + -- Adds an exclude. + -- + addExclude = function( sync, pattern ) + sync:addExclude( pattern ) end, - ----- - -- removes an exclude. -- - rmExclude = function(sync, pattern) - sync:rmExclude(pattern) + -- Removes an exclude. + -- + rmExclude = function( sync, pattern ) + sync:rmExclude( pattern ) end, - ----- - -- gets the list of excludes in their original rsynlike patterns form. -- - getExcludes = function(sync) + -- Gets the list of excludes in their original rsynlike patterns form. + -- + getExcludes = function( sync ) + -- creates a copy - local e = {} + local e = { } local en = 1; - for k, _ in pairs(sync.excludes.list) do - e[en] = k; + + for k, _ in pairs( sync.excludes.list ) do + e[ en ] = k; en = en + 1; end + return e; end, - ----- + -- -- Creates a blanketEvent that blocks everything -- and is blocked by everything. -- - createBlanketEvent = function(sync) - return d2e(sync:addBlanketDelay()) + createBlanketEvent = function( sync ) + return d2e( sync:addBlanketDelay( ) ) end, - ----- + -- -- Discards a waiting event. -- - discardEvent = function(sync, event) - local delay = e2d[event] + discardEvent = function( sync, event ) + local delay = e2d[ event ] if delay.status ~= 'wait' then - log('Error', + log( + 'Error', 'Ignored cancel of a non-waiting event of type ', - event.etype) + event.etype + ) return end - sync:removeDelay(delay) + sync:removeDelay( delay ) end, - ----- + -- -- Gets the next not blocked event from queue. -- - getEvent = function(sync) - return d2e(sync:getNextDelay(now())) + getEvent = function( sync ) + return d2e( sync:getNextDelay( now( ) ) ) end, - ----- + -- -- Gets all events that are not blocked by active events. -- -- @param if not nil a function to test each delay -- - getEvents = function(sync, test) - local dlist = sync:getDelays(test) - return dl2el(dlist) + getEvents = function( sync, test ) + local dlist = sync:getDelays( test ) + return dl2el( dlist ) end, - ----- + -- -- Returns the configuration table specified by sync{} -- - getConfig = function(sync) + getConfig = function( sync ) -- TODO gives a readonly handler only. return sync.config end, } - ----- + -- -- Forwards access to inlet functions. -- local inletMeta = { - __index = function(inlet, func) - local f = inletFuncs[func] - if not f then error('inlet does not have function "'..func..'"', 2) end - return function(...) return f(inlets[inlet], ...) end + __index = function( inlet, func ) + local f = inletFuncs[ func ] + if not f then + error( + 'inlet does not have function "'..func..'"', + 2 + ) + end + + return function( ... ) + return f( inlets[ inlet ], ... ) + end end, } - ----- - -- Creates a new inlet for Sync - local function newInlet(sync) - -- lua runner controlled variables - local inlet = {} + -- + -- Creates a new inlet for Sync. + -- + local function newInlet( sync ) + + -- Lsyncd runner controlled variables + local inlet = { } -- sets use access methods - setmetatable(inlet, inletMeta) - inlets[inlet] = sync + setmetatable( inlet, inletMeta ) + inlets[ inlet ] = sync return inlet end - ----- + -- -- Returns the delay from a event. -- - local function getDelayOrList(event) - return e2d[event] + local function getDelayOrList( event ) + return e2d[ event ] end - ----- + -- -- Returns the sync from an event or list -- - local function getSync(event) - return e2d[event].sync + local function getSync( event ) + return e2d[ event ].sync end - ----- - -- public interface. - -- this one is split, one for user one for runner. + -- + -- Public interface. + -- return { getDelayOrList = getDelayOrList, d2e = d2e, @@ -1158,64 +1240,85 @@ local InletFactory = (function() getSync = getSync, newInlet = newInlet, } -end)() + +end )( ) ------ +-- -- A set of exclude patterns -- -local Excludes = (function() +local Excludes = ( function( ) - ----- - -- Turns a rsync like file pattern to a lua pattern. -- - local function toLuaPattern(p) + -- Turns a rsync like file pattern to a lua pattern. + -- ( at best it can ) + -- + local function toLuaPattern( p ) local o = p - p = string.gsub(p, '%%', '%%%%') - p = string.gsub(p, '%^', '%%^') - p = string.gsub(p, '%$', '%%$') - p = string.gsub(p, '%(', '%%(') - p = string.gsub(p, '%)', '%%)') - p = string.gsub(p, '%.', '%%.') - p = string.gsub(p, '%[', '%%[') - p = string.gsub(p, '%]', '%%]') - p = string.gsub(p, '%+', '%%+') - p = string.gsub(p, '%-', '%%-') - p = string.gsub(p, '%?', '[^/]') - p = string.gsub(p, '%*', '[^/]*') + p = string.gsub( p, '%%', '%%%%' ) + p = string.gsub( p, '%^', '%%^' ) + p = string.gsub( p, '%$', '%%$' ) + p = string.gsub( p, '%(', '%%(' ) + p = string.gsub( p, '%)', '%%)' ) + p = string.gsub( p, '%.', '%%.' ) + p = string.gsub( p, '%[', '%%[' ) + p = string.gsub( p, '%]', '%%]' ) + p = string.gsub( p, '%+', '%%+' ) + p = string.gsub( p, '%-', '%%-' ) + p = string.gsub( p, '%?', '[^/]' ) + p = string.gsub( p, '%*', '[^/]*' ) -- this was a ** before - p = string.gsub(p, '%[%^/%]%*%[%^/%]%*', '.*') - p = string.gsub(p, '^/', '^/') - if p:sub(1,2) ~= '^/' then -- does not begin with '^/' - -- all matches should begin with '/'. - p = '/'..p; + p = string.gsub( p, '%[%^/%]%*%[%^/%]%*', '.*' ) + p = string.gsub( p, '^/', '^/' ) + + if p:sub( 1, 2 ) ~= '^/' then + -- if does not begin with '^/' + -- then all matches should begin with '/'. + p = '/' .. p; end - log('Exclude', 'toLuaPattern "',o,'" = "',p,'"') + + log( + 'Exclude', + 'toLuaPattern "', + o, '" = "', p, '"' + ) + return p end - ----- - -- Adds a pattern to exclude. -- - local function add(self, pattern) - if self.list[pattern] then + -- Adds a pattern to exclude + -- + local function add( self, pattern ) + + if self.list[ pattern ] then -- already in the list return end - local lp = toLuaPattern(pattern) - self.list[pattern] = lp + + local lp = toLuaPattern( pattern ) + self.list[ pattern ] = lp + end - ----- + -- -- Removes a pattern to exclude. -- - local function remove(self, pattern) - if not self.list[pattern] then - -- already in the list - log('Normal', 'Removing not excluded exclude "'..pattern..'"') + local function remove( self, pattern ) + + if not self.list[ pattern ] then + -- already in the list? + + log( + 'Normal', + 'Removing not excluded exclude "' .. pattern .. '"' + ) + return end + self.list[pattern] = nil + end @@ -1228,47 +1331,76 @@ local Excludes = (function() end end - ----- - -- loads excludes from a file -- - local function loadFile(self, file) - f, err = io.open(file) + -- Loads the excludes from a file + -- + local function loadFile( self, file ) + + f, err = io.open( file ) + if not f then - log('Error', 'Cannot open exclude file "',file,'": ', err) + log( + 'Error', + 'Cannot open exclude file "', file,'": ', + err + ) + terminate(-1) -- ERRNO end + for line in f:lines() do + -- lsyncd 2.0 does not support includes + if not string.match(line, '%s*+') then - local p = string.match(line, '%s*-?%s*(.*)') - if p then add(self, p) end + local p = string.match( + line, '%s*-?%s*(.*)' + ) + if p then + add(self, p) + end end end - f:close() + + f:close( ) end - ----- + -- -- Tests if 'path' is excluded. -- - local function test(self, path) - for _, p in pairs(self.list) do - if p:byte(-1) == 36 then + local function test( self, path ) + + for _, p in pairs( self.list ) do + + if p:byte( -1 ) == 36 then -- ends with $ - if path:match(p) then return true end + + if path:match( p ) then + return true + end + else + -- ends either end with / or $ - if path:match(p..'/') or path:match(p..'$') then return true end + if path:match(p .. '/') or path:match(p .. '$') then + return true + end + end end + return false + end - ----- + + -- -- Cretes a new exclude set -- - local function new() + local function new( ) + return { - list = {}, + list = { }, -- functions add = add, @@ -1277,12 +1409,14 @@ local Excludes = (function() remove = remove, test = test, } + end - ----- + -- -- Public interface + -- return { new = new } -end)() +end )( ) ----- -- Holds information about one observed directory inclusively subdirs. From 887b8a004fc21e38daee11c863e3310981248eb4 Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Tue, 2 Oct 2012 22:06:05 +0200 Subject: [PATCH 08/28] repairing default.direct regression --- default-direct.lua | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/default-direct.lua b/default-direct.lua index 8944f27..ed43810 100644 --- a/default-direct.lua +++ b/default-direct.lua @@ -9,6 +9,7 @@ -- Note: -- this is infact just a configuration using Layer 1 configuration -- like any other. It only gets compiled into the binary by default. +-- -- You can simply use a modified one, by copying everything into a -- config file of yours and name it differently. -- @@ -17,12 +18,21 @@ -- --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -if not default then error('default not loaded'); end -if not default.rsync then error('default-direct (currently) needs default.rsync loaded'); end -if default.direct then error('default-direct already loaded'); end +if not default then + error('default not loaded') +end + +if not default.rsync then + error('default-direct (currently) needs default.rsync loaded') +end + +if default.direct then + error('default-direct already loaded') +end default.direct = { - ----- + + -- -- Spawns rsync for a list of events -- action = function(inlet) @@ -149,12 +159,7 @@ default.direct = { ----- -- The rsync binary called. -- - rsyncBinary = '/usr/bin/rsync', - - ----- - -- For startup sync - -- - rsyncOpts = '-lts', + rsync = default.rsync.rsync, ----- -- By default do deletes. From 698c5c3e4dc09af41b29056673cd630c4acb5568 Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Wed, 3 Oct 2012 09:23:18 +0200 Subject: [PATCH 09/28] code beautifications --- lsyncd.lua | 1765 ++++++++++++++++++++++++++++++++++------------------ 1 file changed, 1167 insertions(+), 598 deletions(-) diff --git a/lsyncd.lua b/lsyncd.lua index 99b090b..bcee34c 100644 --- a/lsyncd.lua +++ b/lsyncd.lua @@ -92,7 +92,9 @@ local Array = ( function( ) return o end - -- objects public interface + -- + -- Public interface + -- return { new = new } end )( ) @@ -177,6 +179,7 @@ local CountArray = ( function( ) return o end + -- -- Public interface -- @@ -417,6 +420,7 @@ local Delay = ( function( ) return o end + -- -- Public interface -- @@ -705,7 +709,10 @@ local Combiner = ( function( ) error( 'reached impossible state' ) end - -- public interface + + -- + -- Public interface + -- return { combine = combine } end )( ) @@ -1416,115 +1423,150 @@ local Excludes = ( function( ) -- Public interface -- return { new = new } + end )( ) ------ +-- -- Holds information about one observed directory inclusively subdirs. -- -local Sync = (function() - ----- +local Sync = ( function( ) + + -- -- Syncs that have no name specified by the user script -- get an incremental default name 'Sync[X]' -- local nextDefaultName = 1 - ----- + -- -- Adds an exclude. -- - local function addExclude(self, pattern) - return self.excludes:add(pattern) + local function addExclude( self, pattern ) + + return self.excludes:add( pattern ) + end - ----- + -- -- Removes an exclude. -- - local function rmExclude(self, pattern) - return self.excludes:remove(pattern) + local function rmExclude( self, pattern ) + + return self.excludes:remove( pattern ) + end - ----- + -- -- Removes a delay. -- - local function removeDelay(self, delay) - if self.delays[delay.dpos] ~= delay then - error('Queue is broken, delay not a dpos') + local function removeDelay( self, delay ) + if self.delays[ delay.dpos ] ~= delay then + error( 'Queue is broken, delay not a dpos' ) end - Queue.remove(self.delays, delay.dpos) + + Queue.remove( self.delays, delay.dpos ) -- free all delays blocked by this one. if delay.blocks then - for i, vd in pairs(delay.blocks) do + for i, vd in pairs( delay.blocks ) do vd.status = 'wait' end end end - ----- + -- -- Returns true if this Sync concerns about 'path' -- - local function concerns(self, path) + local function concerns( self, path ) + -- not concerned if watch rootdir doesnt match - if not path:starts(self.source) then + if not path:starts( self.source ) then return false end -- a sub dir and not concerned about subdirs if self.config.subdirs == false and - path:sub(#self.source, -1):match('[^/]+/?') + path:sub( #self.source, -1 ):match( '[^/]+/?' ) then return false end -- concerned if not excluded - return not self.excludes:test(path:sub(#self.source)) + return not self.excludes:test( path:sub( #self.source ) ) end - ----- + -- -- Collects a child process -- - local function collect(self, pid, exitcode) - local delay = self.processes[pid] + local function collect( self, pid, exitcode ) + + local delay = self.processes[ pid ] + if not delay then -- not a child of this sync. return end if delay.status then - log('Delay', 'collected an event') + + log( 'Delay', 'collected an event' ) + if delay.status ~= 'active' then error('collecting a non-active process') end + local rc = self.config.collect( - InletFactory.d2e(delay), - exitcode) + InletFactory.d2e( delay ), + exitcode + ) + if rc == 'die' then - log('Error', 'Critical exitcode.'); - terminate(-1) --ERRNO + log( 'Error', 'Critical exitcode.' ); + terminate( -1 ) --ERRNO end + if rc ~= 'again' then -- if its active again the collecter restarted the event - removeDelay(self, delay) - log('Delay', 'Finish of ',delay.etype,' on ',self.source,delay.path,' = ',exitcode) + removeDelay( self, delay ) + log( + 'Delay', + 'Finish of ', + delay.etype, + ' on ', + self.source,delay.path, + ' = ', + exitcode + ) else -- sets the delay on wait again delay.status = 'wait' + local alarm = self.config.delay + -- delays at least 1 second if alarm < 1 then alarm = 1 end - delay.alarm = now() + alarm + + delay.alarm = now( ) + alarm end else - log('Delay', 'collected a list') + log( + 'Delay', + 'collected a list' + ) + local rc = self.config.collect( - InletFactory.dl2el(delay), - exitcode) + InletFactory.dl2el( delay ), + exitcode + ) + if rc == 'die' then - log('Error', 'Critical exitcode.'); - terminate(-1) --ERRNO + log( 'Error', 'Critical exitcode.' ); + terminate( -1 ) --ERRNO end + if rc == 'again' then + -- sets the delay on wait again delay.status = 'wait' local alarm = self.config.delay @@ -1532,96 +1574,181 @@ local Sync = (function() if alarm < 1 then alarm = 1 end + alarm = now() + alarm - for _, d in ipairs(delay) do + + for _, d in ipairs( delay ) do d.alarm = alarm d.status = 'wait' end end - for _, d in ipairs(delay) do + + for _, d in ipairs( delay ) do if rc ~= 'again' then - removeDelay(self, d) + removeDelay( self, d ) else d.status = 'wait' end end - log('Delay','Finished list = ',exitcode) + + log( 'Delay','Finished list = ',exitcode ) end - self.processes[pid] = nil + + self.processes[ pid ] = nil end - ----- + -- -- Stacks a newDelay on the oldDelay, -- the oldDelay blocks the new Delay. -- -- A delay can block 'n' other delays, -- but is blocked at most by one, the latest delay. -- - local function stack(oldDelay, newDelay) + local function stack( oldDelay, newDelay ) + newDelay.status = 'block' + if not oldDelay.blocks then - oldDelay.blocks = {} + oldDelay.blocks = { } end - table.insert(oldDelay.blocks, newDelay) + + table.insert( oldDelay.blocks, newDelay ) end - ----- + -- -- Puts an action on the delay stack. -- - local function delay(self, etype, time, path, path2) - log('Function', 'delay(',self.config.name,', ',etype,', ',path,', ',path2,')') + local function delay( self, etype, time, path, path2 ) + + log( + 'Function', + 'delay(', + self.config.name, ', ', + etype, ', ', + path, ', ', + path2, + ')' + ) -- TODO - local function recurse() - if etype == 'Create' and path:byte(-1) == 47 then - local entries = lsyncd.readdir(self.source .. path) + local function recurse( ) + + if etype == 'Create' and path:byte( -1 ) == 47 then + local entries = lsyncd.readdir( self.source .. path ) + if entries then + for dirname, isdir in pairs(entries) do + local pd = path .. dirname - if isdir then pd = pd..'/' end - log('Delay', 'Create creates Create on ',pd) - delay(self, 'Create', time, pd, nil) + + if isdir then + pd = pd..'/' + end + + log( + 'Delay', + 'Create creates Create on ', + pd + ) + delay( self, 'Create', time, pd, nil ) + end + end + end + end -- exclusion tests if not path2 then -- simple test for single path events if self.excludes:test(path) then - log('Exclude', 'excluded ',etype,' on "',path,'"') + log( + 'Exclude', + 'excluded ', + etype, + ' on "', + path, + '"' + ) return end else -- for double paths (move) it might result into a split - local ex1 = self.excludes:test(path) - local ex2 = self.excludes:test(path2) + local ex1 = self.excludes:test( path ) + local ex2 = self.excludes:test( path2 ) + if ex1 and ex2 then - log('Exclude', 'excluded "',etype,' on "',path,'" -> "',path2,'"') + + log( + 'Exclude', + 'excluded "', + etype, + ' on "', + path, + '" -> "', + path2, + '"' + ) + return + elseif not ex1 and ex2 then + -- splits the move if only partly excluded - log('Exclude', 'excluded destination transformed ',etype,' to Delete ',path) - delay(self, 'Delete', time, path, nil) + log( + 'Exclude', + 'excluded destination transformed ', + etype, + ' to Delete ', + path + ) + + delay( + self, + 'Delete', + time, + path, + nil + ) + return + elseif ex1 and not ex2 then -- splits the move if only partly excluded - log('Exclude', 'excluded origin transformed ',etype,' to Create.',path2) - delay(self, 'Create', time, path2, nil) + log( + 'Exclude', + 'excluded origin transformed ', + etype, + ' to Create.', + path2 + ) + + delay( + self, + 'Create', + time, + path2, + nil + ) + return end end if etype == 'Move' and not self.config.onMove then + -- if there is no move action defined, -- split a move as delete/create -- layer 1 scripts which want moves events have to -- set onMove simply to 'true' - log('Delay', 'splitting Move into Delete & Create') - delay(self, 'Delete', time, path, nil) - delay(self, 'Create', time, path2, nil) + log( 'Delay', 'splitting Move into Delete & Create' ) + delay( self, 'Delete', time, path, nil ) + delay( self, 'Create', time, path2, nil ) return + end -- creates the new action @@ -1629,33 +1756,42 @@ local Sync = (function() if time and self.config.delay then alarm = time + self.config.delay else - alarm = now() + alarm = now( ) end + -- new delay - local nd = Delay.new(etype, self, alarm, path, path2) + local nd = Delay.new( + etype, + self, + alarm, + path, + path2 + ) + if nd.etype == 'Init' or nd.etype == 'Blanket' then -- always stack blanket events on the last event log('Delay', 'Stacking ',nd.etype,' event.') if self.delays.size > 0 then - stack(self.delays[self.delays.last], nd) + stack( self.delays[ self.delays.last ], nd ) end - nd.dpos = Queue.push(self.delays, nd) - recurse() + nd.dpos = Queue.push( self.delays, nd ) + recurse( ) return end -- detects blocks and combos by working from back until -- front through the fifo - for il, od in Queue.qpairsReverse(self.delays) do + for il, od in Queue.qpairsReverse( self.delays ) do + -- asks Combiner what to do - local ac = Combiner.combine(od, nd) + local ac = Combiner.combine( od, nd ) if ac then if ac == 'remove' then - Queue.remove(self.delays, il) + Queue.remove( self.delays, il ) elseif ac == 'stack' then - stack(od, nd) - nd.dpos = Queue.push(self.delays, nd) + stack( od, nd ) + nd.dpos = Queue.push( self.delays, nd ) elseif ac == 'absorb' then -- nada elseif ac == 'replace' then @@ -1663,75 +1799,81 @@ local Sync = (function() od.path = nd.path od.path2 = nd.path2 elseif ac == 'split' then - delay(self, 'Delete', time, path, nil) - delay(self, 'Create', time, path2, nil) + delay( self, 'Delete', time, path, nil ) + delay( self, 'Create', time, path2, nil ) else - error('unknown result of combine()') + error( 'unknown result of combine()' ) end - recurse() + recurse( ) return end + il = il - 1 end + if nd.path2 then - log('Delay','New ',nd.etype,':',nd.path,'->',nd.path2) + log( 'Delay','New ',nd.etype,':',nd.path,'->',nd.path2 ) else - log('Delay','New ',nd.etype,':',nd.path) + log( 'Delay','New ',nd.etype,':',nd.path ) end + -- no block or combo - nd.dpos = Queue.push(self.delays, nd) - recurse() + nd.dpos = Queue.push( self.delays, nd ) + recurse( ) end - ----- - -- Returns the nearest alarm for this Sync. -- - local function getAlarm(self) - if self.processes:size() >= self.config.maxProcesses then + -- Returns the soonest alarm for this Sync. + -- + local function getAlarm( self ) + + if self.processes:size( ) >= self.config.maxProcesses then return false end -- first checks if more processes could be spawned - if self.processes:size() < self.config.maxProcesses then + if self.processes:size( ) < self.config.maxProcesses then + -- finds the nearest delay waiting to be spawned - for _, d in Queue.qpairs(self.delays) do + for _, d in Queue.qpairs( self.delays ) do if d.status == 'wait' then return d.alarm end end + end -- nothing to spawn return false end - ----- + -- -- Gets all delays that are not blocked by active delays. -- -- @param test function to test each delay -- - local function getDelays(self, test) - local dlist = { sync = self} + local function getDelays( self, test ) + local dlist = { sync = self} local dlistn = 1 - local blocks = {} + local blocks = { } - ---- + -- -- inheritly transfers all blocks from delay -- - local function getBlocks(delay) - blocks[delay] = true + local function getBlocks( delay ) + blocks[ delay ] = true if delay.blocks then - for i, d in ipairs(delay.blocks) do - getBlocks(d) + for i, d in ipairs( delay.blocks ) do + getBlocks( d ) end end end - for i, d in Queue.qpairs(self.delays) do + for i, d in Queue.qpairs( self.delays ) do if d.status == 'active' or - (test and not test(InletFactory.d2e(d))) + ( test and not test( InletFactory.d2e( d ) ) ) then - getBlocks(d) - elseif not blocks[d] then - dlist[dlistn] = d + getBlocks( d ) + elseif not blocks[ d ] then + dlist[ dlistn ] = d dlistn = dlistn + 1 end end @@ -1739,21 +1881,34 @@ local Sync = (function() return dlist end - ----- + -- -- Creates new actions -- - local function invokeActions(self, timestamp) - log('Function', 'invokeActions("',self.config.name,'",',timestamp,')') - if self.processes:size() >= self.config.maxProcesses then + local function invokeActions( self, timestamp ) + + log( + 'Function', + 'invokeActions("', + self.config.name, + '",', + timestamp, + ')' + ) + + if self.processes:size( ) >= self.config.maxProcesses then -- no new processes return end - for _, d in Queue.qpairs(self.delays) do + + for _, d in Queue.qpairs( self.delays ) do -- if reached the global limit return - if settings.maxProcesses and processCount >= settings.maxProcesses then + if settings.maxProcesses and + processCount >= settings.maxProcesses + then log('Alarm', 'at global process limit.') return end + if self.delays.size < self.config.maxDelays then -- time constrains are only concerned if not maxed -- the delay FIFO already. @@ -1762,14 +1917,15 @@ local Sync = (function() return end end + if d.status == 'wait' then -- found a waiting delay if d.etype ~= 'Init' then - self.config.action(self.inlet) + self.config.action( self.inlet ) else - self.config.init(InletFactory.d2e(d)) + self.config.init( InletFactory.d2e( d ) ) end - if self.processes:size() >= self.config.maxProcesses then + if self.processes:size( ) >= self.config.maxProcesses then -- no further processes return end @@ -1777,11 +1933,13 @@ local Sync = (function() end end - ----- + -- -- Gets the next event to be processed. -- - local function getNextDelay(self, timestamp) - for i, d in Queue.qpairs(self.delays) do + local function getNextDelay( self, timestamp ) + + for i, d in Queue.qpairs( self.delays ) do + if self.delays.size < self.config.maxDelays then -- time constrains are only concerned if not maxed -- the delay FIFO already. @@ -1790,75 +1948,89 @@ local Sync = (function() return nil end end + if d.status == 'wait' then -- found a waiting delay return d end end + end ------ -- Adds and returns a blanket delay thats blocks all. -- Used as custom marker. -- - local function addBlanketDelay(self) - local newd = Delay.new('Blanket', self, true, '') - newd.dpos = Queue.push(self.delays, newd) + local function addBlanketDelay( self ) + local newd = Delay.new( 'Blanket', self, true, '' ) + newd.dpos = Queue.push( self.delays, newd ) return newd end - ------ + -- -- Adds and returns a blanket delay thats blocks all. -- Used as startup marker to call init asap. -- - local function addInitDelay(self) - local newd = Delay.new('Init', self, true, '') - newd.dpos = Queue.push(self.delays, newd) + local function addInitDelay( self ) + local newd = Delay.new( 'Init', self, true, '' ) + newd.dpos = Queue.push( self.delays, newd ) return newd end - ----- + -- -- Writes a status report about delays in this sync. -- - local function statusReport(self, f) + local function statusReport( self, f ) + local spaces = ' ' - f:write(self.config.name,' source=',self.source,'\n') - f:write('There are ',self.delays.size, ' delays\n') - for i, vd in Queue.qpairs(self.delays) do + + f:write( self.config.name, ' source=', self.source, '\n' ) + f:write( 'There are ', self.delays.size, ' delays\n') + + for i, vd in Queue.qpairs( self.delays ) do local st = vd.status - f:write(st, string.sub(spaces, 1, 7 - #st)) - f:write(vd.etype,' ') - f:write(vd.path) - if (vd.path2) then - f:write(' -> ',vd.path2) + f:write( st, string.sub( spaces, 1, 7 - #st ) ) + f:write( vd.etype, ' ' ) + f:write( vd.path ) + + if vd.path2 then + f:write( ' -> ',vd.path2 ) end + f:write('\n') + end - f:write('Excluding:\n') + + f:write( 'Excluding:\n' ) + local nothing = true - for t, p in pairs(self.excludes.list) do + + for t, p in pairs( self.excludes.list ) do nothing = false - f:write(t,'\n') + f:write( t,'\n' ) end if nothing then f:write(' nothing.\n') end - f:write('\n') + + f:write( '\n' ) end - ----- + -- -- Creates a new Sync -- - local function new(config) + local function new( config ) local s = { -- fields + config = config, - delays = Queue.new(), + delays = Queue.new( ), source = config.source, - processes = CountArray.new(), - excludes = Excludes.new(), + processes = CountArray.new( ), + excludes = Excludes.new( ), -- functions + addBlanketDelay = addBlanketDelay, addExclude = addExclude, addInitDelay = addInitDelay, @@ -1873,111 +2045,129 @@ local Sync = (function() rmExclude = rmExclude, statusReport = statusReport, } - s.inlet = InletFactory.newInlet(s) + + s.inlet = InletFactory.newInlet( s ) -- provides a default name if needed if not config.name then - config.name = 'Sync'..nextDefaultName + config.name = 'Sync' .. nextDefaultName end - -- increments default nevertheless to cause less confusion - -- so name will be the n-th call to sync{} + + -- increments defaults if a config name was given or not + -- so Sync{n} will be the n-th call to sync{} nextDefaultName = nextDefaultName + 1 -- loads exclusions if config.exclude then - local te = type(config.exclude) + + local te = type( config.exclude ) + if te == 'table' then - s.excludes:addList(config.exclude) + s.excludes:addList( config.exclude ) elseif te == 'string' then - s.excludes:add(config.exclude) + s.excludes:add( config.exclude ) else - error('type for exclude must be table or string', 2) + error( 'type for exclude must be table or string', 2 ) end end + if config.excludeFrom then - s.excludes:loadFile(config.excludeFrom) + s.excludes:loadFile( config.excludeFrom ) end return s end - ----- - -- public interface -- - return {new = new} -end)() + -- Public interface + -- + return { new = new } + +end )( ) ------ +-- -- Syncs - a singleton -- --- It maintains all configured directories to be synced. +-- Syncs maintains all configured syncs. -- -local Syncs = (function() - ----- +local Syncs = ( function( ) + + -- -- the list of all syncs -- - local list = Array.new() + local list = Array.new( ) - ----- + -- -- The round robin pointer. In case of global limited maxProcesses -- gives every sync equal chances to spawn the next process. -- local round = 1 - ----- + -- -- The cycle() sheduler goes into the next round of roundrobin. - local function nextRound() + -- + local function nextRound( ) + round = round + 1; + if round > #list then round = 1 end + return round end - ----- + -- -- Returns the round - local function getRound() + -- + local function getRound( ) return round end - ----- + -- -- Returns sync at listpos i - local function get(i) - return list[i]; + -- + local function get( i ) + return list[ i ]; end - ----- + -- -- Inheritly copies all non integer keys from - -- copy source (cs) to copy destination (cd). + -- table copy source ( cs ) to + -- table copy destination ( cd ). -- - -- all entries with integer keys are treated as new sources to copy + -- All entries with integer keys are treated as new sources to copy -- - local function inherit(cd, cs) + local function inherit( cd, cs ) + -- first copies from source all -- non-defined non-integer keyed values - for k, v in pairs(cs) do - if type(k) ~= 'number' and cd[k] == nil then - cd[k] = v + for k, v in pairs( cs ) do + if type( k ) ~= 'number' and cd[ k ] == nil then + cd[ k ] = v end end + -- first recurses into all integer keyed tables - for i, v in ipairs(cs) do - if type(v) == 'table' then - inherit(cd, v) + for i, v in ipairs( cs ) do + if type( v ) == 'table' then + inherit( cd, v ) end end + end - ----- + -- -- Adds a new sync (directory-tree to observe). -- - local function add(config) + local function add( config ) + -- Creates a new config table and inherit all keys/values -- from integer keyed tables local uconfig = config - config = {} - inherit(config, uconfig) + config = { } + inherit( config, uconfig ) -- Lets settings or commandline override delay values. if settings then @@ -1986,40 +2176,60 @@ local Syncs = (function() -- at very first lets the userscript 'prepare' function -- fill out more values. - if type(config.prepare) == 'function' then + if type( config.prepare ) == 'function' then -- explicitly gives a writeable copy of config. - config.prepare(config) + config.prepare( config ) end - if not config['source'] then - local info = debug.getinfo(3, 'Sl') - log('Error', info.short_src,':',info.currentline,': source missing from sync.') - terminate(-1) -- ERRNO + if not config[ 'source' ] then + local info = debug.getinfo( 3, 'Sl' ) + log( + 'Error', + info.short_src,':', + info.currentline,': source missing from sync.' + ) + terminate( -1 ) -- ERRNO end -- absolute path of source - local realsrc = lsyncd.realdir(config.source) + local realsrc = lsyncd.realdir( config.source ) + if not realsrc then - log('Error', 'Cannot access source directory: ',config.source) - terminate(-1) -- ERRNO + log( + 'Error', + 'Cannot access source directory: ', + config.source + ) + terminate( -1 ) -- ERRNO end + config._source = config.source config.source = realsrc - if not config.action and not config.onAttrib and - not config.onCreate and not config.onModify and - not config.onDelete and not config.onMove + if + not config.action and + not config.onAttrib and + not config.onCreate and + not config.onModify and + not config.onDelete and + not config.onMove then - local info = debug.getinfo(3, 'Sl') - log('Error', info.short_src, ':', info.currentline, - ': no actions specified, use e.g. "config = default.rsync".') - terminate(-1) -- ERRNO + local info = debug.getinfo( 3, 'Sl' ) + log( + 'Error', + info.short_src, ':', + info.currentline, + ': no actions specified, use e.g. "config = default.rsync".' + ) + + terminate( -1 ) -- ERRNO end -- loads a default value for an option if not existent if not settings then settings = {} end + local defaultValues = { 'action', 'collect', @@ -2027,55 +2237,73 @@ local Syncs = (function() 'maxDelays', 'maxProcesses', } - for _, dn in pairs(defaultValues) do - if config[dn] == nil then - config[dn] = settings[dn] or default[dn] + + for _, dn in pairs( defaultValues ) do + if config[ dn ] == nil then + config[ dn ] = settings[ dn ] or default[ dn ] end end -- the monitor to use config.monitor = - settings.monitor or config.monitor or Monitors.default() - if config.monitor ~= 'inotify' and config.monitor ~= 'fsevents' then - local info = debug.getinfo(3, 'Sl') - log('Error',info.short_src,':',info.currentline, - ': event monitor "',config.monitor,'" unknown.') - terminate(-1) -- ERRNO + settings.monitor or + config.monitor or + Monitors.default( ) + + if + config.monitor ~= 'inotify' and + config.monitor ~= 'fsevents' + then + local info = debug.getinfo( 3, 'Sl' ) + + log( + 'Error', + info.short_src, ':', + info.currentline, + ': event monitor "', + config.monitor, + '" unknown.' + ) + + terminate( -1 ) -- ERRNO end --- creates the new sync - local s = Sync.new(config) - table.insert(list, s) + local s = Sync.new( config ) + table.insert( list, s ) return s end - ----- + -- -- Allows a for-loop to walk through all syncs. -- - local function iwalk() - return ipairs(list) + local function iwalk( ) + return ipairs( list ) end - ----- + -- -- Returns the number of syncs. -- - local size = function() + local size = function( ) return #list end - ------ + -- -- Tests if any sync is interested in a path. -- - local function concerns(path) - for _, s in ipairs(list) do - if s:concerns(path) then + local function concerns( path ) + for _, s in ipairs( list ) do + if s:concerns( path ) then return true end end + return false end - -- public interface + -- + -- Public interface + -- return { add = add, get = get, @@ -2085,63 +2313,74 @@ local Syncs = (function() nextRound = nextRound, size = size } -end)() +end )( ) ------ --- Utility function, returns the relative part of absolute path if it +-- +-- Utility function, +-- Returns the relative part of absolute path if it -- begins with root -- -local function splitPath(path, root) +local function splitPath( path, root ) + local rlen = #root - local sp = string.sub(path, 1, rlen) + local sp = string.sub( path, 1, rlen ) if sp == root then - return string.sub(path, rlen, -1) + return string.sub( path, rlen, -1 ) else return nil end end ------ --- Interface to inotify, watches recursively subdirs and --- sends events. -- --- All inotify specific implementation should be enclosed here. +-- Interface to inotify. -- -local Inotify = (function() +-- watches recursively subdirs and sends events. +-- +-- All inotify specific implementation is enclosed here. +-- +local Inotify = ( function( ) - ----- - -- A list indexed by inotifies watch descriptor yielding the - -- directories absolute paths. -- - local wdpaths = CountArray.new() - - ----- - -- The same vice versa, all watch descriptors by its - -- absolute path. + -- A list indexed by inotify watch descriptors yielding + -- the directories absolute paths. -- - local pathwds = {} + local wdpaths = CountArray.new( ) - ----- - -- A list indexed by sync's containing the root path this - -- sync is interested in. -- - local syncRoots = {} + -- The same vice versa, + -- all watch descriptors by their absolute paths. + -- + local pathwds = { } - ----- + -- + -- A list indexed by syncs containing yielding + -- the root paths the syncs are interested in. + -- + local syncRoots = { } + + -- -- Stops watching a directory -- - -- @param path absolute path to unwatch - -- @param core if false not actually send the unwatch to the kernel - -- (used in moves which reuse the watch) + -- path ... absolute path to unwatch + -- core ... if false not actually send the unwatch to the kernel + -- (used in moves which reuse the watch) -- - local function removeWatch(path, core) - local wd = pathwds[path] - if not wd then return end - if core then lsyncd.inotify.rmwatch(wd) end - wdpaths[wd] = nil - pathwds[path] = nil + local function removeWatch( path, core ) + + local wd = pathwds[ path ] + + if not wd then + return + end + + if core then + lsyncd.inotify.rmwatch( wd ) + end + + wdpaths[ wd ] = nil + pathwds[ path ] = nil end ----- @@ -2159,93 +2398,138 @@ local Inotify = (function() end -- registers the watch - local inotifyMode = (settings and settings.inotifyMode) or ''; - local wd = lsyncd.inotify.addwatch(path, inotifyMode); + local inotifyMode = ( settings and settings.inotifyMode ) or ''; + local wd = lsyncd.inotify.addwatch( path, inotifyMode) ; + if wd < 0 then - log('Inotify','Unable to add watch "',path,'"') + log( 'Inotify','Unable to add watch "', path, '"' ) return end do -- If this watch descriptor is registered already -- the kernel reuses it since old dir is gone. - local op = wdpaths[wd] + local op = wdpaths[ wd ] if op and op ~= path then - pathwds[op] = nil + pathwds[ op ] = nil end end - pathwds[path] = wd - wdpaths[wd] = path + + pathwds[ path ] = wd + wdpaths[ wd ] = path -- registers and adds watches for all subdirectories - local entries = lsyncd.readdir(path) - if not entries then return end - for dirname, isdir in pairs(entries) do - if isdir then addWatch(path .. dirname .. '/') end + local entries = lsyncd.readdir( path ) + + if not entries then + return + end + + for dirname, isdir in pairs( entries ) do + if isdir then + addWatch( path .. dirname .. '/' ) + end end end - ----- + -- -- Adds a Sync to receive events. -- -- sync: Object to receive events -- rootdir: root dir to watch -- - local function addSync(sync, rootdir) - if syncRoots[sync] then error('duplicate sync in Inotify.addSync()') end - syncRoots[sync] = rootdir - addWatch(rootdir) + local function addSync( sync, rootdir ) + if syncRoots[ sync ] then + error( 'duplicate sync in Inotify.addSync()' ) + end + syncRoots[ sync ] = rootdir + addWatch( rootdir ) end - ----- + -- -- Called when an event has occured. -- - -- etype: 'Attrib', 'Modify', 'Create', 'Delete', 'Move' - -- wd: watch descriptor, matches lsyncd.inotifyadd() - -- isdir: true if filename is a directory - -- time: time of event - -- filename: string filename without path - -- wd2: watch descriptor for target if it's a Move - -- filename2: string filename without path of Move target - -- - local function event(etype, wd, isdir, time, filename, wd2, filename2) + local function event( + etype, -- 'Attrib', 'Modify', 'Create', 'Delete', 'Move' + wd, -- watch descriptor, matches lsyncd.inotifyadd() + isdir, -- true if filename is a directory + time, -- time of event + filename, -- string filename without path + wd2, -- watch descriptor for target if it's a Move + filename2 -- string filename without path of Move target + ) if isdir then - filename = filename..'/' - if filename2 then filename2 = filename2..'/' end + filename = filename .. '/' + + if filename2 then + filename2 = filename2 .. '/' + end end if filename2 then - log('Inotify','got event ',etype,' ',filename,'(',wd,') to ',filename2,'(',wd2,')') + log( + 'Inotify', + 'got event ', + etype, + ' ', + filename, + '(', wd, ') to ', + filename2, + '(', wd2 ,')' + ) else - log('Inotify','got event ',etype,' ',filename,'(',wd,')') + log( + 'Inotify', + 'got event ', + etype, + ' ', + filename, + '(', wd, ')' + ) end -- looks up the watch descriptor id - local path = wdpaths[wd] - if path then path = path..filename end + local path = wdpaths[ wd ] + if path then + path = path..filename + end - local path2 = wd2 and wdpaths[wd2] - if path2 and filename2 then path2 = path2..filename2 end + local path2 = wd2 and wdpaths[ wd2 ] + + if path2 and filename2 then + path2 = path2..filename2 + end if not path and path2 and etype == 'Move' then - log('Inotify', 'Move from deleted directory ',path2,' becomes Create.') - path = path2 + log( + 'Inotify', + 'Move from deleted directory ', + path2, + ' becomes Create.' + ) + path = path2 path2 = nil etype = 'Create' end if not path then -- this is normal in case of deleted subdirs - log('Inotify', 'event belongs to unknown watch descriptor.') + log( + 'Inotify', + 'event belongs to unknown watch descriptor.' + ) return end - for sync, root in pairs(syncRoots) do repeat - local relative = splitPath(path, root) + for sync, root in pairs( syncRoots ) do repeat + + local relative = splitPath( path, root ) local relative2 = nil + if path2 then - relative2 = splitPath(path2, root) + relative2 = splitPath( path2, root ) end + if not relative and not relative2 then -- sync is not interested in this dir break -- continue @@ -2253,103 +2537,144 @@ local Inotify = (function() -- makes a copy of etype to possibly change it local etyped = etype + if etyped == 'Move' then if not relative2 then - log('Normal', 'Transformed Move to Delete for ', sync.config.name) + log( + 'Normal', + 'Transformed Move to Delete for ', + sync.config.name + ) etyped = 'Delete' elseif not relative then relative = relative2 relative2 = nil - log('Normal', 'Transformed Move to Create for ', sync.config.name) + log( + 'Normal', + 'Transformed Move to Create for ', + sync.config.name + ) etyped = 'Create' end end if isdir then if etyped == 'Create' then - addWatch(path) + addWatch( path ) elseif etyped == 'Delete' then - removeWatch(path, true) + removeWatch( path, true ) elseif etyped == 'Move' then - removeWatch(path, false) - addWatch(path2) + removeWatch( path, false ) + addWatch( path2 ) end end - sync:delay(etyped, time, relative, relative2) + sync:delay( etyped, time, relative, relative2 ) + until true end end - ----- - -- Writes a status report about inotifies to a filedescriptor -- - local function statusReport(f) - f:write('Inotify watching ',wdpaths:size(),' directories\n') - for wd, path in wdpaths:walk() do - f:write(' ',wd,': ',path,'\n') + -- Writes a status report about inotify to a file descriptor + -- + local function statusReport( f ) + + f:write( 'Inotify watching ', wdpaths:size(), ' directories\n' ) + + for wd, path in wdpaths:walk( ) do + f:write( ' ', wd, ': ', path, '\n' ) end end - -- public interface + + -- + -- Public interface. + -- return { addSync = addSync, event = event, statusReport = statusReport, } -end)() ----- --- Interface to OSX /dev/fsevents, watches the whole filesystems --- --- All fsevents specific implementation should be enclosed here. --- -local Fsevents = (function() +end)( ) + +-- +-- Interface to OSX /dev/fsevents +-- +-- This watches all the filesystems at once, +-- but needs root access. +-- +-- All fsevents specific implementation are enclosed here. +-- +local Fsevents = ( function( ) + - ----- - -- A list indexed by sync's containing the root path this - -- sync is interested in. -- - local syncRoots = {} + -- A list indexed by syncs yielding + -- the root path the sync is interested in. + -- + local syncRoots = { } - ----- - -- adds a Sync to receive events + + -- + -- Adds a Sync to receive events. -- -- @param sync Object to receive events -- @param dir dir to watch -- - local function addSync(sync, dir) - if syncRoots[sync] then error('duplicate sync in Fanotify.addSync()') end - syncRoots[sync] = dir - end + local function addSync( sync, dir ) - ----- - -- Called when any event has occured. - -- - -- etype: 'Attrib', 'Modify', 'Create', 'Delete', 'Move') - -- isdir: true if filename is a directory - -- time: time of event - -- path: path of file - -- path2: path of target in case of 'Move' - -- - local function event(etype, isdir, time, path, path2) - if isdir then - path = path..'/' - if path2 then path2 = path2..'/' end + if syncRoots[ sync ] then + error( 'duplicate sync in Fanotify.addSync()' ) end - log('Fsevents',etype,',',isdir,',',time,',',path,',',path2) + syncRoots[ sync ] = dir + + end + + -- + -- Called when an event has occured. + -- + local function event( + etype, -- 'Attrib', 'Modify', 'Create', 'Delete', 'Move' + isdir, -- true if filename is a directory + time, -- time of event + path, -- path of file + path2 -- path of target in case of 'Move' + ) + if isdir then + path = path .. '/' + + if path2 then + path2 = path2 .. '/' + end + end + + log( + 'Fsevents', + etype, ',', + isdir, ',', + time, ',', + path, ',', + path2 + ) for _, sync in Syncs.iwalk() do repeat + local root = sync.source - if not path:starts(root) then - if not path2 or not path2:starts(root) then + + -- TODO combine ifs + if not path:starts( root ) then + if not path2 or not path2:starts( root ) then break -- continue end end - local relative = splitPath(path, root) + + local relative = splitPath( path, root ) + local relative2 if path2 then - relative2 = splitPath(path2, root) + relative2 = splitPath( path2, root ) end -- possibly change etype for this iteration only @@ -2365,66 +2690,82 @@ local Fsevents = (function() etyped = 'Create' end end - sync:delay(etyped, time, relative, relative2) + + sync:delay( etyped, time, relative, relative2 ) + until true end + end - ----- - -- Writes a status report about inotifies to a filedescriptor + -- - local function statusReport(f) + -- Writes a status report about fsevents to a filedescriptor. + -- + local function statusReport( f ) -- TODO end - -- public interface + -- + -- Public interface + -- return { - addSync = addSync, - event = event, + addSync = addSync, + event = event, statusReport = statusReport } -end)() +end )( ) ------ + +-- -- Holds information about the event monitor capabilities -- of the core. -- -Monitors = (function() +Monitors = ( function( ) - ----- + + -- -- The cores monitor list -- - local list = {} + local list = { } - ----- + + -- -- The default event monitor. -- - local function default() - return list[1] + local function default( ) + return list[ 1 ] end - ----- - -- initializes with info received from core + -- - local function initialize(clist) - for k, v in ipairs(clist) do - list[k] = v + -- Initializes with info received from core + -- + local function initialize( clist ) + for k, v in ipairs( clist ) do + list[ k ] = v end end - -- public interface - return { default = default, - list = list, - initialize = initialize + + -- + -- Public interface + -- + return { + default = default, + list = list, + initialize = initialize } -end)() ------- --- Writes functions for the user for layer 3 configuration. +end)( ) + -- -local functionWriter = (function() +-- Writes functions for the user for layer 3 configurations. +-- +local functionWriter = ( function( ) - ----- - -- all variables for layer 3 + -- + -- All variables known to layer 3 configs. + -- transVars = { { '%^pathname', 'event.pathname', 1 }, { '%^pathdir', 'event.pathdir', 1 }, @@ -2455,23 +2796,27 @@ local functionWriter = (function() { '%^d%.targetPath', 'event2.targetPath', 2 }, } - ----- - -- Splits a user string into its arguments -- - -- @param a string where parameters are seperated by spaces. + -- Splits a user string into its arguments. + -- Returns a table of arguments -- - -- @return a table of arguments - -- - local function splitStr(str) - local args = {} + local function splitStr( + str -- a string where parameters are seperated by spaces. + ) + local args = { } + while str ~= '' do + -- break where argument stops local bp = #str + -- in a quote local inQuote = false + -- tests characters to be space and not within quotes - for i=1,#str do - local c = string.sub(str, i, i) + for i=1, #str do + local c = string.sub( str, i, i ) + if c == '"' then inQuote = not inQuote elseif c == ' ' and not inQuote then @@ -2479,47 +2824,60 @@ local functionWriter = (function() break end end - local arg = string.sub(str, 1, bp) - arg = string.gsub(arg, '"', '\\"') - table.insert(args, arg) - str = string.sub(str, bp + 1, -1) - str = string.match(str, '^%s*(.-)%s*$') + + local arg = string.sub( str, 1, bp ) + arg = string.gsub( arg, '"', '\\"' ) + table.insert( args, arg ) + str = string.sub( str, bp + 1, -1 ) + str = string.match( str, '^%s*(.-)%s*$' ) + end + return args end - ----- + + -- -- Translates a call to a binary to a lua function. + -- TODO this has a little too blocking. -- - -- TODO this has a little too much coding blocks. - -- - local function translateBinary(str) + local function translateBinary( str ) + -- splits the string - local args = splitStr(str) + local args = splitStr( str ) -- true if there is a second event local haveEvent2 = false - for ia, iv in ipairs(args) do + for ia, iv in ipairs( args ) do + -- a list of arguments this arg is being split into - local a = {{true, iv}} + local a = { { true, iv } } + -- goes through all translates - for _, v in ipairs(transVars) do + for _, v in ipairs( transVars ) do local ai = 1 while ai <= #a do - if a[ai][1] then + if a[ ai ][ 1 ] then local pre, post = - string.match(a[ai][2], '(.*)"..v[1].."(.*)') + string.match( a[ ai ][ 2 ], '(.*)"..v[1].."(.*)' ) + if pre then + if v[3] > 1 then haveEvent2 = true end + if pre ~= '' then - table.insert(a, ai, {true, pre}) + table.insert( a, ai, { true, pre } ) ai = ai + 1 end - a[ai] = {false, v[2]} - if post ~= '' then table.insert(a, ai + 1, {true, post}) end + + a[ ai ] = { false, v[ 2 ] } + + if post ~= '' then + table.insert( a, ai + 1, { true, post } ) + end end end ai = ai + 1 @@ -2529,18 +2887,23 @@ local functionWriter = (function() -- concats the argument pieces into a string. local as = '' local first = true - for _, v in ipairs(a) do + + for _, v in ipairs( a ) do + if not first then as = as..' .. ' end - if v[1] then - as = as..'"'..v[2]..'"' + + if v[ 1 ] then + as = as .. '"' .. v[ 2 ] .. '"' else - as = as..v[2] + as = as .. v[ 2 ] end + first = false end - args[ia] = as + + args[ ia ] = as end local ft @@ -2549,217 +2912,324 @@ local functionWriter = (function() else ft = 'function(event, event2)\n' end - ft = ft.. - " log('Normal', 'Event ', event.etype, \n".. - " ' spawns action \"".. str.."\"')\n".. + + ft = ft .. + " log('Normal', 'Event ', event.etype, \n" .. + " ' spawns action \"".. str.."\"')\n" .. " spawn(event" - for _, v in ipairs(args) do - ft = ft..',\n '..v + + for _, v in ipairs( args ) do + ft = ft .. ',\n ' .. v end - ft = ft..')\nend' + + ft = ft .. ')\nend' return ft + end - ----- + + -- -- Translates a call using a shell to a lua function -- - local function translateShell(str) + local function translateShell( str ) + local argn = 1 - local args = {} + local args = { } local cmd = str local lc = str + -- true if there is a second event local haveEvent2 = false - for _, v in ipairs(transVars) do + for _, v in ipairs( transVars ) do + local occur = false - cmd = string.gsub(cmd, v[1], - function() + + cmd = string.gsub( + cmd, + v[ 1 ], + function( ) occur = true - return '"$'..argn..'"' - end) - lc = string.gsub(lc, v[1], ']]..'..v[2]..'..[[') + return '"$' .. argn .. '"' + end + ) + + lc = string.gsub( + lc, + v[1], + ']]..' .. v[2] .. '..[[' + ) + if occur then argn = argn + 1 - table.insert(args, v[2]) - if v[3] > 1 then + table.insert( args, v[ 2 ] ) + + if v[ 3 ] > 1 then haveEvent2 = true end end + end + local ft if not haveEvent2 then ft = 'function(event)\n' else ft = 'function(event, event2)\n' end + -- TODO do array joining instead ft = ft.. " log('Normal', 'Event ',event.etype,\n".. " [[ spawns shell \""..lc.."\"]])\n".. " spawnShell(event, [["..cmd.."]]" - for _, v in ipairs(args) do + + for _, v in ipairs( args ) do ft = ft..',\n '..v end - ft = ft..')\nend' + + ft = ft .. ')\nend' + return ft + end - ----- - -- writes a lua function for a layer 3 user script. - local function translate(str) + -- + -- Writes a lua function for a layer 3 user script. + -- + local function translate( str ) -- trim spaces - str = string.match(str, '^%s*(.-)%s*$') + str = string.match( str, '^%s*(.-)%s*$' ) local ft - if string.byte(str, 1, 1) == 47 then + if string.byte( str, 1, 1 ) == 47 then -- starts with / - ft = translateBinary(str) - elseif string.byte(str, 1, 1) == 94 then + ft = translateBinary( str ) + elseif string.byte( str, 1, 1 ) == 94 then -- starts with ^ - ft = translateShell(str:sub(2, -1)) + ft = translateShell( str:sub( 2, -1 ) ) else - ft = translateShell(str) + ft = translateShell( str ) end - log('FWrite','translated "',str,'" to \n',ft) + + log( + 'FWrite', + 'translated "', + str, + '" to \n', + ft + ) + return ft end - ----- - -- public interface + -- - return {translate = translate} -end)() + -- Public interface. + -- + return { translate = translate } ----- +end )( ) + + + +-- -- Writes a status report file at most every [statusintervall] seconds. -- --- -local StatusFile = (function() +local StatusFile = ( function( ) - ----- + + -- -- Timestamp when the status file has been written. + -- local lastWritten = false - ----- - -- Timestamp when a status file should be written + + -- + -- Timestamp when a status file should be written. + -- local alarm = false - ----- - -- Returns when the status file should be written + + -- + -- Returns the alarm when the status file should be written- -- local function getAlarm() return alarm end - ----- + + -- -- Called to check if to write a status file. -- - local function write(timestamp) - log('Function', 'write(', timestamp, ')') + local function write( timestamp ) + + log( + 'Function', + 'write(', + timestamp, + ')' + ) + + -- takes care not write too often - -- some logic to not write too often if settings.statusInterval > 0 then - -- already waiting + + -- already waiting? if alarm and timestamp < alarm then - log('Statusfile', 'waiting(',timestamp,' < ',alarm,')') + log( + 'Statusfile', + 'waiting(', + timestamp, + ' < ', + alarm, + ')' + ) return end + -- determines when a next write will be possible if not alarm then local nextWrite = lastWritten and timestamp + settings.statusInterval + if nextWrite and timestamp < nextWrite then - log('Statusfile', 'setting alarm: ', nextWrite) + log( + 'Statusfile', + 'setting alarm: ', + nextWrite + ) alarm = nextWrite + return end end + lastWritten = timestamp alarm = false end - log('Statusfile', 'writing now') - local f, err = io.open(settings.statusFile, 'w') + log( 'Statusfile', 'writing now' ) + + local f, err = io.open( settings.statusFile, 'w' ) + if not f then - log('Error', 'Cannot open status file "'..settings.statusFile.. '" :'..err) + log( + 'Error', + 'Cannot open status file "' .. + settings.statusFile .. + '" :' .. + err + ) return end - f:write('Lsyncd status report at ',os.date(),'\n\n') - for i, s in Syncs.iwalk() do - s:statusReport(f) - f:write('\n') + + f:write( 'Lsyncd status report at ', os.date( ), '\n\n' ) + + for i, s in Syncs.iwalk( ) do + s:statusReport( f ) + f:write( '\n' ) end - Inotify.statusReport(f) - f:close() + Inotify.statusReport( f ) + f:close( ) end - -- public interface - return {write = write, getAlarm = getAlarm} -end)() ------- --- Lets the userscript make its own alarms. + -- + -- Public interface + -- + return { + write = write, + getAlarm = getAlarm + } + +end )( ) + + -- -local UserAlarms = (function() - local alarms = {} +-- Lets userscripts make their own alarms. +-- +local UserAlarms = ( function( ) - ----- + local alarms = { } + + + -- -- Calls the user function at timestamp. -- - local function alarm(timestamp, func, extra) + local function alarm( timestamp, func, extra ) + local idx - for k, v in ipairs(alarms) do + for k, v in ipairs( alarms ) do if timestamp < v.timestamp then idx = k break end end - local a = {timestamp = timestamp, - func = func, - extra = extra} + + local a = { + timestamp = timestamp, + func = func, + extra = extra + } + if idx then - table.insert(alarms, idx, a) + table.insert( alarms, idx, a ) else - table.insert(alarms, a) + table.insert( alarms, a ) end + end - ---- - -- Retrieves the nearest alarm. + -- - local function getAlarm() + -- Retrieves the soonest alarm. + -- + local function getAlarm( ) + if #alarms == 0 then return false else return alarms[1].timestamp end + end - ----- + + -- -- Calls user alarms. -- - local function invoke(timestamp) - while #alarms > 0 and alarms[1].timestamp <= timestamp do - alarms[1].func(alarms[1].timestamp, alarms[1].extra) - table.remove(alarms, 1) + local function invoke( timestamp ) + while + #alarms > 0 and + alarms[ 1 ].timestamp <= timestamp + do + alarms[ 1 ].func( alarms[ 1 ].timestamp, alarms[ 1 ].extra ) + table.remove( alarms, 1 ) end end - -- public interface - return { alarm = alarm, - getAlarm = getAlarm, - invoke = invoke } -end)() + + -- + -- Public interface + -- + return { + alarm = alarm, + getAlarm = getAlarm, + invoke = invoke + } + + +end )( ) --============================================================================ --- lsyncd runner plugs. These functions will be called from core. +-- Lsyncd runner's plugs. These functions are called from core. --============================================================================ ------ --- Current status of lsyncd. +-- +-- Current status of Lsyncd. -- -- 'init' ... on (re)init -- 'run' ... normal operation @@ -2767,96 +3237,131 @@ end)() -- local lsyncdStatus = 'init' ----- --- the cores interface to the runner -- -local runner = {} +-- The cores interface to the runner. +-- +local runner = { } ------ --- Called from core whenever lua code failed. +-- +-- Called from core whenever Lua code failed. +-- -- Logs a backtrace -- -function runner.callError(message) - log('Error', 'IN LUA: ', message) +function runner.callError( message ) + log('Error', 'in Lua: ', message ) + -- prints backtrace local level = 2 while true do - local info = debug.getinfo(level, 'Sl') + + local info = debug.getinfo( level, 'Sl' ) + if not info then - terminate(-1) -- ERRNO + terminate( -1 ) -- ERRNO end - log('Error', 'Backtrace ',level - 1,' :',info.short_src,':',info.currentline) + + log( + 'Error', + 'Backtrace ', + level - 1, ' :', + info.short_src, ':', + info.currentline + ) + level = level + 1 end end ------ --- Called from code whenever a child process finished and --- zombie process was collected by core. + -- -function runner.collectProcess(pid, exitcode) +-- Called from core whenever a child process has finished and +-- the zombie process was collected by core. +-- +function runner.collectProcess( pid, exitcode ) + processCount = processCount - 1 - if processCount < 0 then error('negative number of processes!') end + + if processCount < 0 then + error( 'negative number of processes!' ) + end for _, s in Syncs.iwalk() do if s:collect(pid, exitcode) then return end end + end ------ +-- -- Called from core everytime a masterloop cycle runs through. +-- -- This happens in case of -- * an expired alarm. -- * a returned child process. -- * received filesystem events. -- * received a HUP or TERM signal. -- --- @param timestamp the current kernel time (in jiffies) --- -function runner.cycle(timestamp) - -- goes through all syncs and spawns more actions - -- if possible +function runner.cycle( + timestamp -- the current kernel time (in jiffies) +) + if lsyncdStatus == 'fade' then if processCount > 0 then - log('Normal', 'waiting for ',processCount,' more child processes.') + log( + 'Normal', + 'waiting for ', + processCount, + ' more child processes.' + ) return true else return false end end + if lsyncdStatus ~= 'run' then - error('runner.cycle() called while not running!') + error( 'runner.cycle() called while not running!' ) end - --- only let Syncs invoke actions if not on global limit - if not settings.maxProcesses or processCount < settings.maxProcesses then - local start = Syncs.getRound() + -- + -- goes through all syncs and spawns more actions + -- if possibly. But only let Syncs invoke actions if + -- not at global limit + -- + if + not settings.maxProcesses or + processCount < settings.maxProcesses + then + local start = Syncs.getRound( ) local ir = start + repeat - local s = Syncs.get(ir) - s:invokeActions(timestamp) + local s = Syncs.get( ir ) + s:invokeActions( timestamp ) ir = ir + 1 - if ir > Syncs.size() then + + if ir > Syncs.size( ) then ir = 1 end + until ir == start - Syncs.nextRound() + + Syncs.nextRound( ) end - UserAlarms.invoke(timestamp) + UserAlarms.invoke( timestamp ) if settings.statusFile then - StatusFile.write(timestamp) + StatusFile.write( timestamp ) end return true end ------ --- Called by core before anything is '-help' or '--help' is in +-- +-- Called by core if '-help' or '--help' is in -- the arguments. -- -function runner.help() +function runner.help( ) io.stdout:write( [[ @@ -2902,101 +3407,165 @@ SEE: end ------ --- settings specified by command line. -- -local clSettings = {} +-- Settings specified by command line. +-- +local clSettings = { } ------ +-- -- Called from core to parse the command line arguments --- @returns a string as user script to load. --- or simply 'true' if running with rsync bevaiour --- terminates on invalid arguments -- -function runner.configure(args, monitors) - Monitors.initialize(monitors) +-- returns a string as user script to load. +-- or simply 'true' if running with rsync bevaiour +-- +-- terminates on invalid arguments. +-- +function runner.configure( args, monitors ) - -- a list of all valid --options - -- first paramter is number of options - -- if < 0 the function checks existance - -- second paramter is function to call when in args + Monitors.initialize (monitors ) + + -- a list of all valid options + -- + -- first paramter is the number of parameters an option takes + -- if < 0 the function checks existance -- TODO what? + -- + -- second paramter is the function to call -- local options = { -- log is handled by core already. - delay = - {1, function(secs) - clSettings.delay = secs - end}, - insist = - {0, function() - clSettings.insist = true - end}, - log = - {1, nil}, - logfile = - {1, function(file) - clSettings.logfile = file - end}, - monitor = - {-1, function(monitor) - if not monitor then - io.stdout:write('This Lsyncd supports these monitors:\n') - for _, v in ipairs(Monitors.list) do - io.stdout:write(' ',v,'\n') - end - io.stdout:write('\n'); - lsyncd.terminate(-1); -- ERRNO - else - clSettings.monitor=monitor + delay = + { + 1, + function( secs ) + clSettings.delay = secs end - end}, + }, + + insist = + { + 0, + function( ) + clSettings.insist = true + end + }, + + log = + { + 1, + nil + }, + + logfile = + { + 1, + function( file ) + clSettings.logfile = file + end + }, + + monitor = + { + -1, + function( monitor ) + if not monitor then + io.stdout:write('This Lsyncd supports these monitors:\n') + for _, v in ipairs(Monitors.list) do + io.stdout:write(' ',v,'\n') + end + io.stdout:write('\n'); + lsyncd.terminate(-1); -- ERRNO + else + clSettings.monitor=monitor + end + end + }, + nodaemon = - {0, function() - clSettings.nodaemon = true - end}, + { + 0, + function( ) + clSettings.nodaemon = true + end + }, + pidfile = - {1, function(file) - clSettings.pidfile=file - end}, + { + 1, + function( file ) + clSettings.pidfile=file + end + }, + rsync = - {2, function(src, trg) - clSettings.syncs = clSettings.syncs or {} - table.insert(clSettings.syncs, {'rsync', src, trg}) - end}, + { + 2, + function( src, trg ) + clSettings.syncs = clSettings.syncs or { } + table.insert( + clSettings.syncs, + { 'rsync', src, trg } + ) + end + }, + rsyncssh = - {3, function(src, host, tdir) - clSettings.syncs = clSettings.syncs or {} - table.insert(clSettings.syncs, {'rsyncssh', src, host, tdir}) - end}, + { + 3, + function( src, host, tdir ) + clSettings.syncs = clSettings.syncs or { } + table.insert( + clSettings.syncs, + { 'rsyncssh', src, host, tdir } + ) + end + }, + direct = - {2, function(src, trg) - clSettings.syncs = clSettings.syncs or {} - table.insert(clSettings.syncs, {'direct', src, trg}) - end}, + { + 2, + function( src, trg ) + clSettings.syncs = clSettings.syncs or { } + table.insert( + clSettings.syncs, + { 'direct', src, trg } + ) + end + }, + version = - {0, function() - io.stdout:write('Version: ',lsyncd_version,'\n') - os.exit(0) - end} + { + 0, + function( ) + io.stdout:write( 'Version: ', lsyncd_version, '\n' ) + os.exit( 0 ) + end + } } - -- nonopts is filled with all args that were no part dash options - local nonopts = {} - local i = 1 + + -- non-opts is filled with all args that were no part dash options + local nonopts = { } + local i = + 1 while i <= #args do + local a = args[i] - if a:sub(1, 1) ~= '-' then - table.insert(nonopts, args[i]) + + if a:sub( 1, 1 ) ~= '-' then + table.insert( nonopts, args[ i ] ) else - if a:sub(1, 2) == '--' then - a = a:sub(3) + if a:sub( 1, 2 ) == '--' then + a = a:sub( 3 ) else - a = a:sub(2) + a = a:sub( 2 ) end - local o = options[a] + + local o = options[ a ] + if not o then log('Error','unknown option command line option ', args[i]) os.exit(-1) -- ERRNO end + if o[1] >= 0 and i + o[1] > #args then log('Error',a,' needs ',o[1],' arguments') os.exit(-1) -- ERRNO From 899077ccd7ae5369f54b2d5eced42c34f5025142 Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Wed, 3 Oct 2012 17:37:49 +0200 Subject: [PATCH 10/28] code beautifications --- lsyncd.lua | 520 ++++++++++++++++++++++++++++++++++++----------------- 1 file changed, 356 insertions(+), 164 deletions(-) diff --git a/lsyncd.lua b/lsyncd.lua index bcee34c..1f566a2 100644 --- a/lsyncd.lua +++ b/lsyncd.lua @@ -5,6 +5,8 @@ -- It works closely together with the Lsyncd core in lsyncd.c. This means it -- cannot be runned directly from the standard lua interpreter. -- +-- This code assumes your editor is at least 100 chars wide. +-- -- License: GPLv2 (see COPYING) or any later version -- Authors: Axel Kittenberger -- @@ -18,10 +20,16 @@ -- The core will exit if version ids mismatch. -- if lsyncd_version then - -- checks if the runner is being loaded twice - lsyncd.log('Error', 'You cannot use the lsyncd runner as configuration file!') - lsyncd.terminate(-1) -- ERRNO + + -- ensures the runner is not being loaded twice + lsyncd.log( + 'Error', + 'You cannot use the lsyncd runner as configuration file!' + ) + + lsyncd.terminate( -1 ) end + lsyncd_version = '2.0.7' -- @@ -1352,7 +1360,7 @@ local Excludes = ( function( ) err ) - terminate(-1) -- ERRNO + terminate( -1 ) end for line in f:lines() do @@ -1521,7 +1529,7 @@ local Sync = ( function( ) if rc == 'die' then log( 'Error', 'Critical exitcode.' ); - terminate( -1 ) --ERRNO + terminate( -1 ) end if rc ~= 'again' then @@ -1562,7 +1570,7 @@ local Sync = ( function( ) if rc == 'die' then log( 'Error', 'Critical exitcode.' ); - terminate( -1 ) --ERRNO + terminate( -1 ) end if rc == 'again' then @@ -2188,7 +2196,7 @@ local Syncs = ( function( ) info.short_src,':', info.currentline,': source missing from sync.' ) - terminate( -1 ) -- ERRNO + terminate( -1 ) end -- absolute path of source @@ -2200,7 +2208,7 @@ local Syncs = ( function( ) 'Cannot access source directory: ', config.source ) - terminate( -1 ) -- ERRNO + terminate( -1 ) end config._source = config.source @@ -2222,7 +2230,7 @@ local Syncs = ( function( ) ': no actions specified, use e.g. "config = default.rsync".' ) - terminate( -1 ) -- ERRNO + terminate( -1 ) end -- loads a default value for an option if not existent @@ -2265,7 +2273,7 @@ local Syncs = ( function( ) '" unknown.' ) - terminate( -1 ) -- ERRNO + terminate( -1 ) end --- creates the new sync @@ -3257,7 +3265,7 @@ function runner.callError( message ) local info = debug.getinfo( level, 'Sl' ) if not info then - terminate( -1 ) -- ERRNO + terminate( -1 ) end log( @@ -3403,7 +3411,7 @@ SEE: -- -monitor NAME Uses operating systems event montior NAME -- (inotify/fanotify/fsevents) - os.exit(-1) -- ERRNO + os.exit( -1 ) end @@ -3427,12 +3435,15 @@ function runner.configure( args, monitors ) -- a list of all valid options -- -- first paramter is the number of parameters an option takes - -- if < 0 the function checks existance -- TODO what? + -- if < 0 the called function has to check the presence of + -- optional arguments. -- -- second paramter is the function to call -- local options = { + -- log is handled by core already. + delay = { 1, @@ -3455,7 +3466,7 @@ function runner.configure( args, monitors ) nil }, - logfile = + logfile = { 1, function( file ) @@ -3468,14 +3479,16 @@ function runner.configure( args, monitors ) -1, function( monitor ) if not monitor then - io.stdout:write('This Lsyncd supports these monitors:\n') + io.stdout:write( 'This Lsyncd supports these monitors:\n' ) for _, v in ipairs(Monitors.list) do io.stdout:write(' ',v,'\n') end - io.stdout:write('\n'); - lsyncd.terminate(-1); -- ERRNO + + io.stdout:write('\n') + + lsyncd.terminate(-1) else - clSettings.monitor=monitor + clSettings.monitor = monitor end end }, @@ -3488,7 +3501,7 @@ function runner.configure( args, monitors ) end }, - pidfile = + pidfile = { 1, function( file ) @@ -3532,7 +3545,7 @@ function runner.configure( args, monitors ) end }, - version = + version = { 0, function( ) @@ -3543,12 +3556,13 @@ function runner.configure( args, monitors ) } -- non-opts is filled with all args that were no part dash options + local nonopts = { } - local i = - 1 + + local i = 1 while i <= #args do - local a = args[i] + local a = args[ i ] if a:sub( 1, 1 ) ~= '-' then table.insert( nonopts, args[ i ] ) @@ -3562,57 +3576,82 @@ function runner.configure( args, monitors ) local o = options[ a ] if not o then - log('Error','unknown option command line option ', args[i]) - os.exit(-1) -- ERRNO + log( + 'Error', + 'unknown option command line option ', + args[i] + ) + os.exit( -1 ) end - if o[1] >= 0 and i + o[1] > #args then - log('Error',a,' needs ',o[1],' arguments') - os.exit(-1) -- ERRNO + if o[ 1 ] >= 0 and i + o[ 1 ] > #args then + log( 'Error', a ,' needs ', o[ 1 ],' arguments' ) + os.exit( -1 ) elseif o[1] < 0 then - o[1] = -o[1] + o[ 1 ] = -o[ 1 ] end - if o[2] then - if o[1] == 0 then - o[2]() - elseif o[1] == 1 then - o[2](args[i + 1]) - elseif o[1] == 2 then - o[2](args[i + 1], args[i + 2]) - elseif o[1] == 3 then - o[2](args[i + 1], args[i + 2], args[i + 3]) + + if o[ 2 ] then + if o[ 1 ] == 0 then + o[ 2 ]( ) + elseif o[ 1 ] == 1 then + o[ 2 ]( args[i + 1] ) + elseif o[ 1 ] == 2 then + o[ 2 ]( args[i + 1], args[i + 2] ) + elseif o[ 1 ] == 3 then + o[ 2 ]( args[i + 1], args[i + 2], args[i + 3] ) end end i = i + o[1] end i = i + 1 + end if clSettings.syncs then + if #nonopts ~= 0 then - log('Error', 'There cannot be command line default syncs with a config file.') - os.exit(-1) -- ERRNO + log( + 'Error', + 'There cannot be command line syncs and config file together.' + ) + os.exit( -1 ) end + else + if #nonopts == 0 then - runner.help(args[0]) + + runner.help( args[ 0 ] ) + elseif #nonopts == 1 then - return nonopts[1] + + return nonopts[ 1 ] + else - log('Error', 'There can only be one config file in command line.') - os.exit(-1) -- ERRNO + + -- TODO make this possible + log( + 'Error', + 'There can only be one config file in command line.' + ) + os.exit( -1 ) + end + end end ----- +-- -- Called from core on init or restart after user configuration. -- --- @firstTime true the first time Lsyncd startup, false on resets --- due to HUP signal or monitor queue OVERFLOW. +-- firstTime: +-- true when Lsyncd startups the first time, +-- false on resets, due to HUP signal or monitor queue overflow. -- -function runner.initialize(firstTime) +function runner.initialize( firstTime ) + -- creates settings if user didnt settings = settings or {} @@ -3620,74 +3659,109 @@ function runner.initialize(firstTime) lockGlobals() -- copies simple settings with numeric keys to 'key=true' settings. - for k, v in ipairs(settings) do - if settings[v] then - log('Error', 'Double setting "'..v..'"') - os.exit(-1) -- ERRNO + for k, v in ipairs( settings ) do + + if settings[ v ] then + log( + 'Error', + 'Double setting "' .. v.. '"' + ) + os.exit( -1 ) end - settings[v]=true + + settings[ v ]= true end -- all command line settings overwrite config file settings - for k, v in pairs(clSettings) do + + for k, v in pairs( clSettings ) do + if k ~= 'syncs' then - settings[k]=v + settings[ k ] = v end + end - -- implicitly force insist to be true on Lsyncd resets. + -- implicitly force 'insist' on Lsyncd resets. if not firstTime then settings.insist = true end -- adds syncs specified by command line. if clSettings.syncs then - for _, s in ipairs(clSettings.syncs) do + + for _, s in ipairs( clSettings.syncs ) do + if s[1] == 'rsync' then - sync{default.rsync, source=s[2], target=s[3]} + sync{ + default.rsync, + source = s[ 2 ], + target = s[ 3 ] + } elseif s[1] == 'rsyncssh' then - sync{default.rsyncssh, source=s[2], host=s[3], targetdir=s[4]} + sync{ + default.rsyncssh, + source = s[ 2 ], + host = s[ 3 ], + targetdir=s[ 4 ] + } elseif s[1] == 'direct' then - sync{default.direct, source=s[2], target=s[3]} + sync{ default.direct, source=s[2], target=s[3]} end + end + end if settings.nodaemon then lsyncd.configure('nodaemon') end + if settings.logfile then lsyncd.configure('logfile', settings.logfile) end + if settings.logident then lsyncd.configure('logident', settings.logident) end + if settings.logfacility then lsyncd.configure('logfacility', settings.logfacility) end + if settings.pidfile then lsyncd.configure('pidfile', settings.pidfile) end - ----- - -- transfers some defaults to settings + -- + -- Transfers some defaults to settings + -- if settings.statusInterval == nil then settings.statusInterval = default.statusInterval end -- makes sure the user gave Lsyncd anything to do if Syncs.size() == 0 then - log('Error', 'Nothing to watch!') - os.exit(-1) -- ERRNO + + log( + 'Error', + 'Nothing to watch!' + ) + + os.exit( -1 ) end -- from now on use logging as configured instead of stdout/err. lsyncdStatus = 'run'; - lsyncd.configure('running'); + lsyncd.configure( 'running' ); local ufuncs = { - 'onAttrib', 'onCreate', 'onDelete', - 'onModify', 'onMove', 'onStartup', + 'onAttrib', + 'onCreate', + 'onDelete', + 'onModify', + 'onMove', + 'onStartup', } -- translates layer 3 scripts @@ -3703,247 +3777,365 @@ function runner.initialize(firstTime) end -- runs through the Syncs created by users - for _, s in Syncs.iwalk() do + for _, s in Syncs.iwalk( ) do if s.config.monitor == 'inotify' then - Inotify.addSync(s, s.source) + Inotify.addSync( s, s.source ) elseif s.config.monitor == 'fsevents' then - Fsevents.addSync(s, s.source) + Fsevents.addSync( s, s.source ) else - error('sync '..s.config.name..' has no known event monitor interface.') + error( + 'sync ' .. + s.config.name .. + ' has no known event monitor interface.' + ) end - -- if the sync has an init function, stacks an init delay - -- that will cause the init function to be called. + + -- if the sync has an init function, the init delay + -- is stacked which causes the init function to be called. if s.config.init then - s:addInitDelay() + s:addInitDelay( ) end end + end ----- --- Called by core to query soonest alarm. +-- +-- Called by core to query the soonest alarm. -- -- @return false ... no alarm, core can in untimed sleep, or -- true ... immediate action -- times ... the alarm time (only read if number is 1) -- -function runner.getAlarm() +function runner.getAlarm( ) + if lsyncdStatus ~= 'run' then return false end + local alarm = false - ---- - -- checks if current nearest alarm or a is earlier + -- - local function checkAlarm(a) + -- Checks if 'a' is sooner than the 'alarm' up-value. + -- + local function checkAlarm( a ) + if a == nil then error('got nil alarm') end + if alarm == true or not a then - -- already immediate or no new alarm + -- 'alarm' is already immediate or + -- a not a new alarm return end - -- returns the ealier time + + -- sets 'alarm' to a if a is sooner if not alarm or a < alarm then alarm = a end + end - -- checks all syncs for their earliest alarm + -- + -- checks all syncs for their earliest alarm, -- but only if the global process limit is not yet reached. - if not settings.maxProcesses or processCount < settings.maxProcesses then - for _, s in Syncs.iwalk() do - checkAlarm(s:getAlarm()) + -- + if + not settings.maxProcesses or + processCount < settings.maxProcesses + then + for _, s in Syncs.iwalk( ) do + checkAlarm( s:getAlarm ( )) end else - log('Alarm', 'at global process limit.') + log( + 'Alarm', + 'at global process limit.' + ) end -- checks if a statusfile write has been delayed - checkAlarm(StatusFile.getAlarm()) - -- checks for an userAlarm - checkAlarm(UserAlarms.getAlarm()) + checkAlarm( StatusFile.getAlarm( ) ) + + -- checks for an userAlarm + checkAlarm( UserAlarms.getAlarm( ) ) + + log( + 'Alarm', + 'runner.getAlarm returns: ', + alarm + ) - log('Alarm', 'runner.getAlarm returns: ',alarm) return alarm + end ------ --- Called when an inotify event arrived. --- Simply forwards it directly to the object. +-- +-- Called when an file system monitor events arrive -- runner.inotifyEvent = Inotify.event runner.fsEventsEvent = Fsevents.event ------ +-- -- Collector for every child process that finished in startup phase -- --- Parameters are pid and exitcode of child process --- --- Can return either a new pid if one other child process --- has been spawned as replacement (e.g. retry) or 0 if --- finished/ok. --- -function runner.collector(pid, exitcode) +function runner.collector( + pid, -- pid of the child process + exitcode -- exitcode of the child process +) if exitcode ~= 0 then log('Error', 'Startup process',pid,' failed') - terminate(-1) -- ERRNO + terminate( -1 ) end + return 0 end ------ +-- -- Called by core when an overflow happened. -- -function runner.overflow() - log('Normal', '--- OVERFLOW in event queue ---') +function runner.overflow( ) + + log( + 'Normal', + '--- OVERFLOW in event queue ---' + ) + lsyncdStatus = 'fade' + end ------ +-- -- Called by core on a hup signal. -- -function runner.hup() - log('Normal', '--- HUP signal, resetting ---') +function runner.hup( ) + + log( + 'Normal', + '--- HUP signal, resetting ---' + ) + lsyncdStatus = 'fade' + end ------ +-- -- Called by core on a term signal. -- -function runner.term() - log('Normal', '--- TERM signal, fading ---') +function runner.term( ) + + log( + 'Normal', + '--- TERM signal, fading ---' + ) + lsyncdStatus = 'fade' + end --============================================================================ --- Lsyncd user interface +-- Lsyncd runner's user interface --============================================================================ ------ --- Main utility to create new observations. --- @returns an Inlet to that sync. -- -function sync(opts) +-- Main utility to create new observations. +-- +-- Returns an Inlet to that sync. +-- +function sync( opts ) + if lsyncdStatus ~= 'init' then - error('Sync can only be created during initialization.', 2) + error( + 'Sync can only be created during initialization.', + 2 + ) end - return Syncs.add(opts).inlet + + return Syncs.add( opts ).inlet + end ------ +-- -- Spawns a new child process. -- --- @param agent the reason why a process is spawned. --- normally this is a delay/event of a sync. --- it will mark the related files as blocked. --- @param binary binary to call --- @param ... arguments --- -function spawn(agent, binary, ...) - if agent == nil or type(agent) ~= 'table' then - error('spawning with an invalid agent', 2) +function spawn( + agent, -- the reason why a process is spawned. + -- a delay or delay list for a sync + -- it will mark the related files as blocked. + binary, -- binary to call + ... -- arguments +) + if + agent == nil or + type( agent ) ~= 'table' + then + error( + 'spawning with an invalid agent', + 2 + ) end if lsyncdStatus == 'fade' then - log('Normal', 'ignored process spawning while fading') + log( + 'Normal', + 'ignored process spawning while fading' + ) return end - if type(binary) ~= 'string' then - error('calling spawn(agent, binary, ...), binary is not a string', 2) + if type( binary ) ~= 'string' then + error( + 'calling spawn(agent, binary, ...): binary is not a string', + 2 + ) end - local dol = InletFactory.getDelayOrList(agent) - if not dol then error('spawning with an unknown agent', 2) end + local dol = InletFactory.getDelayOrList( agent ) - -- checks if spawn is called on already active event + if not dol then + error( + 'spawning with an unknown agent', + 2 + ) + end + + -- + -- checks if a spawn is called on an already active event + -- if dol.status then + + -- is an event + if dol.status ~= 'wait' then error('spawn() called on an non-waiting event', 2) end - else -- is a list + + else + -- is a list + for _, d in ipairs(dol) do if d.status ~= 'wait' and d.status ~= 'block' then error('spawn() called on an non-waiting event list', 2) end end + end - local pid = lsyncd.exec(binary, ...) + -- + -- tries to spawn the process + -- + local pid = lsyncd.exec( binary, ... ) if pid and pid > 0 then + processCount = processCount + 1 - if settings.maxProcesses and processCount > settings.maxProcesses then - error('Spawned too much processes!') + if + settings.maxProcesses and + processCount > settings.maxProcesses + then + error( 'Spawned too much processes!' ) end - local sync = InletFactory.getSync(agent) + + local sync = InletFactory.getSync( agent ) + -- delay or list if dol.status then + -- is a delay dol.status = 'active' - sync.processes[pid] = dol + sync.processes[ pid ] = dol + else + -- is a list - for _, d in ipairs(dol) do + for _, d in ipairs( dol ) do d.status = 'active' end - sync.processes[pid] = dol + sync.processes[ pid ] = dol + end + end end ------ +-- -- Spawns a child process using the default shell. -- -function spawnShell(agent, command, ...) - return spawn(agent, '/bin/sh', '-c', command, '/bin/sh', ...) +function spawnShell( + agent, -- the delay(list) to spawn the command for + command, -- the shell command + ... -- additonal arguments +) + return spawn( + agent, + '/bin/sh', + '-c', + command, + '/bin/sh', + ... + ) end ----- -- Observes a filedescriptor -- -function observefd(fd, ready, writey) - return lsyncd.observe_fd(fd, ready, writey) +function observefd( + fd, -- file descriptor + ready, -- called when fd is ready to be read + writey -- called when fd is ready to be written +) + return lsyncd.observe_fd( + fd, + ready, + writey + ) end ------ --- Nonobserves a filedescriptor -- -function nonobservefd(fd) - return lsyncd.nonobserve_fd(fd) +-- Stops observeing a filedescriptor +-- +function nonobservefd( + fd -- file descriptor +) + return lsyncd.nonobserve_fd( fd ) end ------ +-- -- Calls func at timestamp. +-- -- Use now() to receive current timestamp --- add seconds with '+' to it) +-- add seconds with '+' to it -- alarm = UserAlarms.alarm ------ +-- -- Comfort routine also for user. -- Returns true if 'String' starts with 'Start' -- -function string.starts(String,Start) - return string.sub(String,1,#Start)==Start +function string.starts( String, Start ) + + return string.sub( String, 1, #Start )==Start + end ------ +-- -- Comfort routine also for user. -- Returns true if 'String' ends with 'End' -- -function string.ends(String,End) - return End=='' or string.sub(String,-#End)==End +function string.ends( String, End ) + + return End == '' or string.sub( String, -#End ) == End + end ------ --- provides a default empty settings table. -- -settings = {} +-- Provides a default empty settings table. +-- +settings = { } ------ +-- -- Returns the core the runners function interface. -- return runner From 2e9c103f5502cc18bc24e2d0e3923453edb8dbbd Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Wed, 3 Oct 2012 18:34:09 +0200 Subject: [PATCH 11/28] beautifing code --- default-rsync.lua | 539 ++++++++++++++++++++++++++-------------------- default.lua | 4 +- 2 files changed, 307 insertions(+), 236 deletions(-) diff --git a/default-rsync.lua b/default-rsync.lua index 3fdd0ef..86731a7 100644 --- a/default-rsync.lua +++ b/default-rsync.lua @@ -23,257 +23,326 @@ if default.rsync then error('default-rsync already loaded') end -default.rsync = { - ----- - -- Spawns rsync for a list of events +default.rsync = { } + +-- +-- Spawns rsync for a list of events +-- +-- Exlcusions are already handled by not having +-- events for them. +-- +default.rsync.action = function( inlet ) + -- - action = function(inlet) - -- gets all events ready for syncing - local elist = inlet.getEvents( - function(event) - return event.etype ~= 'Init' and event.etype ~= 'Blanket' + -- gets all events ready for syncing + -- + local elist = inlet.getEvents( + function(event) + return event.etype ~= 'Init' and event.etype ~= 'Blanket' + end + ) + + -- + -- Replaces what rsync would consider filter rules by literals + -- + local function sub( p ) + if not p then + return + end + + return p: + gsub('%?', '\\?'): + gsub('%*', '\\*'): + gsub('%[', '\\['): + gsub('%]', '\\]') + end + + -- + -- Gets the list of paths for the event list + -- + -- Deletes create multi match patterns + -- + local paths = elist.getPaths( + function( etype, path1, path2 ) + if string.byte( path1, -1 ) == 47 and etype == 'Delete' then + return sub( path1 )..'***', sub( path2 ) + else + return sub( path1 ), sub( path2 ) end + end + ) + + -- + -- stores all filters by integer index + -- + local filterI = { } + + -- + -- Stores all filters with path index + -- + local filterP = { } + + -- + -- Adds one path to the filter + -- + local function addToFilter( path ) + + if filterP[ path ] then + return + end + + filterP[ path ] = true + + table.insert( filterI, path ) + end + + -- + -- Adds a path to the filter. + -- + -- Rsync needs to have entries for all steps in the path, + -- so the file for example d1/d2/d3/f1 needs following filters: + -- 'd1/', 'd1/d2/', 'd1/d2/d3/' and 'd1/d2/d3/f1' + -- + for _, path in ipairs( paths ) do + + if path and path ~= '' then + + addToFilter(path) + + local pp = string.match( path, '^(.*/)[^/]+/?' ) + + while pp do + addToFilter(pp) + pp = string.match( pp, '^(.*/)[^/]+/?' ) + end + + end + + end + + local filterS = table.concat( filterI, '\n' ) + local filter0 = table.concat( filterI, '\000' ) + + log( + 'Normal', + 'Calling rsync with filter-list of new/modified files/dirs\n', + filterS + ) + + local config = inlet.getConfig( ) + local delete = nil + + if config.delete then + delete = { '--delete', '--ignore-errors' } + end + + spawn( + elist, + config.rsync.binary, + '<', filter0, + config.rsync._computed, + '-r', + delete, + '--force', + '--from0', + '--include-from=-', + '--exclude=*', + config.source, + config.target + ) + +end + +-- +-- Spawns the recursive startup sync +-- +init = function(event) + + local config = event.config + local inlet = event.inlet + local excludes = inlet.getExcludes( ) + local delete = nil + + if config.delete then + delete = { '--delete', '--ignore-errors' } + end + + if #excludes == 0 then + -- start rsync without any excludes + log( + 'Normal', + 'recursive startup rsync: ', + config.source, + ' -> ', + config.target ) - ----- - -- replaces filter rule by literals - -- - local function sub(p) - if not p then - return - end - return p:gsub('%?', '\\?'): - gsub('%*', '\\*'): - gsub('%[', '\\['): - gsub('%]', '\\]') - end - - local paths = elist.getPaths( - function(etype, path1, path2) - if string.byte(path1, -1) == 47 and etype == 'Delete' then - return sub(path1)..'***', sub(path2) - else - return sub(path1), sub(path2) - end - end) - -- stores all filters with integer index - -- local filterI = inlet.getExcludes() - local filterI = {} - -- stores all filters with path index - local filterP = {} - - -- adds one entry into the filter - -- @param path ... path to add - -- @param leaf ... true if this the original path - -- false if its a parent - local function addToFilter(path) - if filterP[path] then - return - end - filterP[path]=true - table.insert(filterI, path) - end - - -- adds a path to the filter, for rsync this needs - -- to have entries for all steps in the path, so the file - -- d1/d2/d3/f1 needs filters - -- 'd1/', 'd1/d2/', 'd1/d2/d3/' and 'd1/d2/d3/f1' - for _, path in ipairs(paths) do - if path and path ~= '' then - addToFilter(path) - local pp = string.match(path, '^(.*/)[^/]+/?') - while pp do - addToFilter(pp) - pp = string.match(pp, '^(.*/)[^/]+/?') - end - end - end - - local filterS = table.concat(filterI, '\n') - local filter0 = table.concat(filterI, '\000') - log('Normal', 'Calling rsync with filter-list of new/modified files/dirs\n', filterS) - local config = inlet.getConfig() - local delete = nil - - if config.delete then - delete = { '--delete', '--ignore-errors' } - end - - - spawn(elist, config.rsync.binary, - '<', filter0, + spawn( + event, + config.rsync.binary, + delete, config.rsync._computed, '-r', - delete, - '--force', - '--from0', - '--include-from=-', - '--exclude=*', config.source, - config.target) - end, + config.target + ) - ----- - -- Spawns the recursive startup sync - -- - init = function(event) - local config = event.config - local inlet = event.inlet - local excludes = inlet.getExcludes() - local delete = nil + else + -- start rsync providing an exclude list + -- on stdin + local exS = table.concat( excludes, '\n' ) - if config.delete then - delete = { '--delete', '--ignore-errors' } + log( + 'Normal', + 'recursive startup rsync: ', + config.source, + ' -> ', + config.target, + ' excluding\n', + exS + ) + + spawn( + event, + config.rsync.binary, + '<', exS, + '--exclude-from=-', + delete, + config.rsync._computed, + '-r', + config.source, + config.target + ) + end +end + +-- +-- Prepares and checks a syncs configuration on startup. +-- +default.rsync.prepare = function( config ) + + if not config.target then + error( + 'default.rsync needs "target" configured', + 4 + ) + end + + if config.rsyncOps then + error( + '"rsyncOps" is outdated please use the new rsync = { ... } syntax.', + 4 + ) + end + + if config.rsyncOpts and config.rsync._extra then + error( + '"rsyncOpts" is outdated in favor of the new rsync = { ... } syntax\n"' + + 'for which you provided the _extra attribute as well.\n"' + + 'Please remove rsyncOpts from your config.', + 4 + ) + end + + if config.rsyncOpts then + log( + 'Warn', + '"rsyncOpts" is outdated. Please use the new rsync = { ... } syntax."' + ) + + config.rsync._extra = config.rsyncOpts + config.rsyncOpts = nil + end + + if config.rsyncBinary and config.rsync.binary then + error( + '"rsyncBinary is outdated in favor of the new rsync = { ... } syntax\n"'+ + 'for which you provided the binary attribute as well.\n"' + + "Please remove rsyncBinary from your config.'", + 4 + ) + end + + if config.rsyncBinary then + log( + 'Warn', + '"rsyncBinary" is outdated. Please use the new rsync = { ... } syntax."' + ) + + config.rsync.binary = config.rsyncBinary + config.rsyncOpts = nil + end + + -- checks if the _computed argument exists already + if config.rsync._computed then + error( + 'please do not use the internal rsync._computed parameter', + 4 + ) + end + + -- computes the rsync arguments into one list + local rsync = config.rsync; + rsync._computed = { true } + local computed = rsync._computed + local shorts = { '-' } + + if config.rsync._extra then + for k, v in ipairs( config.rsync._extra ) do + computed[ k + 1 ] = v end + end - if #excludes == 0 then - log('Normal', 'recursive startup rsync: ', config.source, ' -> ', config.target) - spawn(event, config.rsync.binary, - delete, - config.rsync._computed, - '-r', - config.source, - config.target) - else - local exS = table.concat(excludes, '\n') - log('Normal', 'recursive startup rsync: ',config.source, - ' -> ',config.target,' excluding\n',exS) - spawn(event, config.rsync.binary, - '<', exS, - '--exclude-from=-', - delete, - config.rsync._computed, - '-r', - config.source, - config.target) - end - end, + if rsync.links then + shorts[ #shorts + 1 ] = 'l' + end - ----- - -- Checks the configuration. - -- - prepare = function(config) - if not config.target then - error('default.rsync needs "target" configured', 4) - end + if rsync.times then + shorts[ #shorts + 1 ] = 't' + end - if config.rsyncOps then - error('"rsyncOps" is outdated please use the new rsync = { ... } syntax.', 4) - end + if rsync.protectArgs then + shorts[ #shorts + 1 ] = 's' + end - if config.rsyncOpts and config.rsync._extra then - error( - '"rsyncOpts" is outdated in favor of the new rsync = { ... } syntax\n"' + - 'for which you provided the _extra attribute as well.\n"' + - 'Please remove rsyncOpts from your config.', - 4 - ) - end + if #shorts ~= 1 then + computed[ 1 ] = table.concat( shorts, '' ) + else + computed[ 1 ] = { } + end - if config.rsyncOpts then - log( - 'Warn', - '"rsyncOpts" is outdated. Please use the new rsync = { ... } syntax."', - event.etype, '"' - ) + -- appends a / to target if not present + if string.sub(config.target, -1) ~= '/' then + config.target = config.target..'/' + end +end - config.rsync._extra = config.rsyncOpts +-- +-- rsync uses default collect +-- - config.rsyncOpts = nil - end +-- +-- By default do deletes. +-- +default.rsync.delete = true - if config.rsyncBinary and config.rsync.binary then - error( - '"rsyncBinary is outdated in favor of the new rsync = { ... } syntax\n"'+ - 'for which you provided the binary attribute as well.\n"' + - "Please remove rsyncBinary from your config.'", - 4 - ) - end - - if config.rsyncBinary then - log( - 'Warn', - '"rsyncBinary" is outdated. Please use the new rsync = { ... } syntax."', - event.etype, '"' - ) - - config.rsync.binary = config.rsyncBinary - - config.rsyncOpts = nil - end - - -- checks if the _computed argument does not exist already - if config.rsync._computed then - error( - 'please do not use the internal rsync._computed parameter', - 4 - ) - end - - -- computes the rsync arguments into one list - local rsync = config.rsync; - rsync._computed = { true } - local computed = rsync._computed - local shorts = { '-' } - - if config.rsync._extra then - for k, v in ipairs( config.rsync._extra ) do - computed[ k + 1 ] = v - end - end - - if rsync.links then - shorts[ #shorts + 1 ] = 'l' - end - - if rsync.times then - shorts[ #shorts + 1 ] = 't' - end - - if rsync.protectArgs then - shorts[ #shorts + 1 ] = 's' - end - - if #shorts ~= 1 then - computed[ 1 ] = table.concat( shorts, '' ) - else - computed[ 1 ] = { } - end - - -- appends a / to target if not present - if string.sub(config.target, -1) ~= '/' then - config.target = config.target..'/' - end - end, - - ----- - -- rsync uses default collect - ---- - - ----- - -- By default do deletes. - -- - delete = true, - - ----- - -- Calls rsync with this default short opts. - -- - rsync = { - -- The rsync binary to be called. - binary = '/usr/bin/rsync', - links = true, - times = true, - protectArgs = true - }, - - ----- - -- Exit codes for rsync. - -- - exitcodes = default.rsyncExitCodes, - - ----- - -- Default delay - -- - delay = 15, +-- +-- Calls rsync with this default options +-- +default.rsync.rsync = { + -- The rsync binary to be called. + binary = '/usr/bin/rsync', + links = true, + times = true, + protectArgs = true } + +-- +-- Exit codes for rsync. +-- +default.rsync.exitcodes = default.rsyncExitCodes + +-- +-- Default delay +-- +default.rsync.delay = 15 diff --git a/default.lua b/default.lua index 68c9d44..7fde188 100644 --- a/default.lua +++ b/default.lua @@ -8,7 +8,9 @@ -- Authors: Axel Kittenberger --============================================================================ -if default then error('default already loaded'); end +if default then + error('default already loaded') +end default = { ----- From 1bf1d13eaac51962ae80272745c828aa8f2f734e Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Fri, 5 Oct 2012 09:41:46 +0200 Subject: [PATCH 12/28] new inheritance mechanics, code beautifications --- configure.ac | 2 +- default-rsync.lua | 36 ++- default.lua | 100 +++++-- inotify.c | 565 ++++++++++++++++++++++++++-------------- lsyncd.lua | 181 ++++++++++--- tests/exclude-rsync.lua | 2 +- 6 files changed, 610 insertions(+), 276 deletions(-) diff --git a/configure.ac b/configure.ac index c9c491d..e5ad276 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. #AC_PREREQ(2.60) -AC_INIT(lsyncd, 2.0.7, axkibe@gmail.com) +AC_INIT(lsyncd, 2.1.0-beta, axkibe@gmail.com) AM_INIT_AUTOMAKE([foreign]) AC_CONFIG_MACRO_DIR([m4]) diff --git a/default-rsync.lua b/default-rsync.lua index 86731a7..73a3c4e 100644 --- a/default-rsync.lua +++ b/default-rsync.lua @@ -15,15 +15,19 @@ -- --~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + if not default then - error('default not loaded') + error( 'default not loaded' ) end + if default.rsync then - error('default-rsync already loaded') + error( 'default-rsync already loaded' ) end + default.rsync = { } +local rsync = default.rsync -- -- Spawns rsync for a list of events @@ -31,7 +35,7 @@ default.rsync = { } -- Exlcusions are already handled by not having -- events for them. -- -default.rsync.action = function( inlet ) +rsync.action = function( inlet ) -- -- gets all events ready for syncing @@ -51,10 +55,10 @@ default.rsync.action = function( inlet ) end return p: - gsub('%?', '\\?'): - gsub('%*', '\\*'): - gsub('%[', '\\['): - gsub('%]', '\\]') + gsub( '%?', '\\?' ): + gsub( '%*', '\\*' ): + gsub( '%[', '\\[' ): + gsub( '%]', '\\]' ) end -- @@ -153,10 +157,11 @@ default.rsync.action = function( inlet ) end + -- -- Spawns the recursive startup sync -- -init = function(event) +rsync.init = function(event) local config = event.config local inlet = event.inlet @@ -216,10 +221,11 @@ init = function(event) end end + -- -- Prepares and checks a syncs configuration on startup. -- -default.rsync.prepare = function( config ) +rsync.prepare = function( config ) if not config.target then error( @@ -317,19 +323,22 @@ default.rsync.prepare = function( config ) end end + -- -- rsync uses default collect -- + -- -- By default do deletes. -- -default.rsync.delete = true +rsync.delete = true + -- -- Calls rsync with this default options -- -default.rsync.rsync = { +rsync.rsync = { -- The rsync binary to be called. binary = '/usr/bin/rsync', links = true, @@ -337,12 +346,13 @@ default.rsync.rsync = { protectArgs = true } + -- -- Exit codes for rsync. -- -default.rsync.exitcodes = default.rsyncExitCodes +rsync.exitcodes = default.rsyncExitCodes -- -- Default delay -- -default.rsync.delay = 15 +rsync.delay = 15 diff --git a/default.lua b/default.lua index 7fde188..7ec7e05 100644 --- a/default.lua +++ b/default.lua @@ -38,8 +38,10 @@ default = { -- Called when collecting a finished child process -- collect = function(agent, exitcode) + local config = agent.config local rc + if config.exitcodes then rc = config.exitcodes[exitcode] elseif exitcode == 0 then @@ -71,7 +73,8 @@ default = { agent.source, '". Terminating since "insist" is not set.' ) - terminate(-1) -- ERRNO + + terminate( -1 ) end elseif rc == 'die' then log( @@ -80,7 +83,8 @@ default = { agent.source, '".' ) - terminate(-1) -- ERRNO + + terminate( -1 ) else log( 'Error', @@ -96,13 +100,29 @@ default = { if agent.isList then if rc == 'ok' then - log('Normal', 'Finished a list = ',exitcode) + log( + 'Normal', + 'Finished a list after exitcode: ', + exitcode + ) elseif rc == 'again' then - log('Normal', 'Retrying a list on exitcode = ',exitcode) + log( + 'Normal', + 'Retrying a list after exitcode = ', + exitcode + ) elseif rc == 'die' then - log('Error', 'Failure with a list on exitcode = ',exitcode) + log( + 'Error', + 'Failure with a list width exitcode = ', + exitcode + ) else - log('Error', 'Unknown exitcode "',exitcode,'" with a list') + log( + 'Error', + 'Unknown exitcode "',exitcode,'" with a list' + ) + rc = 'die' end else @@ -164,34 +184,58 @@ default = { -- Exitcodes of rsync and what to do. -- rsyncExitCodes = { - [ 0] = 'ok', - [ 1] = 'die', - [ 2] = 'die', - [ 3] = 'again', - [ 4] = 'die', - [ 5] = 'again', - [ 6] = 'again', - [ 10] = 'again', - [ 11] = 'again', - [ 12] = 'again', - [ 14] = 'again', - [ 20] = 'again', - [ 21] = 'again', - [ 22] = 'again', - [ 23] = 'ok', -- partial transfers are ok, since Lsyncd has registered the event that - [ 24] = 'ok', -- caused the transfer to be partial and will recall rsync. - [ 25] = 'die', - [ 30] = 'again', - [ 35] = 'again', - [255] = 'again', + + -- + -- if another config provides the same table + -- this will not be inherited (merged) into that one + -- + -- if it does not, integer keys are to be copied + -- verbatim + -- + _merge = false, + _verbatim = true, + + [ 0 ] = 'ok', + [ 1 ] = 'die', + [ 2 ] = 'die', + [ 3 ] = 'again', + [ 4 ] = 'die', + [ 5 ] = 'again', + [ 6 ] = 'again', + [ 10 ] = 'again', + [ 11 ] = 'again', + [ 12 ] = 'again', + [ 14 ] = 'again', + [ 20 ] = 'again', + [ 21 ] = 'again', + [ 22 ] = 'again', + -- partial transfers are ok, since Lsyncd has registered the event that + -- caused the transfer to be partial and will recall rsync. + [ 23 ] = 'ok', + [ 24 ] = 'ok', + [ 25 ] = 'die', + [ 30 ] = 'again', + [ 35 ] = 'again', + [ 255 ] = 'again', }, ----- -- Exitcodes of ssh and what to do. -- sshExitCodes = { - [0] = 'ok', - [255] = 'again', + + -- + -- if another config provides the same table + -- this will not be inherited (merged) into that one + -- + -- if it does not, integer keys are to be copied + -- verbatim + -- + _merge = false, + _verbatim = true, + + [ 0 ] = 'ok', + [ 255 ] = 'again', }, ----- diff --git a/inotify.c b/inotify.c index fcbcef0..335225a 100644 --- a/inotify.c +++ b/inotify.c @@ -1,14 +1,14 @@ -/** - * inotify.c from Lsyncd - Live (Mirror) Syncing Demon - * - * License: GPLv2 (see COPYING) or any later version - * - * Authors: Axel Kittenberger - * - * ----------------------------------------------------------------------- - * - * Event interface for Lsyncd to Linux´ inotify. - */ +/* +| inotify.c from Lsyncd - Live (Mirror) Syncing Demon +| +| License: GPLv2 (see COPYING) or any later version +| +| Authors: Axel Kittenberger +| +| ----------------------------------------------------------------------- +| +| Event interface for Lsyncd to Linux´ inotify. +*/ #include "lsyncd.h" @@ -39,83 +39,130 @@ #include #include -/*----------------------------------------------------------------------------- - * Event types. - */ + +/* +| Event types. +*/ static const char * ATTRIB = "Attrib"; static const char * MODIFY = "Modify"; static const char * CREATE = "Create"; static const char * DELETE = "Delete"; static const char * MOVE = "Move"; -/** + +/* * The inotify file descriptor. */ static int inotify_fd = -1; -/** - * Standard inotify events to listen to. - */ + +/* +| Standard inotify events to listen to. +*/ static const uint32_t standard_event_mask = - IN_ATTRIB | IN_CLOSE_WRITE | IN_CREATE | - IN_DELETE | IN_DELETE_SELF | IN_MOVED_FROM | - IN_MOVED_TO | IN_DONT_FOLLOW | IN_ONLYDIR; + IN_ATTRIB | + IN_CLOSE_WRITE | + IN_CREATE | + IN_DELETE | + IN_DELETE_SELF | + IN_MOVED_FROM | + IN_MOVED_TO | + IN_DONT_FOLLOW | + IN_ONLYDIR; -/** - * Adds an inotify watch - * - * @param dir (Lua stack) path to directory - * @param inotifyMode (Lua stack) path to directory - * @return (Lua stack) numeric watch descriptor - */ +/* +| Adds an inotify watch +| +| param dir (Lua stack) path to directory +| param inotifyMode (Lua stack) which inotify event to react upon +| "CloseWrite", "CloseWrite or Modify" +| +| returns (Lua stack) numeric watch descriptor +*/ static int -l_addwatch(lua_State *L) +l_addwatch( lua_State *L ) { - const char *path = luaL_checkstring(L, 1); - const char *imode = luaL_checkstring(L, 2); + const char *path = luaL_checkstring( L, 1 ); + const char *imode = luaL_checkstring( L, 2 ); uint32_t mask = standard_event_mask; - if (*imode) { - if (!strcmp(imode, "Modify")) { - // act on modify instead of closeWrite - mask |= IN_MODIFY; + + // checks the desired inotify reaction mode + if (*imode) + { + if ( !strcmp( imode, "Modify" ) ) + { + // acts on modify instead of closeWrite + mask |= IN_MODIFY; mask &= ~IN_CLOSE_WRITE; - } else if (!strcmp(imode, "CloseWrite")) { - // default - } else if (!strcmp(imode, "CloseWrite or Modify")) { + } + else if ( !strcmp( imode, "CloseWrite" ) ) + { + // thats default + } + else if ( !strcmp( imode, "CloseWrite or Modify" ) ) + { // acts on modify and closeWrite mask |= IN_MODIFY; - } else if (!strcmp(imode, "CloseWrite after Modify")) { + } + else if ( ! strcmp( imode, "CloseWrite after Modify") ) + { // might be done in future - printlogf(L, "Error", "'CloseWrite after Modify' not implemented."); - exit(-1); // ERRNO - } else { - printlogf(L, "Error", "'%s' not a valid inotfiyMode.", imode); - exit(-1); // ERRNO + printlogf( + L, "Error", + "'CloseWrite after Modify' not implemented." + ); + exit(-1); + } + else + { + printlogf( + L, "Error", + "'%s' not a valid inotfiyMode.", + imode + ); + exit(-1); } } + // kernel call to create the inotify watch + int wd = inotify_add_watch( inotify_fd, path, mask ); - int wd = inotify_add_watch(inotify_fd, path, mask); - if (wd < 0) { - if (errno == ENOSPC) { - printlogf(L, "Error", "Terminating since out of inotify watches."); - printlogf(L, "Error", "Consider increasing /proc/sys/fs/inotify/max_user_watches"); + if (wd < 0) + { + if (errno == ENOSPC) + { + printlogf( + L, "Error", + "%s\n%s", + "Terminating since out of inotify watches.", + "Consider increasing /proc/sys/fs/inotify/max_user_watches" + ); exit(-1); // ERRNO. } - printlogf(L, "Inotify", "addwatch(%s)->%d; err=%d:%s", path, wd, errno, strerror(errno)); - } else { - printlogf(L, "Inotify", "addwatch(%s)->%d", path, wd); + + printlogf( + L, "Inotify", + "addwatch( %s )-> % d; err= %d : %s", + path, wd, errno, strerror( errno ) + ); } - lua_pushinteger(L, wd); + else + { + printlogf(L, "Inotify", "addwatch( %s )-> %d ", path, wd ); + } + lua_pushinteger( L, wd ); + return 1; } -/** - * Removes an inotify watch + +/* + * Removes an inotify watch. * - * @param dir (Lua stack) numeric watch descriptor - * @return nil + * param dir (Lua stack) numeric watch descriptor + * + * return nil */ static int l_rmwatch(lua_State *L) @@ -126,147 +173,214 @@ l_rmwatch(lua_State *L) return 0; } -/** - * Cores inotify functions. - */ + +/* +| Lsyncd's core's inotify functions. +*/ static const luaL_Reg linotfylib[] = { - {"addwatch", l_addwatch }, - {"rmwatch", l_rmwatch }, - {NULL, NULL} + { "addwatch", l_addwatch }, + { "rmwatch", l_rmwatch }, + { NULL, NULL} }; -/** - * Buffer for MOVE_FROM events. - * Lsyncd buffers MOVE_FROM events to check if - * they are followed by MOVE_TO events with identical cookie - * then they are condensed into one move event to be sent to the - * runner - */ + +/* +| Buffer for MOVE_FROM events. +| Lsyncd buffers MOVE_FROM events to check if +| they are followed by MOVE_TO events with identical cookie +| then they are condensed into one move event to be sent to the +| runner +*/ static struct inotify_event * move_event_buf = NULL; -/** - * Memory allocated for move_event_buf - */ + +/* +| Memory allocated for move_event_buf +*/ static size_t move_event_buf_size = 0; -/** - * true if the buffer is used. - */ + +/* +| True if the buffer is used. +*/ static bool move_event = false; -/** - * Handles an inotify event. - */ + +/* +| Handles an inotify event. +*/ static void -handle_event(lua_State *L, - struct inotify_event *event) +handle_event( + lua_State *L, + struct inotify_event *event +) { const char *event_type = NULL; // used to execute two events in case of unmatched MOVE_FROM buffer struct inotify_event *after_buf = NULL; - if (event && (IN_Q_OVERFLOW & event->mask)) { - /* and overflow happened, tells the runner */ - load_runner_func(L, "overflow"); - if (lua_pcall(L, 0, 0, -2)) { - exit(-1); // ERRNO + if( event && ( IN_Q_OVERFLOW & event->mask ) ) + { + // and overflow happened, tells the runner + load_runner_func( L, "overflow" ); + + if( lua_pcall( L, 0, 0, -2 ) ) + { + exit( -1 ); } - lua_pop(L, 1); + lua_pop( L, 1 ); hup = 1; return; } + // cancel on ignored or resetting - if (event && (IN_IGNORED & event->mask)) { + if( event && ( IN_IGNORED & event->mask ) ) + { return; } - if (event && event->len == 0) { + + if( event && event->len == 0 ) + { // sometimes inotify sends such strange events, // (e.g. when touching a dir return; } - if (event == NULL) { + if( event == NULL ) + { // a buffered MOVE_FROM is not followed by anything, // thus it is unary event = move_event_buf; event_type = "Delete"; move_event = false; - } else if (move_event && - ( !(IN_MOVED_TO & event->mask) || - event->cookie != move_event_buf->cookie) ) { + } + else if( + move_event && + ( + !( IN_MOVED_TO & event->mask ) || + event->cookie != move_event_buf->cookie + ) + ) + { // there is a MOVE_FROM event in the buffer and this is not the match // continue in this function iteration to handle the buffer instead */ - logstring("Inotify", "icore, changing unary MOVE_FROM into DELETE") + logstring( + "Inotify", + "icore, changing unary MOVE_FROM into DELETE" + ) after_buf = event; event = move_event_buf; event_type = "Delete"; move_event = false; - } else if ( move_event && - (IN_MOVED_TO & event->mask) && - event->cookie == move_event_buf->cookie ) { + } + else if( + move_event && + ( + IN_MOVED_TO & event->mask ) && + event->cookie == move_event_buf->cookie + ) + { // this is indeed a matched move */ event_type = "Move"; 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 MOVED_TO of this was an unary move out of the watched // tree. - size_t el = sizeof(struct inotify_event) + event->len; - if (move_event_buf_size < el) { + size_t el = sizeof( struct inotify_event ) + event->len; + + if( move_event_buf_size < el ) + { move_event_buf_size = el; - move_event_buf = s_realloc(move_event_buf, el); + move_event_buf = s_realloc( move_event_buf, el ); } - memcpy(move_event_buf, event, el); + memcpy( move_event_buf, event, el ); move_event = true; return; - } else if (IN_MOVED_TO & event->mask) { + + } + else if( IN_MOVED_TO & event->mask ) + { // must be an unary move-to event_type = CREATE; - } else if (IN_ATTRIB & event->mask) { + } + else if( IN_ATTRIB & event->mask ) + { // just attrib change event_type = ATTRIB; - } else if ((IN_CLOSE_WRITE | IN_MODIFY) & event->mask) { + } + else if( ( IN_CLOSE_WRITE | IN_MODIFY) & event->mask ) + { // modify, or closed after written something // the event type received depends settings.inotifyMode event_type = MODIFY; - } else if (IN_CREATE & event->mask) { + } + else if( IN_CREATE & event->mask ) + { // a new file event_type = CREATE; - } else if (IN_DELETE & event->mask) { + } + else if( IN_DELETE & event->mask ) + { // rm'ed event_type = DELETE; - } else { - logstring("Inotify", "icore, skipped some inotify event."); + } + else + { + logstring( + "Inotify", + "skipped some inotify event." + ); return; } - // and hands over to runner - load_runner_func(L, "inotifyEvent"); - if (!event_type) { - logstring("Error", "Internal: unknown event in handle_event()"); - exit(-1); // ERRNO + // hands the event over to the runner + load_runner_func( L, "inotifyEvent" ); + + if( !event_type ) + { + logstring( + "Error", + "internal failure: unknown event in handle_event()" + ); + + exit( -1 ); } - lua_pushstring(L, event_type); - if (event_type != MOVE) { - lua_pushnumber(L, event->wd); - } else { - lua_pushnumber(L, move_event_buf->wd); + + lua_pushstring( L, event_type ); + if( event_type != MOVE ) + { + lua_pushnumber( L, event->wd ); } - lua_pushboolean(L, (event->mask & IN_ISDIR) != 0); - l_now(L); - if (event_type == MOVE) { - lua_pushstring(L, move_event_buf->name); - lua_pushnumber(L, event->wd); - lua_pushstring(L, event->name); - } else { - lua_pushstring(L, event->name); - lua_pushnil(L); - lua_pushnil(L); + else + { + lua_pushnumber( L, move_event_buf->wd ); } - if (lua_pcall(L, 7, 0, -9)) { - exit(-1); // ERRNO + lua_pushboolean( L, ( event->mask & IN_ISDIR ) != 0 ); + + l_now( L ); + + if( event_type == MOVE ) + { + lua_pushstring( L, move_event_buf->name ); + lua_pushnumber( L, event->wd ); + lua_pushstring( L, event->name ); } - lua_pop(L, 1); + else + { + lua_pushstring( L, event->name ); + lua_pushnil( L ); + lua_pushnil( L ); + } + + if( lua_pcall( L, 7, 0, -9 ) ) + { + exit( -1 ); + } + + lua_pop( L, 1 ); // if there is a buffered event, executes it if (after_buf) { @@ -275,31 +389,44 @@ handle_event(lua_State *L, } } -/** - * buffer to read inotify events into - */ + +/* +| buffer to read inotify events into +*/ static size_t readbuf_size = 2048; static char * readbuf = NULL; -/** - * Called by function pointer from when the inotify file descriptor - * became ready. Reads it contents and forward all received events - * to the runner. - */ + +/* +| Called when the inotify file descriptor became ready. +| Reads it contents and forwards all received events +| to the runner. +*/ static void -inotify_ready(lua_State *L, struct observance *obs) +inotify_ready( + lua_State *L, + struct observance *obs +) { - if (obs->fd != inotify_fd) { - logstring("Error", "Internal, inotify_fd != ob->fd"); - exit(-1); // ERRNO + // sanity check + if( obs->fd != inotify_fd ) + { + logstring( + "Error", + "internal failure, inotify_fd != ob->fd" + ); + exit( -1 ); } - while(true) { + + while( true ) + { ptrdiff_t len; int err; do { - len = read (inotify_fd, readbuf, readbuf_size); + len = read( inotify_fd, readbuf, readbuf_size ); err = errno; - if (len < 0 && err == EINVAL) { + if( len < 0 && err == EINVAL ) + { // kernel > 2.6.21 indicates that way that way that // the buffer was too small to fit a filename. // double its size and try again. When using a lower @@ -309,90 +436,138 @@ inotify_ready(lua_State *L, struct observance *obs) readbuf_size *= 2; readbuf = s_realloc(readbuf, readbuf_size); } - } while(len < 0 && err == EINVAL); - if (len == 0) { - // nothing more inotify + } while( len < 0 && err == EINVAL ); + + if( len == 0 ) + { + // no more inotify events break; } - if (len < 0) { + + if (len < 0) + { if (err == EAGAIN) { // nothing more inotify break; - } else { - printlogf(L, "Error", "Read fail on inotify"); - exit(-1); // ERRNO + } + else + { + printlogf( + L, "Error", + "Read fail on inotify" + ); + exit( -1 ); } } + { int i = 0; - while (i < len && !hup && !term) { + while( i < len && !hup && !term ) + { struct inotify_event *event = - (struct inotify_event *) &readbuf[i]; - handle_event(L, event); - i += sizeof(struct inotify_event) + event->len; + ( struct inotify_event * ) + (readbuf + i); + + handle_event( L, event ); + + i += sizeof( struct inotify_event ) + event->len; } } - if (!move_event) { + + if( !move_event ) + { // give it a pause if not endangering splitting a move break; } } // checks if there is an unary MOVE_FROM left in the buffer - if (move_event) { - logstring("Inotify", "icore, handling unary move from."); - handle_event(L, NULL); + if( move_event ) + { + logstring( + "Inotify", + "handling unary move from." + ); + handle_event( L, NULL ); } } -/** - * registers inotify functions. - */ + +/* +| Registers the inotify functions. +*/ extern void -register_inotify(lua_State *L) +register_inotify( lua_State *L ) { - luaL_register(L, LSYNCD_INOTIFYLIBNAME, linotfylib); + luaL_register( L, LSYNCD_INOTIFYLIBNAME, linotfylib ); } -/** - * closes inotify - */ + +/* +| Cleans up the inotify handling. +*/ static void -inotify_tidy(struct observance *obs) +inotify_tidy( struct observance *obs ) { - if (obs->fd != inotify_fd) { - logstring("Error", "Internal, inotify_fd != ob->fd"); - exit(-1); // ERRNO + if( obs->fd != inotify_fd ) + { + logstring( + "Error", + "internal failure: inotify_fd != ob->fd" + ); + exit( -1 ); } - close(inotify_fd); - free(readbuf); + + close( inotify_fd ); + free( readbuf ); readbuf = NULL; } -/** - * opens and initalizes inotify. - */ +/* +| Initalizes inotify handling +*/ extern void -open_inotify(lua_State *L) +open_inotify( lua_State *L ) { - if (readbuf) { - logstring("Error", - "internal fail, inotify readbuf!=NULL in open_inotify()") - exit(-1); // ERRNO + if( readbuf ) + { + logstring( + "Error", + "internal failure, inotify readbuf != NULL in open_inotify()" + ) + exit(-1); } - readbuf = s_malloc(readbuf_size); - inotify_fd = inotify_init(); - if (inotify_fd < 0) { - printlogf(L, "Error", - "Cannot access inotify monitor! (%d:%s)", - errno, strerror(errno)); - exit(-1); // ERRNO + readbuf = s_malloc( readbuf_size ); + + inotify_fd = inotify_init( ); + + if( inotify_fd < 0 ) + { + printlogf( + L, + "Error", + "Cannot access inotify monitor! ( %d : %s )", + errno, strerror(errno) + ); + exit( -1 ); } - printlogf(L, "Inotify", "inotify fd = %d", inotify_fd); - close_exec_fd(inotify_fd); - non_block_fd(inotify_fd); - observe_fd(inotify_fd, inotify_ready, NULL, inotify_tidy, NULL); + printlogf( + L, "Inotify", + "inotify fd = %d", + inotify_fd + ); + + close_exec_fd( inotify_fd ); + non_block_fd( inotify_fd ); + + observe_fd( + inotify_fd, + inotify_ready, + NULL, + inotify_tidy, + NULL + ); } diff --git a/lsyncd.lua b/lsyncd.lua index 1f566a2..aa6e803 100644 --- a/lsyncd.lua +++ b/lsyncd.lua @@ -30,7 +30,7 @@ if lsyncd_version then lsyncd.terminate( -1 ) end -lsyncd_version = '2.0.7' +lsyncd_version = '2.1.0-beta' -- -- Hides the core interface from user scripts. @@ -1630,12 +1630,12 @@ local Sync = ( function( ) log( 'Function', - 'delay(', + 'delay( ', self.config.name, ', ', etype, ', ', path, ', ', path2, - ')' + ' )' ) -- TODO @@ -1777,14 +1777,24 @@ local Sync = ( function( ) ) if nd.etype == 'Init' or nd.etype == 'Blanket' then - -- always stack blanket events on the last event - log('Delay', 'Stacking ',nd.etype,' event.') + + -- always stack init or blanket events on the last event + log( + 'Delay', + 'Stacking ', + nd.etype, + ' event.' + ) + if self.delays.size > 0 then stack( self.delays[ self.delays.last ], nd ) end + nd.dpos = Queue.push( self.delays, nd ) recurse( ) + return + end -- detects blocks and combos by working from back until @@ -1896,11 +1906,10 @@ local Sync = ( function( ) log( 'Function', - 'invokeActions("', - self.config.name, - '",', - timestamp, - ')' + 'invokeActions( "', + self.config.name, '", ', + timestamp, + ' )' ) if self.processes:size( ) >= self.config.maxProcesses then @@ -1909,6 +1918,7 @@ local Sync = ( function( ) end for _, d in Queue.qpairs( self.delays ) do + -- if reached the global limit return if settings.maxProcesses and processCount >= settings.maxProcesses @@ -1927,12 +1937,14 @@ local Sync = ( function( ) end if d.status == 'wait' then + -- found a waiting delay if d.etype ~= 'Init' then self.config.action( self.inlet ) else self.config.init( InletFactory.d2e( d ) ) end + if self.processes:size( ) >= self.config.maxProcesses then -- no further processes return @@ -1980,8 +1992,11 @@ local Sync = ( function( ) -- Used as startup marker to call init asap. -- local function addInitDelay( self ) + local newd = Delay.new( 'Init', self, true, '' ) + newd.dpos = Queue.push( self.delays, newd ) + return newd end @@ -2113,7 +2128,7 @@ local Syncs = ( function( ) local round = 1 -- - -- The cycle() sheduler goes into the next round of roundrobin. + -- The cycle( ) sheduler goes into the next round of roundrobin. -- local function nextRound( ) @@ -2141,31 +2156,85 @@ local Syncs = ( function( ) end -- - -- Inheritly copies all non integer keys from - -- table copy source ( cs ) to - -- table copy destination ( cd ). + -- Helper function for inherit + -- defined below -- - -- All entries with integer keys are treated as new sources to copy + local inheritKV + + -- + -- Recurvely inherits a source table to a destionation table + -- copying all keys from source. + -- + -- table copy source ( cs ) + -- table copy destination ( cd ) + -- + -- All entries with integer keys are inherited as additional + -- sources for non-verbatim tables -- local function inherit( cd, cs ) - -- first copies from source all - -- non-defined non-integer keyed values + -- + -- First copies all entries with non-integer keys + -- tables are merged, already present keys are not + -- overwritten + -- + -- For verbatim tables integer keys are treated like + -- non integer keys + -- for k, v in pairs( cs ) do - if type( k ) ~= 'number' and cd[ k ] == nil then - cd[ k ] = v + if type( k ) ~= 'number' or cs._verbatim == true then + inheritKV( cd, k, v ) end end - -- first recurses into all integer keyed tables - for i, v in ipairs( cs ) do - if type( v ) == 'table' then - inherit( cd, v ) + -- + -- recursevely inherits all integer keyed tables + -- ( for non-verbatim tables ) + -- + if cs._verbatim ~= true then + + local n = nil + for k, v in ipairs( cs ) do + n = k + if type( v ) == 'table' then + inherit( cd, v ) + else + cd[ #cd + 1 ] = v + end end + + end + end + + -- + -- Helper to inherit. Inherits one key. + -- + inheritKV = function( cd, k, v ) + + local dtype = type( cd [ k ] ) + + if type( v ) == 'table' then + + if dtype == 'nil' then + + cd[ k ] = { } + inherit( cd[ k ], v ) + + elseif dtype == 'table' and v._merge ~= false then + + inherit( cd[ k ], v ) + + end + + elseif dtype == 'nil' then + + cd[ k ] = v + end end + -- -- Adds a new sync (directory-tree to observe). -- @@ -2175,7 +2244,7 @@ local Syncs = ( function( ) -- from integer keyed tables local uconfig = config config = { } - inherit( config, uconfig ) + inherit( config, uconfig, false ) -- Lets settings or commandline override delay values. if settings then @@ -2227,7 +2296,7 @@ local Syncs = ( function( ) 'Error', info.short_src, ':', info.currentline, - ': no actions specified, use e.g. "config = default.rsync".' + ': no actions specified.' ) terminate( -1 ) @@ -2391,14 +2460,21 @@ local Inotify = ( function( ) pathwds[ path ] = nil end - ----- + + -- -- Adds watches for a directory (optionally) including all subdirectories. -- -- @param path absolute path of directory to observe -- @param recurse true if recursing into subdirs -- local function addWatch(path) - log('Function','Inotify.addWatch(',path,')') + + log( + 'Function', + 'Inotify.addWatch( ', + path, + ' )' + ) if not Syncs.concerns(path) then log('Inotify', 'not concerning "',path,'"') @@ -3075,9 +3151,9 @@ local StatusFile = ( function( ) log( 'Function', - 'write(', - timestamp, - ')' + 'write( ', + timestamp, + ' )' ) -- takes care not write too often @@ -3313,20 +3389,25 @@ function runner.cycle( ) if lsyncdStatus == 'fade' then + if processCount > 0 then + log( 'Normal', 'waiting for ', processCount, ' more child processes.' ) + return true else + return false end end if lsyncdStatus ~= 'run' then + error( 'runner.cycle() called while not running!' ) end @@ -3340,17 +3421,19 @@ function runner.cycle( processCount < settings.maxProcesses then local start = Syncs.getRound( ) + local ir = start repeat + local s = Syncs.get( ir ) s:invokeActions( timestamp ) ir = ir + 1 if ir > Syncs.size( ) then + ir = 1 end - until ir == start Syncs.nextRound( ) @@ -3652,13 +3735,19 @@ end -- function runner.initialize( firstTime ) + -- -- creates settings if user didnt + -- settings = settings or {} + -- -- From this point on, no globals may be created anymore - lockGlobals() + -- + lockGlobals( ) - -- copies simple settings with numeric keys to 'key=true' settings. + -- + -- copies simple settings with numeric keys to 'key = true' settings. + -- for k, v in ipairs( settings ) do if settings[ v ] then @@ -3714,23 +3803,23 @@ function runner.initialize( firstTime ) end if settings.nodaemon then - lsyncd.configure('nodaemon') + lsyncd.configure( 'nodaemon' ) end if settings.logfile then - lsyncd.configure('logfile', settings.logfile) + lsyncd.configure( 'logfile', settings.logfile ) end if settings.logident then - lsyncd.configure('logident', settings.logident) + lsyncd.configure( 'logident', settings.logident ) end if settings.logfacility then - lsyncd.configure('logfacility', settings.logfacility) + lsyncd.configure( 'logfacility', settings.logfacility ) end if settings.pidfile then - lsyncd.configure('pidfile', settings.pidfile) + lsyncd.configure( 'pidfile', settings.pidfile ) end -- @@ -3753,6 +3842,7 @@ function runner.initialize( firstTime ) -- from now on use logging as configured instead of stdout/err. lsyncdStatus = 'run'; + lsyncd.configure( 'running' ); local ufuncs = { @@ -3766,34 +3856,49 @@ function runner.initialize( firstTime ) -- translates layer 3 scripts for _, s in Syncs.iwalk() do + -- checks if any user functions is a layer 3 string. local config = s.config + for _, fn in ipairs(ufuncs) do + if type(config[fn]) == 'string' then + local ft = functionWriter.translate(config[fn]) config[fn] = assert(loadstring('return '..ft))() + end + end end -- runs through the Syncs created by users for _, s in Syncs.iwalk( ) do + if s.config.monitor == 'inotify' then + Inotify.addSync( s, s.source ) + elseif s.config.monitor == 'fsevents' then + Fsevents.addSync( s, s.source ) + else + error( 'sync ' .. s.config.name .. ' has no known event monitor interface.' ) + end -- if the sync has an init function, the init delay -- is stacked which causes the init function to be called. if s.config.init then + s:addInitDelay( ) + end end diff --git a/tests/exclude-rsync.lua b/tests/exclude-rsync.lua index 6fcdd27..4bf6bb2 100755 --- a/tests/exclude-rsync.lua +++ b/tests/exclude-rsync.lua @@ -73,7 +73,7 @@ end cwriteln("testing startup excludes"); writefiles(); cwriteln("starting Lsyncd"); -local pid = spawn("./lsyncd", cfgfile); +local pid = spawn("./lsyncd", cfgfile, '-log', 'all'); cwriteln("waiting for Lsyncd to start"); posix.sleep(3) cwriteln("testing excludes after startup"); From 6a862d6b8f70d1c96a876e3ab77a078514bf4ee3 Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Fri, 5 Oct 2012 21:48:06 +0200 Subject: [PATCH 13/28] introducing checkgauge, code beautifications --- default-rsync.lua | 185 +++++++++++--- default-rsyncssh.lua | 558 +++++++++++++++++++++---------------------- default.lua | 499 ++++++++++++++++++++++---------------- 3 files changed, 722 insertions(+), 520 deletions(-) diff --git a/default-rsync.lua b/default-rsync.lua index 73a3c4e..7234641 100644 --- a/default-rsync.lua +++ b/default-rsync.lua @@ -28,6 +28,64 @@ end default.rsync = { } local rsync = default.rsync +-- uses default collect + +-- +-- used to ensure there aren't typos in the keys +-- +rsync.checkgauge = { + + default.checkgauge, + + -- unsets default user action handlers + onCreate = false, + onModify = false, + onDelete = false, + onStartup = false, + onMove = false, + + delete = true, + exclude = true, + source = true, + target = true, + + rsync = { + -- rsync binary + binary = true, + + -- rsync shortflags + verbose = true, + quiet = true, + checksum = true, + update = true, + links = true, + copy_links = true, + hard_links = true, + perms = true, + executability = true, + acls = true, + xattrs = true, + owner = true, + group = true, + times = true, + sparse = true, + dry_run = true, + whole_file = true, + one_file_system = true, + prune_empty_dirs = true, + ignore_times = true, + compress = true, + cvs_exclude = true, + protect_args = true, + ipv4 = true, + ipv6 = true, + + -- further rsync options + rsh = true, + rsync_path = true, + }, +} + -- -- Spawns rsync for a list of events @@ -163,10 +221,19 @@ end -- rsync.init = function(event) - local config = event.config - local inlet = event.inlet + local config = event.config + local inlet = event.inlet local excludes = inlet.getExcludes( ) - local delete = nil + local delete = nil + local target = config.target + + if not target then + if not config.host then + error('Internal fail, Neither target nor host is configured') + end + + target = config.host .. ':' .. config.targetdir + end if config.delete then delete = { '--delete', '--ignore-errors' } @@ -179,7 +246,7 @@ rsync.init = function(event) 'recursive startup rsync: ', config.source, ' -> ', - config.target + target ) spawn( @@ -189,7 +256,7 @@ rsync.init = function(event) config.rsync._computed, '-r', config.source, - config.target + target ) else @@ -202,7 +269,7 @@ rsync.init = function(event) 'recursive startup rsync: ', config.source, ' -> ', - config.target, + target, ' excluding\n', exS ) @@ -216,7 +283,7 @@ rsync.init = function(event) config.rsync._computed, '-r', config.source, - config.target + target ) end end @@ -225,19 +292,30 @@ end -- -- Prepares and checks a syncs configuration on startup. -- -rsync.prepare = function( config ) +rsync.prepare = function( + config, -- the configuration + level, -- additional error level for inherited use ( by rsyncssh ) + skipTarget -- used by rsyncssh, do not check for target +) - if not config.target then + level = level or 4 + + -- + -- First let default.prepare test the checkgauge + -- + default.prepare( config, level + 6 ) + + if not skipTarget and not config.target then error( 'default.rsync needs "target" configured', - 4 + level ) end if config.rsyncOps then error( '"rsyncOps" is outdated please use the new rsync = { ... } syntax.', - 4 + level ) end @@ -246,7 +324,7 @@ rsync.prepare = function( config ) '"rsyncOpts" is outdated in favor of the new rsync = { ... } syntax\n"' + 'for which you provided the _extra attribute as well.\n"' + 'Please remove rsyncOpts from your config.', - 4 + level ) end @@ -265,7 +343,7 @@ rsync.prepare = function( config ) '"rsyncBinary is outdated in favor of the new rsync = { ... } syntax\n"'+ 'for which you provided the binary attribute as well.\n"' + "Please remove rsyncBinary from your config.'", - 4 + level ) end @@ -283,75 +361,108 @@ rsync.prepare = function( config ) if config.rsync._computed then error( 'please do not use the internal rsync._computed parameter', - 4 + level ) end -- computes the rsync arguments into one list local rsync = config.rsync; + rsync._computed = { true } local computed = rsync._computed + local computedN = 1 + + local shortFlags = { + verbose = 'v', + quiet = 'q', + checksum = 'c', + update = 'u', + links = 'l', + copy_links = 'L', + hard_links = 'H', + perms = 'p', + executability = 'E', + acls = 'A', + xattrs = 'X', + owner = 'o', + group = 'g', + times = 't', + sparse = 'S', + dry_run = 'n', + whole_file = 'W', + one_file_system = 'x', + prune_empty_dirs = 'm', + ignore_times = 'I', + compress = 'z', + cvs_exclude = 'C', + protect_args = 's', + ipv4 = '4', + ipv6 = '6' + } + local shorts = { '-' } + local shortsN = 2 if config.rsync._extra then for k, v in ipairs( config.rsync._extra ) do - computed[ k + 1 ] = v + computed[ computedN ] = v + computedN = computedN + 1 end end - if rsync.links then - shorts[ #shorts + 1 ] = 'l' + for k, flag in pairs( shortFlags ) do + if config.rsync[k] then + shorts[ shortsN ] = flag + shortsN = shortsN + 1 + end end - if rsync.times then - shorts[ #shorts + 1 ] = 't' + if config.rsync.rsh then + computed[ computedN ] = '--rsh=' + config.rsync.rsh + computedN = computedN + 1 end - if rsync.protectArgs then - shorts[ #shorts + 1 ] = 's' + if config.rsync.rsync_path then + computed[ computedN ] = '--rsync-path=' + config.rsync.rsync_path + computedN = computedN + 1 end - if #shorts ~= 1 then + if shortsN ~= 2 then computed[ 1 ] = table.concat( shorts, '' ) else computed[ 1 ] = { } end -- appends a / to target if not present - if string.sub(config.target, -1) ~= '/' then + if not skipTarget and string.sub(config.target, -1) ~= '/' then config.target = config.target..'/' end + end --- --- rsync uses default collect --- - - -- -- By default do deletes. -- rsync.delete = true +-- +-- Rsyncd exitcodes +-- +rsync.exitcodes = default.rsyncExitCodes -- -- Calls rsync with this default options -- rsync.rsync = { -- The rsync binary to be called. - binary = '/usr/bin/rsync', - links = true, - times = true, - protectArgs = true + binary = '/usr/bin/rsync', + links = true, + times = true, + protect_args = true } --- --- Exit codes for rsync. --- -rsync.exitcodes = default.rsyncExitCodes - -- -- Default delay -- diff --git a/default-rsyncssh.lua b/default-rsyncssh.lua index 625d08e..9ea744c 100644 --- a/default-rsyncssh.lua +++ b/default-rsyncssh.lua @@ -16,344 +16,340 @@ -- if not default then - error('default not loaded'); + error( 'default not loaded' ); +end + +if not default.rsync then + error( 'default.rsync not loaded' ); end if default.rsyncssh then - error('default-rsyncssh already loaded'); + error( 'default-rsyncssh already loaded' ); end -default.rsyncssh = { +-- +-- rsyncssh extends default.rsync +-- +local rsyncssh = { default.rsync } +default.rsyncssh = rsyncssh - -- - -- Spawns rsync for a list of events - -- - action = function(inlet) +-- +-- used to ensure there aren't typos in the keys +-- +rsyncssh.checkgauge = { - local event, event2 = inlet.getEvent() - local config = inlet.getConfig() + -- unsets the inherited value of from default.rsync + target = false, + onMove = true, - -- makes move local on host - -- if fails deletes the source... - if event.etype == 'Move' then - log('Normal', 'Moving ',event.path,' -> ',event2.path) - spawn(event, '/usr/bin/ssh', - config.host, - 'mv', - '\"' .. config.targetdir .. event.path .. '\"', - '\"' .. config.targetdir .. event2.path .. '\"', - '||', 'rm', '-rf', - '\"' .. config.targetdir .. event.path .. '\"') + -- rsyncssh users host and targetdir + host = true, + targetdir = true, + + -- ssh settings + ssh = { + binary = true, + port = true, + _extra = true + }, + + -- xargs settings + xargs = { + binary = true, + delimiter = true, + _extra = true + } +} + +-- +-- Spawns rsync for a list of events +-- +rsyncssh.action = function( inlet ) + + local event, event2 = inlet.getEvent() + local config = inlet.getConfig() + + -- makes move local on target host + -- if the move fails, it deletes the source + if event.etype == 'Move' then + log('Normal', 'Moving ',event.path,' -> ',event2.path) + + spawn( + event, + config.ssh.binary, +-- config.ssh._computed, TODO XXX + config.host, + 'mv', + '\"' .. config.targetdir .. event.path .. '\"', + '\"' .. config.targetdir .. event2.path .. '\"', + '||', 'rm', '-rf', + '\"' .. config.targetdir .. event.path .. '\"') + return + end + + -- uses ssh to delete files on remote host + -- instead of constructing rsync filters + + if event.etype == 'Delete' then + if not config.delete then + inlet.discardEvent(event) return end - -- uses ssh to delete files on remote host - -- instead of constructing rsync filters - - if event.etype == 'Delete' then - - if not config.delete then - inlet.discardEvent(event) - return - end - - local elist = inlet.getEvents( - function(e) - return e.etype == 'Delete' - end - ) - - local paths = elist.getPaths( - function(etype, path1, path2) - if path2 then - return config.targetdir..path1, config.targetdir..path2 - else - return config.targetdir..path1 - end - end - ) - - for _, v in pairs(paths) do - if string.match(v, '^%s*/+%s*$') then - log('Error', 'refusing to `rm -rf /` the target!') - terminate(-1) -- ERRNO - end - end - - log('Normal', 'Deleting list\n', table.concat(paths, '\n')) - - local params = {} - - if config.port then - params[#params + 1] = 'p' - params[#params + 1] = config.port - end - - spawn( - elist, - config.ssh.binary, - '<', table.concat(paths, config.xargs.delimiter), - params, - config.ssh._extra, - config.host, - config.xargs.binary, - config.xargs._extra - ) - - return - end - - -- for everything else spawn a rsync + -- gets all other deletes ready to be + -- executed local elist = inlet.getEvents( - function(e) - -- TODO use a table - return e.etype ~= 'Move' and - e.etype ~= 'Delete' and - e.etype ~= 'Init' and - e.etype ~= 'Blanket' + function( e ) + return e.etype == 'Delete' end ) - local paths = elist.getPaths() + -- returns the paths of the delete list + local paths = elist.getPaths( + function( etype, path1, path2 ) + if path2 then + return config.targetdir..path1, config.targetdir..path2 + else + return config.targetdir..path1 + end + end + ) - -- removes trailing slashes from dirs. - for k, v in ipairs(paths) do - if string.byte(v, -1) == 47 then - paths[k] = string.sub(v, 1, -2) + -- ensures none of the paths is '/' + for _, v in pairs( paths ) do + if string.match(v, '^%s*/+%s*$') then + log('Error', 'refusing to `rm -rf /` the target!') + terminate(-1) -- ERRNO end end - local sPaths = table.concat(paths, '\n') - local zPaths = table.concat(paths, '\000') - log('Normal', 'Rsyncing list\n', sPaths) + log( + 'Normal', + 'Deleting list\n', + table.concat( paths, '\n' ) + ) + + local params = { } spawn( elist, - config.rsyncBinary, - '<', zPaths, - config.rsyncOpts, - '--from0', - '--files-from=-', - config.source, - config.host .. ':' .. config.targetdir + config.ssh.binary, + '<', table.concat(paths, config.xargs.delimiter), + params, + -- config.ssh._computed, TODO XXX + config.host, + config.xargs.binary, + config.xargs._extra ) - end, - ----- - -- Called when collecting a finished child process + return + end + -- - collect = function(agent, exitcode) + -- for everything else a rsync is spawned + -- + local elist = inlet.getEvents( + function(e) + -- TODO use a table + return e.etype ~= 'Move' and + e.etype ~= 'Delete' and + e.etype ~= 'Init' and + e.etype ~= 'Blanket' + end + ) - local config = agent.config - - if not agent.isList and agent.etype == 'Init' then - - local rc = config.rsyncExitCodes[exitcode] - - if rc == 'ok' then - log('Normal', 'Startup of "',agent.source,'" finished: ', exitcode) - elseif rc == 'again' then - if settings.insist then - log('Normal', 'Retrying startup of "',agent.source,'": ', exitcode) - else - log('Error', 'Temporary or permanent failure on startup of "', - agent.source, '". Terminating since "insist" is not set.'); - terminate(-1) -- ERRNO - end - elseif rc == 'die' then - log('Error', 'Failure on startup of "',agent.source,'": ', exitcode) - else - log('Error', 'Unknown exitcode on startup of "', agent.source,': "',exitcode) - rc = 'die' - end - - return rc + local paths = elist.getPaths( ) + -- + -- removes trailing slashes from dirs. + -- + for k, v in ipairs( paths ) do + if string.byte(v, -1) == 47 then + paths[k] = string.sub(v, 1, -2) end - if agent.isList then + end - local rc = config.rsyncExitCodes[exitcode] + local sPaths = table.concat(paths, '\n') + local zPaths = table.concat(paths, '\000') - if rc == 'ok' then - log('Normal', 'Finished (list): ',exitcode) - elseif rc == 'again' then - log('Normal', 'Retrying (list): ',exitcode) - elseif rc == 'die' then - log('Error', 'Failure (list): ', exitcode) + log('Normal', 'Rsyncing list\n', sPaths) + + spawn( + elist, + config.rsync.binary, + '<', zPaths, + config.rsync._computed, + '--from0', + '--files-from=-', + config.source, + config.host .. ':' .. config.targetdir + ) +end + +----- +-- Called when collecting a finished child process +-- +rsyncssh.collect = function( agent, exitcode ) + + local config = agent.config + + if not agent.isList and agent.etype == 'Init' then + local rc = config.rsyncExitCodes[exitcode] + + if rc == 'ok' then + log('Normal', 'Startup of "',agent.source,'" finished: ', exitcode) + elseif rc == 'again' then + if settings.insist then + log('Normal', 'Retrying startup of "',agent.source,'": ', exitcode) else - log('Error', 'Unknown exitcode (list): ',exitcode) - rc = 'die' + log('Error', 'Temporary or permanent failure on startup of "', + agent.source, '". Terminating since "insist" is not set.'); + terminate(-1) -- ERRNO end - return rc - + elseif rc == 'die' then + log('Error', 'Failure on startup of "',agent.source,'": ', exitcode) else - - local rc = config.sshExitCodes[exitcode] - - if rc == 'ok' then - log('Normal', 'Finished ',agent.etype,' ',agent.sourcePath,': ',exitcode) - elseif rc == 'again' then - log('Normal', 'Retrying ',agent.etype,' ',agent.sourcePath,': ',exitcode) - elseif rc == 'die' then - log('Normal', 'Failure ',agent.etype,' ',agent.sourcePath,': ',exitcode) - else - log('Error', 'Unknown exitcode ',agent.etype,' ',agent.sourcePath,': ',exitcode) - rc = 'die' - end - - return rc - + log('Error', 'Unknown exitcode on startup of "', agent.source,': "',exitcode) + rc = 'die' end - end, + return rc - -- - -- spawns the recursive startup sync - -- - init = function(event) + end - local config = event.config - local inlet = event.inlet - local excludes = inlet.getExcludes() - local target = config.host .. ':' .. config.targetdir - local delete = nil - - if config.delete then - delete = { '--delete', '--ignore-errors' }; - end - - if #excludes == 0 then - log('Normal', 'Recursive startup rsync: ',config.source,' -> ',target) - spawn( - event, config.rsyncBinary, - delete, - '-r', - config.rsyncOpts, - config.source, - target - ) + if agent.isList then + local rc = config.rsyncExitCodes[exitcode] + if rc == 'ok' then + log('Normal', 'Finished (list): ',exitcode) + elseif rc == 'again' then + log('Normal', 'Retrying (list): ',exitcode) + elseif rc == 'die' then + log('Error', 'Failure (list): ', exitcode) else - local exS = table.concat(excludes, '\n') - log('Normal', 'Recursive startup rsync: ',config.source, - ' -> ',target,' with excludes.') - spawn( - event, config.rsyncBinary, - '<', exS, - '--exclude-from=-', - delete, - '-r', - config.rsyncOpts, - config.source, - target - ) + log('Error', 'Unknown exitcode (list): ',exitcode) + rc = 'die' end - end, + return rc + else + local rc = config.sshExitCodes[exitcode] - -- - -- checks the configuration. - -- - prepare = function(config) - - if not config.host then - error('default.rsyncssh needs "host" configured', 4) + if rc == 'ok' then + log('Normal', 'Finished ',agent.etype,' ',agent.sourcePath,': ',exitcode) + elseif rc == 'again' then + log('Normal', 'Retrying ',agent.etype,' ',agent.sourcePath,': ',exitcode) + elseif rc == 'die' then + log('Normal', 'Failure ',agent.etype,' ',agent.sourcePath,': ',exitcode) + else + log('Error', 'Unknown exitcode ',agent.etype,' ',agent.sourcePath,': ',exitcode) + rc = 'die' end - if not config.targetdir then - error('default.rsyncssh needs "targetdir" configured', 4) - end + return rc + end - if config.rsyncOps then - error('did you mean rsyncOpts with "t"?', 4) - end +end - -- appends a slash to the targetdir if missing - if string.sub(config.targetdir, -1) ~= '/' then - config.targetdir = config.targetdir .. '/' - end - end, +-- +-- checks the configuration. +-- +rsyncssh.prepare = function( config ) - -- - -- the rsync binary called - -- - rsyncBinary = '/usr/bin/rsync', + default.rsync.prepare( config, 5, true ) - -- - -- calls rsync with this default short opts - -- - rsyncOpts = '-lts', + if not config.host then + error('default.rsyncssh needs "host" configured', 4) + end - -- - -- allow processes - -- - maxProcesses = 1, + if not config.targetdir then + error('default.rsyncssh needs "targetdir" configured', 4) + end - -- - -- The core should not split move events - -- - onMove = true, + if config.rsyncOps then + error('did you mean rsyncOpts with "t"?', 4) + end - -- - -- default delay - -- - delay = 15, + -- appends a slash to the targetdir if missing + if string.sub(config.targetdir, -1) ~= '/' then + config.targetdir = config.targetdir .. '/' + end + +end + +-- +-- allow processes +-- +rsyncssh.maxProcesses = 1 + +-- +-- The core should not split move events +-- +rsyncssh.onMove = true + +-- +-- default delay +-- +rsyncssh.delay = 15 - -- - -- by default do deletes - -- - delete = true, +-- +-- no default exit codes +-- +rsyncssh.exitcodes = false + +-- +-- rsync exit codes +-- +rsyncssh.rsyncExitCodes = default.rsyncExitCodes + +-- +-- ssh exit codes +-- +rsyncssh.sshExitCodes = default.sshExitCodes + +-- +-- xargs calls configuration +-- +-- xargs is used to delete multiple remote files, when ssh access is +-- available this is simpler than to build filters for rsync for this. +-- +rsyncssh.xargs = { -- - -- rsync exit codes - -- - rsyncExitCodes = default.rsyncExitCodes, + -- the binary called (on target host) + binary = '/usr/bin/xargs', -- - -- ssh exit codes - -- - sshExitCodes = default.sshExitCodes, + -- delimiter, uses null by default, you might want to override this for older + -- by for example '\n' + delimiter = '\000', -- - -- xargs calls configuration - -- - -- xargs is used to delete multiple remote files, when ssh access is - -- available this is simpler than to build filters for rsync for this. - -- - xargs = { - - -- - -- the binary called (on target host) - binary = '/usr/bin/xargs', - - -- - -- delimiter, uses null by default, you might want to override this for older - -- by for example '\n' - delimiter = '\000', - - -- - -- extra parameters - _extra = { '-0', 'rm -rf' } - }, - - -- - -- ssh calls configuration - -- - -- ssh is used to move and delete files on the target host - -- - ssh = { - -- - -- the binary called - binary = '/usr/bin/ssh', - - -- - -- if set connect to this port - port = nil, - - -- - -- extra parameters - _extra = { } - } - + -- extra parameters + _extra = { '-0', 'rm -rf' } } + +-- +-- ssh calls configuration +-- +-- ssh is used to move and delete files on the target host +-- +rsyncssh.ssh = { + + -- + -- the binary called + -- + binary = '/usr/bin/ssh', + + -- + -- if set connect to this port + -- + port = nil, + + -- + -- extra parameters + -- + _extra = { } +} + diff --git a/default.lua b/default.lua index 7ec7e05..a1f23d4 100644 --- a/default.lua +++ b/default.lua @@ -9,237 +9,332 @@ --============================================================================ if default then - error('default already loaded') + error( 'default already loaded' ) end -default = { - ----- - -- Default action calls user scripts on**** functions. - -- - action = function(inlet) - -- in case of moves getEvent returns the origin and dest of the move - local event, event2 = inlet.getEvent() - local config = inlet.getConfig() - local func = config['on'.. event.etype] - if func then - func(event, event2) - end - -- if function didnt change the wait status its not interested - -- in this event -> drop it. - if event.status == 'wait' then - inlet.discardEvent(event) - end - end, +default = { } - ----- - -- Default collector. - -- - -- Called when collecting a finished child process - -- - collect = function(agent, exitcode) +-- +-- used to ensure there aren't typos in the keys +-- +default.checkgauge = { + action = true, + checkgauge = true, + collect = true, + delay = true, + exitcodes = true, + init = true, + maxDelays = true, + maxProcesses = true, + onCreate = true, + onModify = true, + onDelete = true, + onStartup = true, + onMove = true, + prepare = true, + rsyncExitCodes = true, -- TODO + sshExitCodes = true -- TODO +} - local config = agent.config - local rc +-- +-- On default action the user's on*** scripts are called. +-- +default.action = function( inlet ) - if config.exitcodes then - rc = config.exitcodes[exitcode] - elseif exitcode == 0 then - rc = 'ok' - else - rc = 'die' - end + -- in case of moves getEvent returns the origin and dest of the move + local event, event2 = inlet.getEvent( ) + local config = inlet.getConfig( ) - -- TODO synchronize with similar code before - if not agent.isList and agent.etype == 'Init' then - if rc == 'ok' then - log('Normal', 'Startup of "',agent.source,'" finished.') - return 'ok' - elseif rc == 'again' then - if settings.insist then - log( - 'Normal', - 'Retrying startup of "', - agent.source, - '": ', - exitcode - ) + local func = config[ 'on'.. event.etype ] - return 'again' - else - log( - 'Error', - 'Temporary or permanent failure on startup of "', - agent.source, - '". Terminating since "insist" is not set.' - ) + if func then + func( event, event2 ) + end - terminate( -1 ) - end - elseif rc == 'die' then + -- if function didnt change the wait status its not interested + -- in this event -> drop it. + if event.status == 'wait' then + inlet.discardEvent( event ) + end + +end + + +-- +-- Default collector. +-- +-- Called when collecting a finished child process +-- +default.collect = function( agent, exitcode ) + + local config = agent.config + local rc + + if config.exitcodes then + rc = config.exitcodes[exitcode] + elseif exitcode == 0 then + rc = 'ok' + else + rc = 'die' + end + + -- TODO synchronize with similar code before + if not agent.isList and agent.etype == 'Init' then + if rc == 'ok' then + log('Normal', 'Startup of "',agent.source,'" finished.') + return 'ok' + elseif rc == 'again' then + if settings.insist then + log( + 'Normal', + 'Retrying startup of "', + agent.source, + '": ', + exitcode + ) + + return 'again' + else log( 'Error', - 'Failure on startup of "', + 'Temporary or permanent failure on startup of "', agent.source, - '".' + '". Terminating since "insist" is not set.' ) terminate( -1 ) - else - log( - 'Error', - 'Unknown exitcode "', - exitcode, - '" on startup of "', - agent.source, - '".' - ) - return 'die' end - end + elseif rc == 'die' then + log( + 'Error', + 'Failure on startup of "', + agent.source, + '".' + ) - if agent.isList then - if rc == 'ok' then - log( - 'Normal', - 'Finished a list after exitcode: ', - exitcode - ) - elseif rc == 'again' then - log( - 'Normal', - 'Retrying a list after exitcode = ', - exitcode - ) - elseif rc == 'die' then - log( - 'Error', - 'Failure with a list width exitcode = ', - exitcode - ) - else - log( - 'Error', - 'Unknown exitcode "',exitcode,'" with a list' - ) - - rc = 'die' - end + terminate( -1 ) else - if rc == 'ok' then - log('Normal', 'Retrying ',agent.etype,' on ',agent.sourcePath,' = ',exitcode) - elseif rc == 'again' then - log('Normal', 'Finished ',agent.etype,' on ',agent.sourcePath,' = ',exitcode) - elseif rc == 'die' then - log('Error', 'Failure with ',agent.etype,' on ',agent.sourcePath,' = ',exitcode) - else - log('Normal', 'Unknown exitcode "',exitcode,'" with ', agent.etype, - ' on ',agent.sourcePath,' = ',exitcode) - rc = 'die' - end + log( + 'Error', + 'Unknown exitcode "', + exitcode, + '" on startup of "', + agent.source, + '".' + ) + return 'die' end + end - return rc - end, + if agent.isList then + if rc == 'ok' then + log( + 'Normal', + 'Finished a list after exitcode: ', + exitcode + ) + elseif rc == 'again' then + log( + 'Normal', + 'Retrying a list after exitcode = ', + exitcode + ) + elseif rc == 'die' then + log( + 'Error', + 'Failure with a list width exitcode = ', + exitcode + ) + else + log( + 'Error', + 'Unknown exitcode "',exitcode,'" with a list' + ) - ----- - -- called on (re)initialization of Lsyncd. - -- - init = function(event) - local config = event.config - local inlet = event.inlet - -- user functions - -- calls a startup if given by user script. - if type(config.onStartup) == 'function' then - local startup = config.onStartup(event) - -- TODO honor some return codes of startup like "warmstart". + rc = 'die' end - - if event.status == 'wait' then - -- user script did not spawn anything - -- thus the blanket event is deleted again. - inlet.discardEvent(event) + else + if rc == 'ok' then + log('Normal', 'Retrying ',agent.etype,' on ',agent.sourcePath,' = ',exitcode) + elseif rc == 'again' then + log('Normal', 'Finished ',agent.etype,' on ',agent.sourcePath,' = ',exitcode) + elseif rc == 'die' then + log('Error', 'Failure with ',agent.etype,' on ',agent.sourcePath,' = ',exitcode) + else + log('Normal', 'Unknown exitcode "',exitcode,'" with ', agent.etype, + ' on ',agent.sourcePath,' = ',exitcode) + rc = 'die' end - end, + end + + return rc +end + + +-- +-- Called on the Init event sent +-- on (re)initialization of Lsyncd for every sync +-- +default.init = function(event) + local config = event.config + local inlet = event.inlet + + -- user functions + -- calls a startup if given by user script. + if type(config.onStartup) == 'function' then + local startup = config.onStartup(event) + -- TODO honor some return codes of startup like "warmstart". + end + + if event.status == 'wait' then + -- user script did not spawn anything + -- thus the blanket event is deleted again. + inlet.discardEvent(event) + end +end + + +-- +-- The maximum number of processes Lsyncd will +-- simultanously spawn for this sync. +-- +default.maxProcesses = 1 + + +-- +-- The collapsor tries not to have more than these delays. +-- So it dealy stack does not grow too large, +-- since calculation for stacking events is n*log(n) (or so) +-- +default.maxDelays = 1000 + + +--- +-- a default configuration using /bin/cp|rm|mv. +-- TODO huh? +-- +default.direct = default_direct + + +-- +-- Exitcodes of rsync and what to do. +-- TODO move to rsync +-- +default.rsyncExitCodes = { - ----- - -- The maximum number of processes Lsyncd will spawn simultanously for - -- one sync. -- - maxProcesses = 1, - - ----- - -- Try not to have more than these delays. - -- not too large, since total calculation for stacking - -- events is n*log(n) or so.. + -- if another config provides the same table + -- this will not be inherited (merged) into that one -- - maxDelays = 1000, - - ----- - -- a default configuration using /bin/cp|rm|mv. + -- if it does not, integer keys are to be copied + -- verbatim -- - direct = default_direct, + _merge = false, + _verbatim = true, - ------ - -- Exitcodes of rsync and what to do. - -- - rsyncExitCodes = { + [ 0 ] = 'ok', + [ 1 ] = 'die', + [ 2 ] = 'die', + [ 3 ] = 'again', + [ 4 ] = 'die', + [ 5 ] = 'again', + [ 6 ] = 'again', + [ 10 ] = 'again', + [ 11 ] = 'again', + [ 12 ] = 'again', + [ 14 ] = 'again', + [ 20 ] = 'again', + [ 21 ] = 'again', + [ 22 ] = 'again', - -- - -- if another config provides the same table - -- this will not be inherited (merged) into that one - -- - -- if it does not, integer keys are to be copied - -- verbatim - -- - _merge = false, - _verbatim = true, + -- partial transfers are ok, since Lsyncd has registered the event that + -- caused the transfer to be partial and will recall rsync. + [ 23 ] = 'ok', + [ 24 ] = 'ok', - [ 0 ] = 'ok', - [ 1 ] = 'die', - [ 2 ] = 'die', - [ 3 ] = 'again', - [ 4 ] = 'die', - [ 5 ] = 'again', - [ 6 ] = 'again', - [ 10 ] = 'again', - [ 11 ] = 'again', - [ 12 ] = 'again', - [ 14 ] = 'again', - [ 20 ] = 'again', - [ 21 ] = 'again', - [ 22 ] = 'again', - -- partial transfers are ok, since Lsyncd has registered the event that - -- caused the transfer to be partial and will recall rsync. - [ 23 ] = 'ok', - [ 24 ] = 'ok', - [ 25 ] = 'die', - [ 30 ] = 'again', - [ 35 ] = 'again', - [ 255 ] = 'again', - }, + [ 25 ] = 'die', + [ 30 ] = 'again', + [ 35 ] = 'again', - ----- - -- Exitcodes of ssh and what to do. - -- - sshExitCodes = { - - -- - -- if another config provides the same table - -- this will not be inherited (merged) into that one - -- - -- if it does not, integer keys are to be copied - -- verbatim - -- - _merge = false, - _verbatim = true, - - [ 0 ] = 'ok', - [ 255 ] = 'again', - }, - - ----- - -- Minimum seconds between two writes of a status file. - -- - statusInterval = 10, + [ 255 ] = 'again', } + + +-- +-- Exitcodes of ssh and what to do. +-- +default.sshExitCodes = { + + -- + -- if another config provides the same table + -- this will not be inherited (merged) into that one + -- + -- if it does not, integer keys are to be copied + -- verbatim + -- + _merge = false, + _verbatim = true, + + [ 0 ] = 'ok', + [ 255 ] = 'again', +} + + +-- +-- Minimum seconds between two writes of a status file. +-- +default.statusInterval = 10 + + +-- +-- checks all keys to be in the checkgauge +-- + +local function check( + config, + gauge, + level +) + for k, v in pairs( config ) do + + if not gauge[k] then + error( + 'Parameter "' + .. k + .. '" unknown.' + .. ' (if this is not a typo add it to checkgauge)', + level + ); + end + + if type( gauge [ k ] ) == 'table' then + + if type( v ) ~= 'table' then + + error( + 'Parameter "' + .. k + .. '" must be a table.', + level + ) + + end + + check( config[ k ], gauge[ k ], level + 1 ) + + end + end +end + +default.prepare = function( config, level ) + + local gauge = config.checkgauge + + if not gauge then + return + end + + check( config, gauge, level or 2 ) +end + From 6f90c19196a35be1b8fd90b581545bc886241fa1 Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Sat, 6 Oct 2012 13:43:55 +0200 Subject: [PATCH 14/28] working on new inheritance, chechgauge system --- default-rsync.lua | 8 ++- default.lua | 47 ++++++++++++------ lsyncd.lua | 123 +++++++++++++++++++++++++++++----------------- 3 files changed, 114 insertions(+), 64 deletions(-) diff --git a/default-rsync.lua b/default-rsync.lua index 7234641..a012ea0 100644 --- a/default-rsync.lua +++ b/default-rsync.lua @@ -26,8 +26,9 @@ if default.rsync then end -default.rsync = { } -local rsync = default.rsync +local rsync = { } +default.rsync = rsync + -- uses default collect -- @@ -35,8 +36,6 @@ local rsync = default.rsync -- rsync.checkgauge = { - default.checkgauge, - -- unsets default user action handlers onCreate = false, onModify = false, @@ -46,7 +45,6 @@ rsync.checkgauge = { delete = true, exclude = true, - source = true, target = true, rsync = { diff --git a/default.lua b/default.lua index a1f23d4..649270d 100644 --- a/default.lua +++ b/default.lua @@ -15,6 +15,21 @@ end default = { } +-- +-- Only this items are inherited from the default +-- table +-- +default._merge = { + action = true, + checkgauge = true, + collect = true, + delay = true, + init = true, + maxDelays = true, + maxProcesses = true, + prepare = true, +} + -- -- used to ensure there aren't typos in the keys -- @@ -33,8 +48,9 @@ default.checkgauge = { onStartup = true, onMove = true, prepare = true, - rsyncExitCodes = true, -- TODO - sshExitCodes = true -- TODO + -- rsyncExitCodes = true, -- TODO + source = true, + -- sshExitCodes = true -- TODO } -- @@ -195,13 +211,6 @@ default.init = function(event) end --- --- The maximum number of processes Lsyncd will --- simultanously spawn for this sync. --- -default.maxProcesses = 1 - - -- -- The collapsor tries not to have more than these delays. -- So it dealy stack does not grow too large, @@ -210,11 +219,11 @@ default.maxProcesses = 1 default.maxDelays = 1000 ---- --- a default configuration using /bin/cp|rm|mv. --- TODO huh? -- -default.direct = default_direct +-- The maximum number of processes Lsyncd will +-- simultanously spawn for this sync. +-- +default.maxProcesses = 1 -- @@ -294,6 +303,7 @@ default.statusInterval = 10 local function check( config, gauge, + subtable, level ) for k, v in pairs( config ) do @@ -301,6 +311,7 @@ local function check( if not gauge[k] then error( 'Parameter "' + .. subtable .. k .. '" unknown.' .. ' (if this is not a typo add it to checkgauge)', @@ -314,6 +325,7 @@ local function check( error( 'Parameter "' + .. subtable .. k .. '" must be a table.', level @@ -321,7 +333,12 @@ local function check( end - check( config[ k ], gauge[ k ], level + 1 ) + check( + config[ k ], + gauge[ k ], + subtable .. k .. '.', + level + 1 + ) end end @@ -335,6 +352,6 @@ default.prepare = function( config, level ) return end - check( config, gauge, level or 2 ) + check( config, gauge, '', level or 2 ) end diff --git a/lsyncd.lua b/lsyncd.lua index aa6e803..4c1a160 100644 --- a/lsyncd.lua +++ b/lsyncd.lua @@ -2119,7 +2119,7 @@ local Syncs = ( function( ) -- -- the list of all syncs -- - local list = Array.new( ) + local syncsList = Array.new( ) -- -- The round robin pointer. In case of global limited maxProcesses @@ -2134,7 +2134,7 @@ local Syncs = ( function( ) round = round + 1; - if round > #list then + if round > #syncsList then round = 1 end @@ -2152,7 +2152,7 @@ local Syncs = ( function( ) -- Returns sync at listpos i -- local function get( i ) - return list[ i ]; + return syncsList[ i ]; end -- @@ -2182,7 +2182,17 @@ local Syncs = ( function( ) -- non integer keys -- for k, v in pairs( cs ) do - if type( k ) ~= 'number' or cs._verbatim == true then + if + ( + type( k ) ~= 'number' or + cs._verbatim == true + ) + and + ( + type( cs._merge ) ~= 'table' or + cs._merge[ k ] == true + ) + then inheritKV( cd, k, v ) end end @@ -2211,25 +2221,27 @@ local Syncs = ( function( ) -- inheritKV = function( cd, k, v ) + -- don't merge inheritance controls + if k == '_merge' or k == '_verbatim' then + return + end + local dtype = type( cd [ k ] ) if type( v ) == 'table' then if dtype == 'nil' then - cd[ k ] = { } inherit( cd[ k ], v ) - - elseif dtype == 'table' and v._merge ~= false then - + elseif + dtype == 'table' and + v._merge ~= false + then inherit( cd[ k ], v ) - end elseif dtype == 'nil' then - cd[ k ] = v - end end @@ -2240,22 +2252,42 @@ local Syncs = ( function( ) -- local function add( config ) - -- Creates a new config table and inherit all keys/values + -- Creates a new config table which inherits all keys/values -- from integer keyed tables local uconfig = config - config = { } - inherit( config, uconfig, false ) - -- Lets settings or commandline override delay values. + config = { } + + inherit( config, uconfig ) + + -- + -- last and least defaults are inherited + -- + inherit( config, default ) + + local inheritSettings = { + 'delay', + 'maxDelays', + 'maxProcesses' + } + -- Lets settings or commandline override these values. if settings then - config.delay = settings.delay or config.delay + for _, v in ipairs( inheritSettings ) do + if settings[ v ] then + config[ v ] = settings[ v ] + end + end end - -- at very first lets the userscript 'prepare' function - -- fill out more values. + -- + -- lets the userscript 'prepare' function + -- check and complete the config + -- if type( config.prepare ) == 'function' then - -- explicitly gives a writeable copy of config. + + -- prepare is given a writeable copy of config config.prepare( config ) + end if not config[ 'source' ] then @@ -2268,7 +2300,9 @@ local Syncs = ( function( ) terminate( -1 ) end + -- -- absolute path of source + -- local realsrc = lsyncd.realdir( config.source ) if not realsrc then @@ -2307,20 +2341,6 @@ local Syncs = ( function( ) settings = {} end - local defaultValues = { - 'action', - 'collect', - 'init', - 'maxDelays', - 'maxProcesses', - } - - for _, dn in pairs( defaultValues ) do - if config[ dn ] == nil then - config[ dn ] = settings[ dn ] or default[ dn ] - end - end - -- the monitor to use config.monitor = settings.monitor or @@ -2347,7 +2367,9 @@ local Syncs = ( function( ) --- creates the new sync local s = Sync.new( config ) - table.insert( list, s ) + + table.insert( syncsList, s ) + return s end @@ -2355,21 +2377,21 @@ local Syncs = ( function( ) -- Allows a for-loop to walk through all syncs. -- local function iwalk( ) - return ipairs( list ) + return ipairs( syncsList ) end -- -- Returns the number of syncs. -- local size = function( ) - return #list + return #syncsList end -- -- Tests if any sync is interested in a path. -- local function concerns( path ) - for _, s in ipairs( list ) do + for _, s in ipairs( syncsList ) do if s:concerns( path ) then return true end @@ -3761,41 +3783,54 @@ function runner.initialize( firstTime ) settings[ v ]= true end + -- -- all command line settings overwrite config file settings - + -- for k, v in pairs( clSettings ) do - if k ~= 'syncs' then settings[ k ] = v end end - -- implicitly force 'insist' on Lsyncd resets. + -- + -- implicitly forces 'insist' on Lsyncd resets. + -- if not firstTime then settings.insist = true end + -- -- adds syncs specified by command line. + -- if clSettings.syncs then for _, s in ipairs( clSettings.syncs ) do - if s[1] == 'rsync' then + if s[ 1 ] == 'rsync' then + sync{ default.rsync, source = s[ 2 ], target = s[ 3 ] } - elseif s[1] == 'rsyncssh' then + + elseif s[ 1 ] == 'rsyncssh' then + sync{ default.rsyncssh, source = s[ 2 ], host = s[ 3 ], targetdir=s[ 4 ] } - elseif s[1] == 'direct' then - sync{ default.direct, source=s[2], target=s[3]} + + elseif s[ 1 ] == 'direct' then + sync{ + default.direct, + source=s[ 2 ], + target=s[ 3 ] + } + end end From a6b49c8650aad2fd0ff525c34636dfa9a4565fd1 Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Sat, 6 Oct 2012 14:22:08 +0200 Subject: [PATCH 15/28] working on the new rsync config system --- default-direct.lua | 235 ++++++++++++++++++++----------------------- default-rsync.lua | 68 ++++++++++--- default-rsyncssh.lua | 26 ++--- default.lua | 2 +- lsyncd.lua | 2 +- 5 files changed, 183 insertions(+), 150 deletions(-) diff --git a/default-direct.lua b/default-direct.lua index ed43810..29b6c08 100644 --- a/default-direct.lua +++ b/default-direct.lua @@ -30,150 +30,137 @@ if default.direct then error('default-direct already loaded') end -default.direct = { +local direct = { } - -- - -- Spawns rsync for a list of events - -- - action = function(inlet) - -- gets all events ready for syncing - local event, event2 = inlet.getEvent() - local config = inlet.getConfig() +default.direct = direct - if event.etype == 'Create' then - if event.isdir then - spawn( - event, - '/bin/mkdir', - event.targetPath - ) - else - -- 'cp -t', not supported on OSX - spawn( - event, - '/bin/cp', - event.sourcePath, - event.targetPathdir - ) - end - elseif event.etype == 'Modify' then - if event.isdir then - error("Do not know how to handle 'Modify' on dirs") - end - spawn(event, +-- +-- Spawns rsync for a list of events +-- +direct.action = function(inlet) + -- gets all events ready for syncing + local event, event2 = inlet.getEvent() + local config = inlet.getConfig() + + if event.etype == 'Create' then + if event.isdir then + spawn( + event, + '/bin/mkdir', + event.targetPath + ) + else + -- 'cp -t', not supported on OSX + spawn( + event, '/bin/cp', event.sourcePath, event.targetPathdir ) - elseif event.etype == 'Delete' then - if not config.delete then - inlet.discardEvent(event) - end - - local tp = event.targetPath - -- extra security check - if tp == '' or tp == '/' or not tp then - error('Refusing to erase your harddisk!') - end - spawn(event, '/bin/rm', '-rf', tp) - elseif event.etype == 'Move' then - local tp = event.targetPath - -- extra security check - if tp == '' or tp == '/' or not tp then - error('Refusing to erase your harddisk!') - end - local command = '/bin/mv $1 $2 || /bin/rm -rf $1' - if not config.delete then command = '/bin/mv $1 $2'; end - spawnShell( - event, - command, - event.targetPath, - event2.targetPath) - else - log('Warn', 'ignored an event of type "',event.etype, '"') + end + elseif event.etype == 'Modify' then + if event.isdir then + error("Do not know how to handle 'Modify' on dirs") + end + spawn(event, + '/bin/cp', + event.sourcePath, + event.targetPathdir + ) + elseif event.etype == 'Delete' then + if not config.delete then inlet.discardEvent(event) end - end, - ----- - -- Called when collecting a finished child process - -- - collect = function(agent, exitcode) - local config = agent.config + local tp = event.targetPath + -- extra security check + if tp == '' or tp == '/' or not tp then + error('Refusing to erase your harddisk!') + end + spawn(event, '/bin/rm', '-rf', tp) + elseif event.etype == 'Move' then + local tp = event.targetPath + -- extra security check + if tp == '' or tp == '/' or not tp then + error('Refusing to erase your harddisk!') + end + local command = '/bin/mv $1 $2 || /bin/rm -rf $1' + if not config.delete then command = '/bin/mv $1 $2'; end + spawnShell( + event, + command, + event.targetPath, + event2.targetPath) + else + log('Warn', 'ignored an event of type "',event.etype, '"') + inlet.discardEvent(event) + end +end - if not agent.isList and agent.etype == 'Init' then - local rc = config.rsyncExitCodes[exitcode] - if rc == 'ok' then - log('Normal', 'Startup of "',agent.source,'" finished: ', exitcode) - elseif rc == 'again' then - if settings.insist then - log('Normal', 'Retrying startup of "',agent.source,'": ', exitcode) - else - log('Error', 'Temporary or permanent failure on startup of "', - agent.source, '". Terminating since "insist" is not set.'); - terminate(-1) -- ERRNO - end - elseif rc == 'die' then - log('Error', 'Failure on startup of "',agent.source,'": ', exitcode) +-- +-- Called when collecting a finished child process +-- +direct.collect = function(agent, exitcode) + local config = agent.config + + if not agent.isList and agent.etype == 'Init' then + local rc = config.rsyncExitCodes[exitcode] + if rc == 'ok' then + log('Normal', 'Startup of "',agent.source,'" finished: ', exitcode) + elseif rc == 'again' then + if settings.insist then + log('Normal', 'Retrying startup of "',agent.source,'": ', exitcode) else - log('Error', 'Unknown exitcode on startup of "', agent.source,': "',exitcode) - rc = 'die' + log('Error', 'Temporary or permanent failure on startup of "', + agent.source, '". Terminating since "insist" is not set.'); + terminate(-1) -- ERRNO end - return rc + elseif rc == 'die' then + log('Error', 'Failure on startup of "',agent.source,'": ', exitcode) + else + log('Error', 'Unknown exitcode on startup of "', agent.source,': "',exitcode) + rc = 'die' end + return rc + end - -- everything else is just as it is, - -- there is no network to retry something. - return - end, + -- everything else is just as it is, + -- there is no network to retry something. + return +end - ----- - -- Spawns the recursive startup sync - -- (currently) identical to default rsync. - -- - init = default.rsync.init, +-- +-- Spawns the recursive startup sync +-- (currently) identical to default rsync. +-- +direct.init = default.rsync.init - ----- - -- Checks the configuration. - -- - prepare = function(config) - if not config.target then - error('default.direct needs "target".', 4) - end +-- +-- Checks the configuration. +-- +direct.prepare = function( config, level ) - if config.rsyncOps then - error('did you mean rsyncOpts with "t"?', 4) - end - end, + default.rsync.prepare( config, level + 1 ) - ----- - -- Default delay is very short. - -- - delay = 1, +end - ------ - -- Let the core not split move events. - -- - onMove = true, +-- +-- Default delay is very short. +-- +direct.delay = 1 - ----- - -- The rsync binary called. - -- - rsync = default.rsync.rsync, +-- +-- Let the core not split move events. +-- +direct.onMove = true - ----- - -- By default do deletes. - -- - delete = true, +-- +-- By default do deletes. +-- +direct.delete = true - ----- - -- rsync exit codes - -- - rsyncExitCodes = default.rsyncExitCodes, +-- +-- On many system multiple disk operations just rather slow down +-- than speed up. - ----- - -- On many system multiple disk operations just rather slow down - -- than speed up. - - maxProcesses = 1, -} +direct.maxProcesses = 1 diff --git a/default-rsync.lua b/default-rsync.lua index a012ea0..4ceb233 100644 --- a/default-rsync.lua +++ b/default-rsync.lua @@ -296,8 +296,6 @@ rsync.prepare = function( skipTarget -- used by rsyncssh, do not check for target ) - level = level or 4 - -- -- First let default.prepare test the checkgauge -- @@ -363,11 +361,42 @@ rsync.prepare = function( ) end + -- -- computes the rsync arguments into one list - local rsync = config.rsync; + -- + local crsync = config.rsync; - rsync._computed = { true } - local computed = rsync._computed + -- + -- everything implied by archive = true + -- + local archiveFlags = { + recursive = true, + links = true, + perms = true, + times = true, + group = true, + owner = true, + devices = true, + specials = true, + hard_links = false, + acls = false, + xattrs = false, + } + + -- + -- if archive given the implications are filled in + -- + if crsync.archive then + for k, v in pairs( archiveFlags ) do + if crsync[ k ] == nil then + crsync[ k ] = v + end + end + end + + + crsync._computed = { true } + local computed = crsync._computed local computedN = 1 local shortFlags = { @@ -401,27 +430,42 @@ rsync.prepare = function( local shorts = { '-' } local shortsN = 2 - if config.rsync._extra then - for k, v in ipairs( config.rsync._extra ) do + if crsync._extra then + for k, v in ipairs( crsync._extra ) do computed[ computedN ] = v computedN = computedN + 1 end end for k, flag in pairs( shortFlags ) do - if config.rsync[k] then + if crsync[ k ] then shorts[ shortsN ] = flag shortsN = shortsN + 1 end end - if config.rsync.rsh then - computed[ computedN ] = '--rsh=' + config.rsync.rsh + if crsync.devices and crsync.specials then + shorts[ shortsN ] = 'D' + shortsN = shortsN + 1 + else + if crsync.devices then + computed[ computedN ] = '--devices' + computedN = computedN + 1 + end + + if crsync.specials then + computed[ computedN ] = '--specials' + computedN = computedN + 1 + end + end + + if crsync.rsh then + computed[ computedN ] = '--rsh=' + crsync.rsh computedN = computedN + 1 end - if config.rsync.rsync_path then - computed[ computedN ] = '--rsync-path=' + config.rsync.rsync_path + if crsync.rsync_path then + computed[ computedN ] = '--rsync-path=' + crsync.rsync_path computedN = computedN + 1 end diff --git a/default-rsyncssh.lua b/default-rsyncssh.lua index 9ea744c..e02cc6b 100644 --- a/default-rsyncssh.lua +++ b/default-rsyncssh.lua @@ -39,25 +39,27 @@ default.rsyncssh = rsyncssh rsyncssh.checkgauge = { -- unsets the inherited value of from default.rsync - target = false, - onMove = true, + target = false, + onMove = true, -- rsyncssh users host and targetdir - host = true, - targetdir = true, + host = true, + targetdir = true, + sshExitCodes = true, + rsyncExitCodes = true, -- ssh settings ssh = { - binary = true, - port = true, - _extra = true + binary = true, + port = true, + _extra = true }, -- xargs settings xargs = { - binary = true, - delimiter = true, - _extra = true + binary = true, + delimiter = true, + _extra = true } } @@ -254,9 +256,9 @@ end -- -- checks the configuration. -- -rsyncssh.prepare = function( config ) +rsyncssh.prepare = function( config, level ) - default.rsync.prepare( config, 5, true ) + default.rsync.prepare( config, level + 1, true ) if not config.host then error('default.rsyncssh needs "host" configured', 4) diff --git a/default.lua b/default.lua index 649270d..b801c56 100644 --- a/default.lua +++ b/default.lua @@ -352,6 +352,6 @@ default.prepare = function( config, level ) return end - check( config, gauge, '', level or 2 ) + check( config, gauge, '', level + 1 ) end diff --git a/lsyncd.lua b/lsyncd.lua index 4c1a160..f779881 100644 --- a/lsyncd.lua +++ b/lsyncd.lua @@ -2286,7 +2286,7 @@ local Syncs = ( function( ) if type( config.prepare ) == 'function' then -- prepare is given a writeable copy of config - config.prepare( config ) + config.prepare( config, 4 ) end From 1b6d9bb65afb0c9f30b0a1245eaa874147e7dd22 Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Sun, 7 Oct 2012 19:48:23 +0200 Subject: [PATCH 16/28] code beautifications and cleanup, exits with error level 143 after receiving a sigterm --- lsyncd.c | 2999 ++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 1890 insertions(+), 1109 deletions(-) diff --git a/lsyncd.c b/lsyncd.c index 44a4a5d..8916d96 100644 --- a/lsyncd.c +++ b/lsyncd.c @@ -1,17 +1,19 @@ -/** - * lsyncd.c Live (Mirror) Syncing Demon - * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - * - * This is the core. It contains as minimal as possible glues - * to the operating system needed for lsyncd operation. All high-level - * logic is coded (when feasable) into lsyncd.lua - * - * This code assumes you have a 100 character wide display to view it (when tabstop is 4) - * - * License: GPLv2 (see COPYING) or any later version - * Authors: Axel Kittenberger - * - **/ +/* +| lsyncd.c Live (Mirror) Syncing Demon +| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +| +| This is Lsyncd's core. +| +| It contains as minimal as possible glues to the operating system needed +| for Lsyncd's operation. All high-level logic is coded (when feasable) +| into lsyncd.lua +| +| This code assumes you have a 100 character wide display to view it (when tabstop is 4) +| +| License: GPLv2 (see COPYING) or any later version +| Authors: Axel Kittenberger +| +*/ #include "lsyncd.h" @@ -44,18 +46,18 @@ #include #include -/** - * The Lua part of lsyncd if compiled into the binary. - */ +/* +| The Lua part of Lsyncd +*/ extern const char runner_out[]; extern size_t runner_size; extern const char defaults_out[]; extern size_t defaults_size; -/** - * Makes sure there is one monitor. - */ +/* +| Makes sure there is one file system monitor. +*/ #ifndef LSYNCD_WITH_INOTIFY #ifndef LSYNCD_WITH_FANOTIFY #ifndef LSYNCD_WITH_FSEVENTS @@ -64,9 +66,9 @@ extern size_t defaults_size; #endif #endif -/** - * All monitors supported by this Lsyncd. - */ +/* +| All monitors supported by this Lsyncd. +*/ static char *monitors[] = { #ifdef LSYNCD_WITH_INOTIFY @@ -85,48 +87,49 @@ static char *monitors[] = { }; /** - * configuration parameters - */ +| Configuration parameters that matter to the core +*/ struct settings settings = { - .log_file = NULL, - .log_syslog = false, - .log_ident = NULL, + .log_file = NULL, + .log_syslog = false, + .log_ident = NULL, .log_facility = LOG_USER, - .log_level = LOG_NOTICE, - .nodaemon = false, + .log_level = LOG_NOTICE, + .nodaemon = false, }; -/** - * True when lsyncd daemonized itself. - */ +/* +| True when Lsyncd daemonized itself. +*/ static bool is_daemon = false; -/** - * The config file loaded by Lsyncd. - * Global so it is retained during HUPs - */ +/* +| The config file loaded by Lsyncd. +*/ char * lsyncd_config_file = NULL; -/** - * False after first time Lsyncd started up. - * - * Thus configuration error messages are written to stdout/stderr only on - * first start. - * - * All other resets (HUP or inotify OVERFLOW) run with implictly --insist - * turned on and thus Lsyncd not failing on a not responding target. - */ +/* +| False after first time Lsyncd started up. +| +| Configuration error messages are thus written to +| stdout/stderr only on first start. +| +| All other resets (HUP or monitor OVERFLOW) run with 'insist' +| implictly turned on and thus Lsyncd does not failing on a non +| responding target. +*/ static bool first_time = true; -/** - * Set to TERM or HUP in signal handler, when lsyncd should end or reset ASAP. - */ +/* +| Set by TERM or HUP signal handler +| telling Lsyncd should end or reset ASAP. +*/ volatile sig_atomic_t hup = 0; volatile sig_atomic_t term = 0; -/** - * The kernels clock ticks per second. - */ +/* +| The kernel's clock ticks per second. +*/ static long clocks_per_sec; /** @@ -149,188 +152,254 @@ sig_handler(int sig) } } -/** - * Non glibc builds need a real tms structure for times() call - */ +/* +| Non glibc builds need a real tms structure for the times( ) call +*/ #ifdef __GLIBC__ - static struct tms *dummy_tms = NULL; + static struct tms * dummy_tms = NULL; #else - static struct tms _dummy_tms; - static struct tms *dummy_tms = &_dummy_tms; + static struct tms _dummy_tms; + static struct tms * dummy_tms = &_dummy_tms; #endif -/** -| Returns the absolute path of path. +/* +| Returns the absolute path of a path. +| | This is a wrapper to various C-Library differences. */ char * -get_realpath(const char * rpath) { +get_realpath( const char * rpath ) +{ // uses c-library to get the absolute path #ifdef __GLIBC__ // in case of GLIBC the task is easy. - return realpath(rpath, NULL); + return realpath( rpath, NULL ); #else # warning having to use old style realpath() - // otherwise less so and requires PATH_MAX limit. - char buf[PATH_MAX]; - char *asw = realpath(rpath, buf); - if (!asw) return NULL; - return s_strdup(asw); + // otherwise less so and requires PATH_MAX limit + char buf[ PATH_MAX] ; + char *asw = realpath( rpath, buf ); + if( !asw ) + { return NULL; } + + return s_strdup( asw ); #endif } -/***************************************************************************** - * Logging - ****************************************************************************/ -/** - * A logging category - */ -struct logcat { + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* +( Logging ) + *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + +/* +| A logging category +*/ +struct logcat +{ char *name; int priority; }; -/** - * A table of all enabled logging categories. - * Sorted by first letter to have to do less comparisons; - */ -static struct logcat *logcats[26] = {0,}; -/** - * Returns the positive priority if category is configured to be logged or -1. - */ +/* +| A table of all enabled logging categories. +| Sorted by first letter for faster access. +*/ +static struct logcat * +logcats[ 26 ] = { 0, }; + + +/* +| Returns a positive priority if category is configured to be logged or -1. +*/ extern int -check_logcat(const char *name) +check_logcat( const char *name ) { struct logcat *lc; - if (name[0] < 'A' || name[0] > 'Z') { - return 99; - } - lc = logcats[name[0]-'A']; - if (!lc) return 99; + if( name[ 0 ] < 'A' || name[ 0 ] > 'Z') + { return 99; } + + lc = logcats[ name[ 0 ] - 'A' ]; + + if( !lc ) + { return 99; } + + while( lc->name ) + { + if( !strcmp( lc->name, name ) ) + { return lc->priority; } - while (lc->name) { - if (!strcmp(lc->name, name)) { - return lc->priority; - } lc++; } return 99; } -/** - * Adds a logging category - * @return true if OK. - */ + +/* +| Adds a logging category +| +| Returns true if OK. +*/ static bool -add_logcat(const char *name, int priority) +add_logcat( const char *name, int priority ) { struct logcat *lc; - if (!strcmp("all", name)) { + + if( !strcmp( "all", name ) ) + { settings.log_level = 99; return true; } - if (!strcmp("scarce", name)) { + + if( !strcmp( "scarce", name ) ) + { settings.log_level = LOG_WARNING; return true; } // categories must start with a capital letter. - if (name[0] < 'A' || name[0] > 'Z') return false; + if( name[ 0 ] < 'A' || name[ 0 ] > 'Z' ) + { return false; } - if (!logcats[name[0]-'A']) { + if( !logcats[ name[ 0 ]- 'A' ] ) + { // an empty capital letter lc = logcats[name[0]-'A'] = s_calloc(2, sizeof(struct logcat)); - } else { - int ll = 0; // length of letter list + } + else + { + // length of letter list + int ll = 0; + // counts list length - for(lc = logcats[name[0]-'A']; lc->name; lc++) { - ll++; - } + for( lc = logcats[name[0]-'A']; lc->name; lc++ ) + { ll++; } + // enlarges list - logcats[name[0]-'A'] = - s_realloc(logcats[name[0]-'A'], (ll + 2) * sizeof(struct logcat)); - // go to list end - for(lc = logcats[name[0]-'A']; lc->name; lc++) { - if (!strcmp(name, lc->name)) { + logcats[ name[ 0 ] - 'A'] = + s_realloc( + logcats[ name[ 0 ]-'A' ], + ( ll + 2 ) * sizeof( struct logcat ) + ); + + // goes to the list end + for( lc = logcats[ name[ 0 ] - 'A']; lc->name; lc++ ) + { + if( !strcmp( name, lc->name ) ) + { // already there return true; } } } - lc->name = s_strdup(name); + + lc->name = s_strdup( name ); lc->priority = priority; + // terminates the list - lc[1].name = NULL; + lc[ 1 ].name = NULL; return true; } -/** - * Logs a string. - * - * Do not call directly, but the macro logstring() in lsyncd.h - * - * @param priorty the priority of the log message - * @param cat the category - * @param message the log message - */ + +/* +| Logs a string. +| +| Do not call this directly, but the macro logstring( ) +| defined in lsyncd.h +*/ extern void -logstring0(int priority, const char *cat, const char *message) +logstring0( + int priority, // the priority of the log message + const char * cat, // the category + const char * message // the log message +) { - if (first_time) { - // lsyncd is in intial configuration phase. + if( first_time ) + { + // lsyncd is in it's intial configuration phase. // thus just print to normal stdout/stderr. - if (priority >= LOG_ERR) { - fprintf(stderr, "%s: %s\n", cat, message); - } else { - printf("%s: %s\n", cat, message); + if( priority >= LOG_ERR ) + { + fprintf( stderr, "%s: %s\n", cat, message); + } + else + { + printf( "%s: %s\n", cat, message ); } return; } // writes on console if not daemonized - if (!is_daemon) { - char ct[255]; + if( !is_daemon ) + { + char ct[ 255 ]; // gets current timestamp hour:minute:second time_t mtime; - time(&mtime); - strftime(ct, sizeof(ct), "%T", localtime(&mtime)); + time( &mtime ); + + strftime( ct, sizeof( ct ), "%T", localtime( &mtime ) ); + FILE * flog = priority <= LOG_ERR ? stderr : stdout; - fprintf(flog, "%s %s: %s\n", ct, cat, message); + + fprintf( + flog, + "%s %s: %s\n", + ct, cat, message + ); } // writes to file if configured so - if (settings.log_file) { - FILE * flog = fopen(settings.log_file, "a"); - // gets current timestamp day-time-year + if (settings.log_file) + { + FILE * flog = fopen( settings.log_file, "a" ); char * ct; time_t mtime; - time(&mtime); - ct = ctime(&mtime); - // cuts trailing linefeed - ct[strlen(ct) - 1] = 0; - if (flog == NULL) { - fprintf(stderr, "Cannot open logfile [%s]!\n", - settings.log_file); - exit(-1); // ERRNO + // gets current timestamp day-time-year + time( &mtime ); + ct = ctime( &mtime ); + + // cuts trailing linefeed + ct[ strlen( ct ) - 1] = 0; + + if( flog == NULL ) + { + fprintf( + stderr, + "Cannot open logfile [%s]!\n", + settings.log_file + ); + exit( -1 ); } - fprintf(flog, "%s %s: %s\n", ct, cat, message); - fclose(flog); + + fprintf( + flog, + "%s %s: %s\n", + ct, cat, message + ); + + fclose( flog ); } // sends to syslog if configured so - if (settings.log_syslog) syslog(priority, "%s, %s", cat, message); + if( settings.log_syslog ) + { + syslog( priority, "%s, %s", cat, message ); + } + return; } -/** - * Lets the core print logmessages comfortably as formated string. - * This uses the lua_State for it easy string buffers only. - */ + +/* +| Lets the core print logmessages comfortably as formated string. +| This uses the lua_State for it easy string buffers only. +*/ extern void printlogf0(lua_State *L, int priority, @@ -346,551 +415,754 @@ printlogf0(lua_State *L, return; } -/***************************************************************************** - * Simple memory management - * TODO: call the garbace collector in case of out of memory. - ****************************************************************************/ -/** - * "secured" calloc. - */ + +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* +( Simple memory management ) + *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + +// FIXME call the Lua garbace collector in case of out of memory + +/* +| "Secured" calloc +*/ extern void * -s_calloc(size_t nmemb, size_t size) +s_calloc( size_t nmemb, size_t size ) { - void *r = calloc(nmemb, size); - if (r == NULL) { - logstring0(LOG_ERR, "Error", "Out of memory!"); - exit(-1); // ERRNO + void * r = calloc( nmemb, size ); + + if( r == NULL ) + { + logstring0( + LOG_ERR, + "Error", + "Out of memory!" + ); + + exit( -1 ); } + return r; } -/** - * "secured" malloc. the deamon shall kill itself - * in case of out of memory. - */ + +/* +| "Secured" malloc +*/ extern void * -s_malloc(size_t size) +s_malloc( size_t size ) { - void *r = malloc(size); - if (r == NULL) { - logstring0(LOG_ERR, "Error", "Out of memory!"); - exit(-1); // ERRNO + void * r = malloc( size ); + + if( r == NULL ) + { + logstring0( + LOG_ERR, + "Error", + "Out of memory!" + ); + + exit( -1 ); } + return r; } -/** - * "secured" realloc. - */ + +/* +| "Secured" realloc +*/ extern void * -s_realloc(void *ptr, size_t size) +s_realloc( void * ptr, size_t size ) { - void *r = realloc(ptr, size); - if (r == NULL) { - logstring0(LOG_ERR, "Error", "Out of memory!"); - exit(-1); + void * r = realloc( ptr, size ); + + if( r == NULL ) + { + logstring0( + LOG_ERR, + "Error", + "Out of memory!" + ); + + exit( -1 ); } + return r; } -/** - * "secured" strdup. - */ + +/* +| "Secured" strdup +*/ extern char * -s_strdup(const char *src) +s_strdup( const char *src ) { - char *s = strdup(src); - if (s == NULL) { - logstring0(LOG_ERR, "Error", "Out of memory!"); - exit(-1); // ERRNO + char *s = strdup( src ); + + if( s == NULL ) + { + logstring0( + LOG_ERR, + "Error", + "Out of memory!" + ); + + exit( -1 ); } + return s; } -/***************************************************************************** - * Pipes management - ****************************************************************************/ -/** - * A child process gets text piped longer than on - * write() can manage. - */ -struct pipemsg { - char *text; // message to send - int tlen; // length of text - int pos; // position in message +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* +( Pipes Management ) + *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + +/* +| A child process gets text piped through stdin +*/ +struct pipemsg +{ + char * text; // message to send + int tlen; // length of text + int pos; // position in message }; -/** - * Called by the core whenever a pipe becomes - * writeable again - */ +/* +| Called by the core whenever a pipe becomes +| writeable again +*/ static void -pipe_writey(lua_State *L, struct observance *observance) +pipe_writey( + lua_State * L, + struct observance * observance +) { int fd = observance->fd; - struct pipemsg *pm = (struct pipemsg *) observance->extra; - int len = write(fd, pm->text + pm->pos, pm->tlen - pm->pos); + + struct pipemsg *pm = (struct pipemsg * ) observance->extra; + + int len = write( + fd, + pm->text + pm->pos, + pm->tlen - pm->pos + ); + pm->pos += len; - if (len < 0) { - logstring("Normal", "broken pipe."); - nonobserve_fd(fd); - } else if (pm->pos >= pm->tlen) { + + if( len < 0 ) + { + logstring( "Normal", "broken pipe." ); + nonobserve_fd( fd ); + } + else if( pm->pos >= pm->tlen ) + { logstring("Exec", "finished pipe."); nonobserve_fd(fd); } } -/** - * Called when cleaning up a pipe - */ + +/* +| Called when cleaning up a pipe. +*/ static void -pipe_tidy(struct observance *observance) +pipe_tidy( struct observance * observance ) { - struct pipemsg *pm = (struct pipemsg *) observance->extra; - close(observance->fd); - free(pm->text); - free(pm); + struct pipemsg *pm = ( struct pipemsg * ) observance->extra; + + close( observance->fd ); + free( pm->text ); + free( pm ); } -/***************************************************************************** - * helper routines. - ****************************************************************************/ -/** - * Dummy variable whos address is used as the cores index in the lua registry - * to the lua runners function table in the lua registry. - */ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* +( Helper Routines ) + *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + +/* +| Dummy variable of which it's address is used as +| the cores index in the lua registry to +| the lua runners function table in the lua registry. +*/ static int runner; -/** - * Dummy variable whos address is used as the cores index n the lua registry - * to the lua runners error handler. - */ +/* +| Dummy variable of which it's address is used as +| the cores index n the lua registry to +| the lua runners error handler. +*/ static int callError; -/** - * Sets the close-on-exit flag for an fd - */ +/* +| Sets the close-on-exit flag of a file descriptor. +*/ extern void -close_exec_fd(int fd) +close_exec_fd( int fd ) { int flags; - flags = fcntl(fd, F_GETFD); - if (flags == -1) { - logstring("Error", "cannot get descriptor flags!"); - exit(-1); // ERRNO + + flags = fcntl( fd, F_GETFD ); + + if( flags == -1 ) + { + logstring( "Error", "cannot get descriptor flags!" ); + exit( -1 ); } flags |= FD_CLOEXEC; - if (fcntl(fd, F_SETFD, flags) == -1) { - logstring("Error", "cannot set descripptor flags!"); - exit(-1); // ERRNO + + if( fcntl( fd, F_SETFD, flags ) == -1 ) + { + logstring( "Error", "cannot set descripptor flags!" ); + exit( -1 ); } } -/** - * Sets the non-blocking flag for an fd - */ + +/* +| Sets the non-blocking flag of a file descriptor. +*/ extern void -non_block_fd(int fd) +non_block_fd( int fd ) { int flags; - flags = fcntl(fd, F_GETFL); - if (flags == -1) { - logstring("Error", "cannot get status flags!"); - exit(-1); // ERRNO + flags = fcntl( fd, F_GETFL ); + + if( flags == -1 ) + { + logstring( "Error", "cannot get status flags!" ); + exit( -1 ); } flags |= O_NONBLOCK; - if (fcntl(fd, F_SETFL, flags) == -1) { - logstring("Error", "cannot set status flags!"); - exit(-1); // ERRNO + if( fcntl( fd, F_SETFL, flags ) == -1 ) + { + logstring( "Error", "cannot set status flags!" ); + exit( -1 ); } } -/** - * Writes a pid file. - */ +/* +| Writes a pid file. +*/ static void -write_pidfile(lua_State *L, const char *pidfile) { - FILE* f = fopen(pidfile, "w"); - if (!f) { - printlogf(L, "Error", "Cannot write pidfile; '%s'", pidfile); - exit(-1); // ERRNO +write_pidfile( lua_State *L, const char *pidfile ) +{ + FILE* f = fopen( pidfile, "w" ); + + if( !f ) + { + printlogf( + L, "Error", + "Cannot write pidfile; '%s'", + pidfile + ) + ; + exit( -1 ); } - fprintf(f, "%i\n", getpid()); - fclose(f); + + fprintf( f, "%i\n", getpid( ) ); + fclose( f ); } -/***************************************************************************** - * Observances - ****************************************************************************/ -/** - * List of file descriptor watches. - */ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* +( Observances ) + *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ + + +/* +| List of file descriptor watches. +*/ static struct observance * observances = NULL; -static int observances_len = 0; -static int observances_size = 0; +static int observances_len = 0; +static int observances_size = 0; -/** - * List of file descriptors to nonobserve. - * While working for the oberver lists, it may - * not be altered, thus nonobserve stores here the - * actions that will be delayed. - */ -static int *nonobservances = NULL; -static int nonobservances_len = 0; + +/* +| List of file descriptors to not observe. +| +| While working for the oberver lists, it may +| not be altered, thus nonobserve stores the +| delayed removals. +*/ +static int * nonobservances = NULL; +static int nonobservances_len = 0; static int nonobservances_size = 0; -/** - * true while the observances list is being handled. - */ +/* +| True while the observances list is being handled. +*/ static bool observance_action = false; -/** - * Core watches a filedescriptor to become ready, - * one of read_ready or write_ready may be zero - */ + +/* +| Core watches a filedescriptor to become ready, +| one of read_ready or write_ready may be zero +*/ extern void -observe_fd(int fd, - void (*ready) (lua_State *, struct observance *), - void (*writey)(lua_State *, struct observance *), - void (*tidy) (struct observance *), - void *extra) +observe_fd( + int fd, + void ( * ready ) (lua_State *, struct observance * ), + void ( * writey ) (lua_State *, struct observance * ), + void ( * tidy ) (struct observance * ), + void *extra +) { int pos; + // looks if the fd is already there as pos or // stores the position to insert the new fd in pos - for(pos = 0; pos < observances_len; pos++) { - if (fd <= observances[pos].fd) { - break; - } + for( pos = 0; pos < observances_len; pos++) + { + if( fd <= observances[ pos ].fd ) + { break; } } - if (pos < observances_len && observances[pos].fd == fd) { + + if( pos < observances_len && observances[ pos ].fd == fd ) + { // just updates an existing observance - logstring("Masterloop", "updating fd observance"); - observances[pos].ready = ready; - observances[pos].writey = writey; - observances[pos].tidy = tidy; - observances[pos].extra = extra; + logstring( "Masterloop", "updating fd observance" ); + observances[ pos ].ready = ready; + observances[ pos ].writey = writey; + observances[ pos ].tidy = tidy; + observances[ pos ].extra = extra; return; } - if (observance_action) { - // TODO - logstring("Error", - "internal, New observances in ready/writey handlers not yet supported"); - exit(-1); // ERRNO + if( observance_action ) + { + // FIXME + logstring( + "Error", + "New observances in ready/writey handlers not yet supported" + ); + + exit( -1 ); } - if (!tidy) { - logstring("Error", - "internal, tidy() in observe_fd() must not be NULL."); - exit(-1); // ERRNO + if( !tidy ) + { + logstring( + "Error", + "internal, tidy() in observe_fd() must not be NULL." + ); + exit( -1 ); } - if (observances_len + 1 > observances_size) { + + if( observances_len + 1 > observances_size ) + { observances_size = observances_len + 1; - observances = s_realloc(observances, - observances_size * sizeof(struct observance)); + observances = s_realloc( + observances, + observances_size * sizeof( struct observance ) + ); } - memmove(observances + pos + 1, observances + pos, - (observances_len - pos) * (sizeof(struct observance))); + + memmove( + observances + pos + 1, + observances + pos, + (observances_len - pos) * sizeof(struct observance) + ); observances_len++; - observances[pos].fd = fd; - observances[pos].ready = ready; - observances[pos].writey = writey; - observances[pos].tidy = tidy; - observances[pos].extra = extra; + + observances[ pos ].fd = fd; + observances[ pos ].ready = ready; + observances[ pos ].writey = writey; + observances[ pos ].tidy = tidy; + observances[ pos ].extra = extra; } -/** - * Makes the core to no longer watch a filedescriptor. - */ + +/* +| Makes the core no longer watch a filedescriptor. +*/ extern void -nonobserve_fd(int fd) +nonobserve_fd( int fd ) { int pos; - if (observance_action) { + if( observance_action ) + { // this function is called through a ready/writey handler // while the core works through the observance list, thus // it does not alter the list, but stores this actions // on a stack nonobservances_len++; - if (nonobservances_len > nonobservances_size) { + if( nonobservances_len > nonobservances_size ) + { nonobservances_size = nonobservances_len; - nonobservances = s_realloc(nonobservances, nonobservances_size * sizeof(int)); + nonobservances = s_realloc( + nonobservances, + nonobservances_size * sizeof( int ) + ); } - nonobservances[nonobservances_len - 1] = fd; + + nonobservances[ nonobservances_len - 1 ] = fd; return; } // looks for the fd - for(pos = 0; pos < observances_len; pos++) { - if (observances[pos].fd == fd) { - break; - } + for( pos = 0; pos < observances_len; pos++ ) + { + if( observances[ pos ].fd == fd ) + { break; } } - if (pos >= observances_len) { - logstring("Error", - "internal fail, not observance file descriptor in nonobserve"); - exit(-1); //ERRNO + + if( pos >= observances_len ) + { + logstring( + "Error", + "internal fail, not observance file descriptor in nonobserve" + ); + + exit( -1 ); } // tidies up the observance - observances[pos].tidy(observances + pos); + observances[ pos ].tidy( observances + pos ); // and moves the list down - memmove(observances + pos, observances + pos + 1, - (observances_len - pos) * (sizeof(struct observance))); + memmove( + observances + pos, + observances + pos + 1, + (observances_len - pos) * sizeof( struct observance ) + ); + observances_len--; } -/** - * A user observance became read-ready - */ + +/* +| A user observance became read-ready. +*/ static void -user_obs_ready(lua_State *L, struct observance *obs) +user_obs_ready( + lua_State * L, + struct observance * obs +) { int fd = obs->fd; + // pushes the ready table on table - lua_pushlightuserdata(L, (void *) user_obs_ready); - lua_gettable(L, LUA_REGISTRYINDEX); + lua_pushlightuserdata( L, ( void * ) user_obs_ready ); + lua_gettable( L, LUA_REGISTRYINDEX ); // pushes the error handler - lua_pushlightuserdata(L, (void *) &callError); - lua_gettable(L, LUA_REGISTRYINDEX); - - // pushed the user func - lua_pushnumber(L, fd); - lua_gettable(L, -3); - - // gives the ufunc the fd - lua_pushnumber(L, fd); - - // calls the user function - if (lua_pcall(L, 1, 0, -3)) exit(-1); // ERRNO - lua_pop(L, 2); -} - -/** - * A user observance became write-ready - */ -static void -user_obs_writey(lua_State *L, struct observance *obs) -{ - int fd = obs->fd; - // pushes the writey table on table - lua_pushlightuserdata(L, (void *) user_obs_writey); - lua_gettable(L, LUA_REGISTRYINDEX); - - // pushes the error handler - lua_pushlightuserdata(L, (void *) &callError); - lua_gettable(L, LUA_REGISTRYINDEX); + lua_pushlightuserdata( L, (void *) &callError ); + lua_gettable( L, LUA_REGISTRYINDEX ); // pushes the user func - lua_pushnumber(L, fd); - lua_gettable(L, -3); + lua_pushnumber( L, fd ); + lua_gettable( L, -3 ); - // gives the user func the fd - lua_pushnumber(L, fd); + // gives the ufunc the fd + lua_pushnumber( L, fd ); // calls the user function - if (lua_pcall(L, 1, 0, -3)) exit(-1); // ERRNO - lua_pop(L, 2); + if( lua_pcall( L, 1, 0, -3 ) ) + { exit( -1 ); } + + lua_pop( L, 2 ); } -/** - * Tidies up a user observance - * TODO - give the user a chance to do something in that case! - */ + +/* +| A user observance became write-ready +*/ static void -user_obs_tidy(struct observance *obs) +user_obs_writey( + lua_State * L, + struct observance * obs +) { - close(obs->fd); + int fd = obs->fd; + + // pushes the writey table on table + lua_pushlightuserdata( L, (void *) user_obs_writey ); + lua_gettable( L, LUA_REGISTRYINDEX ); + + // pushes the error handler + lua_pushlightuserdata(L, (void *) &callError); + lua_gettable( L, LUA_REGISTRYINDEX ); + + // pushes the user func + lua_pushnumber( L, fd ); + lua_gettable( L, -3 ); + + // gives the user func the fd + lua_pushnumber( L, fd ); + + // calls the user function + if( lua_pcall( L, 1, 0, -3 ) ) + { + exit(-1); + } + + lua_pop( L, 2 ); +} + +/* +| Tidies up a user observance +| FIXME - give the user a chance to do something in that case! +*/ +static void +user_obs_tidy( struct observance *obs ) +{ + close( obs->fd ); } -/***************************************************************************** - * Library calls for lsyncd.lua - * - * These are as minimal as possible glues to the operating system needed for - * lsyncd operation. - ****************************************************************************/ +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* +( Library calls for the runner ) + *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ -static void daemonize(lua_State *L); -int l_stackdump(lua_State* L); -/** - * Logs a message. - * - * @param loglevel (Lua stack) loglevel of massage - * @param string (Lua stack) the string to log - */ +static void daemonize( lua_State *L ); +int l_stackdump( lua_State* L ); + + +/* +| Logs a message. +| +| Params on Lua stack: +| +| 1: loglevel of massage +| 2: the string to log +*/ static int -l_log(lua_State *L) +l_log( lua_State *L ) { const char * cat; // log category const char * message; // log message int priority; // log priority - cat = luaL_checkstring(L, 1); - priority = check_logcat(cat); - /* skips filtered messages */ - if (priority > settings.log_level) return 0; + cat = luaL_checkstring( L, 1 ); + priority = check_logcat( cat ); + // skips filtered messages + if( priority > settings.log_level ) + { return 0; } + + // replaces non string values { - // replace non string values int i; int top = lua_gettop(L); - for (i = 1; i <= top; i++) { - int t = lua_type(L, i); - switch (t) { - case LUA_TTABLE: - lua_pushfstring(L, "(Table: %p)", lua_topointer(L, i)); - lua_replace(L, i); - break; - case LUA_TBOOLEAN: - if (lua_toboolean(L, i)) { - lua_pushstring(L, "(true)"); - } else { - lua_pushstring(L, "(false)"); - } - lua_replace(L, i); - break; - case LUA_TUSERDATA: - { - clock_t *c = (clock_t *) luaL_checkudata(L, i, "Lsyncd.jiffies"); - double d = (*c); - d /= clocks_per_sec; - lua_pushfstring(L, "(Timestamp: %f)", d); + for( i = 1; i <= top; i++ ) + { + int t = lua_type( L, i ); + + switch( t ) + { + case LUA_TTABLE : + lua_pushfstring( + L, + "(Table: %p)", + lua_topointer( L, i ) + ); + + lua_replace( L, i ); + break; + + case LUA_TBOOLEAN : + if( lua_toboolean( L, i ) ) + { lua_pushstring( L, "(true)" ); } + else + { lua_pushstring( L, "(false)" ); } + lua_replace(L, i); - } - break; - case LUA_TNIL: - lua_pushstring(L, "(nil)"); - lua_replace(L, i); - break; + break; + + case LUA_TUSERDATA: + { + clock_t *c = ( clock_t * ) + luaL_checkudata( L, i, "Lsyncd.jiffies" ); + + double d = *c; + d /= clocks_per_sec; + lua_pushfstring( L, "(Timestamp: %f)", d ); + lua_replace( L, i ); + } + break; + + case LUA_TNIL: + lua_pushstring( L, "(nil)" ); + lua_replace( L, i ); + break; } } } // concates if there is more than one string parameter - lua_concat(L, lua_gettop(L) - 1); + lua_concat( L, lua_gettop( L ) - 1 ); + + message = luaL_checkstring( L, 2 ); + logstring0( priority, cat, message ); - message = luaL_checkstring(L, 2); - logstring0(priority, cat, message); return 0; } -/** - * Returns (on Lua stack) the current kernels - * clock state (jiffies) - */ + +/* +| Returns (on Lua stack) the current kernels +| clock state (jiffies) +*/ extern int l_now(lua_State *L) { - clock_t *j = lua_newuserdata(L, sizeof(clock_t)); - luaL_getmetatable(L, "Lsyncd.jiffies"); - lua_setmetatable(L, -2); - *j = times(dummy_tms); + clock_t * j = lua_newuserdata( L, sizeof( clock_t ) ); + luaL_getmetatable( L, "Lsyncd.jiffies" ); + lua_setmetatable( L, -2 ); + *j = times( dummy_tms ); return 1; } -/** - * Executes a subprocess. Does not wait for it to return. - * - * @param (Lua stack) Path to binary to call - * @params (Lua stack) list of string as arguments - * or "<" in which case the next argument is a string that will be piped - * on stdin. the arguments will follow that one. - * - * @return (Lua stack) the pid on success, 0 on failure. - */ +/* +| Executes a subprocess. Does not wait for it to return. +| +| Params on Lua stack: +| +| 1: Path to binary to call +| 2: List of string as arguments +| or "<" in which case the next argument is a string +| that will be piped on stdin. +| The arguments will follow that one. +| +| Returns (Lua stack) the pid on success, 0 on failure. +*/ static int -l_exec(lua_State *L) +l_exec( lua_State *L ) { - const char *binary = luaL_checkstring(L, 1); // the binary to call - int argc = lua_gettop(L) - 1; // number of arguments - pid_t pid; // the pid spawned - int li = 1; // the arguments position in the lua arguments + // the binary to call + const char *binary = luaL_checkstring(L, 1); + + // number of arguments + int argc = lua_gettop( L ) - 1; + + // the pid spawned + pid_t pid; + + // the arguments position in the lua arguments + int li = 1; + + // the pipe to text + char const * pipe_text = NULL; + + // the pipes length + size_t pipe_len = 0; + + // the arguments + char const ** argv; + + // pipe file descriptors + int pipefd[ 2 ]; - char const *pipe_text = NULL; // the pipe to text - size_t pipe_len = 0; // the pipes length - char const **argv; // the arguments - int pipefd[2]; // pipe file descriptors int i; - // expands tables if there are any, removes nils - for(i = 1; i <= lua_gettop(L); i++) { - if (lua_isnil(L, i)) { - lua_remove(L, i); + // expands tables + // and removes nils + for( i = 1; i <= lua_gettop( L ); i++ ) + { + if( lua_isnil( L, i ) ) + { + lua_remove( L, i ); i--; argc--; continue; } - if (lua_istable(L, i)) { + if( lua_istable( L, i ) ) + { int tlen; int it; - lua_checkstack(L, lua_gettop(L) + lua_objlen(L, i) + 1); - // move table to top of stack - lua_pushvalue(L, i); - lua_remove(L, i); + lua_checkstack( L, lua_gettop( L ) + lua_objlen( L, i ) + 1 ); + + // moves table to top of stack + lua_pushvalue( L, i ); + lua_remove( L, i ); argc--; - tlen = lua_objlen(L, -1); - for (it = 1; it <= tlen; it++) { - lua_pushinteger(L, it); - lua_gettable(L, -2); - lua_insert(L,i); + tlen = lua_objlen( L, -1 ); + + for( it = 1; it <= tlen; it++ ) + { + lua_pushinteger( L, it ); + lua_gettable( L, -2 ); + lua_insert( L, i ); i++; argc++; } i--; - lua_pop(L, 1); + lua_pop( L, 1 ); } } - // writes a log message, prepares the message only if actually needed. - if (check_logcat("Exec") <= settings.log_level) { - lua_checkstack(L, lua_gettop(L) + argc * 3 + 2); - lua_pushvalue(L, 1); - for(i = 1; i <= argc; i++) { - lua_pushstring(L, " ["); - lua_pushvalue(L, i + 1); - lua_pushstring(L, "]"); + // writes a log message (if needed). + if( check_logcat( "Exec" ) <= settings.log_level ) + { + lua_checkstack( L, lua_gettop( L ) + argc * 3 + 2 ); + lua_pushvalue( L, 1 ); + + for( i = 1; i <= argc; i++ ) + { + lua_pushstring( L, " [" ); + lua_pushvalue( L, i + 1 ); + lua_pushstring( L, "]" ); } - lua_concat(L, 3 * argc + 1); - logstring0(LOG_DEBUG, "Exec", luaL_checkstring(L, -1)); - lua_pop(L, 1); + + lua_concat( L, 3 * argc + 1 ); + + logstring0( + LOG_DEBUG, "Exec", + luaL_checkstring(L, -1) + ); + + lua_pop( L, 1 ); } - if (argc >= 2 && !strcmp(luaL_checkstring(L, 2), "<")) { + if( argc >= 2 && !strcmp( luaL_checkstring( L, 2 ), "<" ) ) + { // pipes something into stdin - if (!lua_isstring(L, 3)) { - logstring("Error", "in spawn(), expected a string after pipe '<'"); - exit(-1); // ERRNO + if( !lua_isstring( L, 3 ) ) + { + logstring( + "Error", + "in spawn(), expected a string after pipe '<'" + ); + + exit( -1 ); } - pipe_text = lua_tolstring(L, 3, &pipe_len); - if (strlen(pipe_text) > 0) { + + pipe_text = lua_tolstring( L, 3, &pipe_len ); + + if( strlen( pipe_text ) > 0 ) + { // creates the pipe - if (pipe(pipefd) == -1) { - logstring("Error", "cannot create a pipe!"); - exit(-1); // ERRNO + if( pipe( pipefd ) == -1 ) + { + logstring( "Error", "cannot create a pipe!" ); + + exit( -1 ); } + // always closes the write end for child processes - close_exec_fd(pipefd[1]); + close_exec_fd( pipefd[ 1 ] ); + // sets the write end on non-blocking - non_block_fd(pipefd[1]); - } else { + non_block_fd( pipefd[ 1 ] ); + } + else + { pipe_text = NULL; } argc -= 2; @@ -898,72 +1170,115 @@ l_exec(lua_State *L) } // prepares the arguments - argv = s_calloc(argc + 2, sizeof(char *)); - argv[0] = binary; - for(i = 1; i <= argc; i++) { - argv[i] = luaL_checkstring(L, i + li); - } - argv[i] = NULL; - pid = fork(); + argv = s_calloc( argc + 2, sizeof( char * ) ); + argv[ 0 ] = binary; + for( i = 1; i <= argc; i++ ) + { argv[i] = luaL_checkstring( L, i + li ); } + argv[ i ] = NULL; - if (pid == 0) { + // the fork! + pid = fork( ); + + if( pid == 0 ) + { // replaces stdin for pipes - if (pipe_text) dup2(pipefd[0], STDIN_FILENO); + if( pipe_text ) + { dup2( pipefd[ 0 ], STDIN_FILENO ); } // if lsyncd runs as a daemon and has a logfile it will redirect // stdout/stderr of child processes to the logfile. - if (is_daemon && settings.log_file) { - if (!freopen(settings.log_file, "a", stdout)) { - printlogf(L, "Error", "cannot redirect stdout to '%s'.", settings.log_file); + if( is_daemon && settings.log_file ) + { + if( !freopen( settings.log_file, "a", stdout ) ) + { + printlogf( + L, "Error", + "cannot redirect stdout to '%s'.", + settings.log_file + ); } - if (!freopen(settings.log_file, "a", stderr)) { - printlogf(L, "Error", "cannot redirect stderr to '%s'.", settings.log_file); + if( !freopen( settings.log_file, "a", stderr ) ) + { + printlogf( + L, "Error", + "cannot redirect stderr to '%s'.", + settings.log_file + ); } } - execv(binary, (char **)argv); + + execv( binary, ( char ** ) argv ); + // in a sane world execv does not return! - printlogf(L, "Error", "Failed executing [%s]!", binary); - exit(-1); // ERRNO + printlogf( + L, "Error", + "Failed executing [ %s ]!", + binary + ); + + exit( -1 ); } - if (pipe_text) { + if( pipe_text ) + { int len; + // first closes read-end of pipe, this is for child process only - close(pipefd[0]); + close( pipefd[ 0 ] ); + // starts filling the pipe - len = write(pipefd[1], pipe_text, pipe_len); - if (len < 0) { - logstring("Normal", "immediatly broken pipe."); - close(pipefd[1]); - } else if (len == pipe_len) { + len = write( pipefd[ 1 ], pipe_text, pipe_len ); + + if( len < 0 ) + { + logstring( "Normal", "immediatly broken pipe." ); + close( pipefd[ 1 ] ); + } + else if( len == pipe_len ) + { // usual and best case, the pipe accepted all input -> close - close(pipefd[1]); - logstring("Exec", "one-sweeped pipe"); - } else { + close( pipefd[ 1 ] ); + logstring( "Exec", "one-sweeped pipe" ); + } + else + { struct pipemsg *pm; - logstring("Exec", "adding pipe observance"); - pm = s_calloc(1, sizeof(struct pipemsg)); - pm->text = s_calloc(pipe_len + 1, sizeof(char*)); - memcpy(pm->text, pipe_text, pipe_len + 1); + logstring( "Exec", "adding pipe observance" ); + pm = s_calloc( 1, sizeof( struct pipemsg ) ); + pm->text = s_calloc( pipe_len + 1, sizeof( char * ) ); + memcpy( pm->text, pipe_text, pipe_len + 1 ); pm->tlen = pipe_len; pm->pos = len; - observe_fd(pipefd[1], NULL, pipe_writey, pipe_tidy, pm); + + observe_fd( + pipefd[ 1 ], + NULL, + pipe_writey, + pipe_tidy, + pm + ); } } - free(argv); - lua_pushnumber(L, pid); + + free( argv ); + lua_pushnumber( L, pid ); + return 1; } -/** - * Converts a relative directory path to an absolute. - * - * @param dir: a relative path to directory - * @return the absolute path of directory - */ + +/* +| Converts a relative directory path to an absolute. +| +| Params on Lua stack: +| 1: a relative path to directory +| +| Returns on Lua stack: +| The absolute path of directory +*/ static int -l_realdir(lua_State *L) +l_realdir( lua_State *L ) { luaL_Buffer b; const char *rdir = luaL_checkstring(L, 1); @@ -1002,966 +1317,1432 @@ l_realdir(lua_State *L) return 1; } -/** - * Dumps the LUA stack. For debugging purposes. - */ +/* +| Dumps the Lua stack. +| For debugging purposes. +*/ int -l_stackdump(lua_State* L) +l_stackdump( lua_State * L ) { int i; - int top = lua_gettop(L); - printlogf(L, "Debug", "total in stack %d",top); - for (i = 1; i <= top; i++) { - int t = lua_type(L, i); - switch (t) { - case LUA_TSTRING: - printlogf(L, "Debug", "%d string: '%s'", i, lua_tostring(L, i)); - break; - case LUA_TBOOLEAN: - printlogf(L, "Debug", "%d boolean %s", i, lua_toboolean(L, i) ? "true" : "false"); - break; - case LUA_TNUMBER: - printlogf(L, "Debug", "%d number: %g", i, lua_tonumber(L, i)); - break; - default: - printlogf(L, "Debug", "%d %s", i, lua_typename(L, t)); - break; + int top = lua_gettop( L ); + + printlogf( + L, "Debug", + "total in stack %d", + top + ); + + for( i = 1; i <= top; i++ ) + { + int t = lua_type( L, i ); + switch( t ) + { + case LUA_TSTRING: + printlogf( + L, "Debug", + "%d string: '%s'", + i, lua_tostring( L, i ) + ); + break; + + case LUA_TBOOLEAN: + printlogf( + L, "Debug", + "%d boolean %s", + i, lua_toboolean( L, i ) ? "true" : "false" + ); + break; + + case LUA_TNUMBER: + printlogf( + L, "Debug", + "%d number: %g", + i, lua_tonumber( L, i ) + ); + break; + + default: + printlogf( + L, "Debug", + "%d %s", + i, lua_typename( L, t ) + ); + break; } } + return 0; } -/** - * Reads the directories entries. - * - * @param (Lua stack) absolute path to directory. - * @return (Lua stack) a table of directory names. - * names are keys, values are boolean - * true on dirs. - */ +/* +| Reads the directories entries. +| +| Params on Lua stack: +| 1: absolute path to directory +| +| Returns on Lua stack: +| a table of directory names. +| names are keys +| values are boolean true on dirs. +*/ static int -l_readdir (lua_State *L) +l_readdir ( lua_State *L ) { - const char * dirname = luaL_checkstring(L, 1); + const char * dirname = luaL_checkstring( L, 1 ); DIR *d; - d = opendir(dirname); - if (d == NULL) { - printlogf(L, "Error", "cannot open dir [%s].", dirname); + d = opendir( dirname ); + if( d == NULL ) + { + printlogf( + L, "Error", "cannot open dir [%s].", + dirname + ); + return 0; } - lua_newtable(L); - while (!hup && !term) { - struct dirent *de = readdir(d); + lua_newtable( L ); + + while( !hup && !term ) + { + struct dirent *de = readdir( d ); bool isdir; - if (de == NULL) break; // finished + + if( de == NULL ) + { + // finished + break; + } // ignores . and .. - if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; + if( + !strcmp( de->d_name, "." ) || + !strcmp( de->d_name, ".." ) + ) + { + continue; + } - if (de->d_type == DT_UNKNOWN) { + if( de->d_type == DT_UNKNOWN ) + { // must call stat on some systems :-/ - char *entry = s_malloc(strlen(dirname) + strlen(de->d_name) + 2); + // ( e.g. ReiserFS ) + char *entry = s_malloc( + strlen( dirname ) + + strlen( de->d_name ) + + 2 + ); struct stat st; - strcpy(entry, dirname); - strcat(entry, "/"); - strcat(entry, de->d_name); - lstat(entry, &st); - isdir = S_ISDIR(st.st_mode); - free(entry); - } else { - // readdir can trusted + + strcpy( entry, dirname ); + strcat( entry, "/" ); + strcat( entry, de->d_name ); + + lstat( entry, &st ); + + isdir = S_ISDIR( st.st_mode ); + + free( entry ); + } + else + { + // otherwise readdir can be trusted isdir = de->d_type == DT_DIR; } // adds this entry to the Lua table - lua_pushstring(L, de->d_name); - lua_pushboolean(L, isdir); - lua_settable(L, -3); + lua_pushstring( L, de->d_name ); + lua_pushboolean( L, isdir ); + lua_settable( L, -3 ); } - closedir(d); + + closedir( d ); return 1; } -/** - * Terminates lsyncd daemon. - * - * @param (Lua stack) exitcode for lsyncd. - * - * Does not return. - */ + +/* +| Terminates Lsyncd. +| +| Params on Lua stack: +| 1: exitcode of Lsyncd. +| +| Does not return. +| +*/ int l_terminate(lua_State *L) { - int exitcode = luaL_checkinteger(L, 1); - exit(exitcode); + int exitcode = luaL_checkinteger( L, 1 ); + + exit( exitcode ); + return 0; } -/** - * Configures core parameters. - * - * @param (Lua stack) a string for a core configuratoin - * @param (Lua stack) --differes depending on string. - */ +/* +| Configures core parameters. +| +| Params on Lua stack: +| 1: a string, configure option +| 2: depends on Param 1 +*/ static int -l_configure(lua_State *L) +l_configure( lua_State *L ) { - const char * command = luaL_checkstring(L, 1); - if (!strcmp(command, "running")) { + const char * command = luaL_checkstring( L, 1 ); + if( !strcmp( command, "running" ) ) + { // set by runner after first initialize // from this on log to configurated log end instead of // stdout/stderr first_time = false; - if (!settings.nodaemon && !settings.log_file) { + if( !settings.nodaemon && !settings.log_file ) + { settings.log_syslog = true; - const char * log_ident = settings.log_ident ? settings.log_ident : "lsyncd"; - openlog(log_ident, 0, settings.log_facility); + + const char * log_ident = + settings.log_ident ? + settings.log_ident : + "lsyncd"; + + openlog( log_ident, 0, settings.log_facility ); } - if (!settings.nodaemon && !is_daemon) { - logstring("Debug", "daemonizing now."); - daemonize(L); + if( !settings.nodaemon && !is_daemon ) + { + logstring( "Debug", "daemonizing now." ); + daemonize( L ); } - if (settings.pidfile) write_pidfile(L, settings.pidfile); + if( settings.pidfile ) + { + write_pidfile( L, settings.pidfile ); + } - } else if (!strcmp(command, "nodaemon")) { + } + else if( !strcmp( command, "nodaemon" ) ) + { settings.nodaemon = true; + } + else if( !strcmp( command, "logfile" ) ) + { + const char * file = luaL_checkstring( L, 2 ); - } else if (!strcmp(command, "logfile")) { - const char * file = luaL_checkstring(L, 2); - if (settings.log_file) free(settings.log_file); - settings.log_file = s_strdup(file); + if( settings.log_file ) + { free( settings.log_file ); } - } else if (!strcmp(command, "pidfile")) { - const char * file = luaL_checkstring(L, 2); - if (settings.pidfile) free(settings.pidfile); - settings.pidfile = s_strdup(file); + settings.log_file = s_strdup( file ); + } + else if( !strcmp( command, "pidfile" ) ) + { + const char * file = luaL_checkstring( L, 2 ); - } else if (!strcmp(command, "logfacility")) { - if (lua_isstring(L, 2)) { - const char * fname = luaL_checkstring(L, 2); + if( settings.pidfile ) + { free( settings.pidfile ); } + + settings.pidfile = s_strdup( file ); + } + else if( !strcmp( command, "logfacility" ) ) + { + if( lua_isstring( L, 2 ) ) + { + const char * fname = luaL_checkstring( L, 2 ); int i; - for(i = 0; facilitynames[i].c_name; i++) { - if (!strcasecmp(fname, facilitynames[i].c_name)) break; + for( i = 0; facilitynames[ i ].c_name; i++ ) + { + if( !strcasecmp( fname, facilitynames[ i ].c_name ) ) + { break; } } - if (!facilitynames[i].c_name) { - printlogf(L, "Error", "Logging facility '%s' unknown.", fname); - exit(-1); //ERRNO + + if( !facilitynames[ i ].c_name ) + { + printlogf( + L, "Error", + "Logging facility '%s' unknown.", + fname + ); + + exit( -1 ); } - settings.log_facility = facilitynames[i].c_val; - } else if (lua_isnumber(L, 2)) { - settings.log_facility = luaL_checknumber(L, 2); - } else { - printlogf(L, "Error", "Logging facility must be a number or string"); - exit(-1); // ERRNO; + settings.log_facility = facilitynames[ i ].c_val; } + else if (lua_isnumber(L, 2)) + { + settings.log_facility = luaL_checknumber(L, 2); + } + else + { + printlogf( + L, "Error", + "Logging facility must be a number or string" + ); - } else if (!strcmp(command, "logident")) { - const char * ident = luaL_checkstring(L, 2); - if (settings.log_ident) free(settings.log_ident); - settings.log_ident = s_strdup(ident); + exit( -1 ); + } + } + else if( !strcmp( command, "logident" ) ) + { + const char * ident = luaL_checkstring( L, 2 ); - } else { - printlogf(L, "Error", "Internal error, unknown parameter in l_configure(%s)", command); - exit(-1); //ERRNO + if (settings.log_ident) + { free(settings.log_ident); } + + settings.log_ident = s_strdup( ident ); + } + else + { + printlogf( + L, "Error", + "Internal error, unknown parameter in l_configure( %s )", + command + ); + + exit( -1 ); } return 0; } -/** - * Allows the user to observe filedescriptors - * - * @param (Lua stack) filedescriptor. - * @param (Lua stack) function to call on ready - * @param (Lua stack) function to call on writey - */ +/* +| Allows user scripts to observe filedescriptors +| +| Params on Lua stack: +| 1: file descriptor +| 2: function to call when read becomes ready +| 3: function to call when write becomes ready +*/ static int -l_observe_fd(lua_State *L) +l_observe_fd( lua_State *L ) { - int fd = luaL_checknumber(L, 1); + int fd = luaL_checknumber( L, 1 ); bool ready = false; bool writey = false; + // Stores the user function in the lua registry. // It uses the address of the cores ready/write functions // for the user as key - if (!lua_isnoneornil(L, 2)) { - lua_pushlightuserdata(L, (void *) user_obs_ready); - lua_gettable(L, LUA_REGISTRYINDEX); - if lua_isnil(L, -1) { - lua_pop(L, 1); - lua_newtable(L); - lua_pushlightuserdata(L, (void *) user_obs_ready); - lua_pushvalue(L, -2); - lua_settable(L, LUA_REGISTRYINDEX); + if( !lua_isnoneornil( L, 2 ) ) + { + lua_pushlightuserdata( L, (void *) user_obs_ready ); + + lua_gettable( L, LUA_REGISTRYINDEX ); + + if( lua_isnil( L, -1 ) ) + { + lua_pop ( L, 1 ); + lua_newtable ( L ); + lua_pushlightuserdata ( L, (void *) user_obs_ready ); + lua_pushvalue ( L, -2 ); + lua_settable ( L, LUA_REGISTRYINDEX ); } - lua_pushnumber(L, fd); - lua_pushvalue(L, 2); - lua_settable(L, -3); - lua_pop(L, 1); + + lua_pushnumber ( L, fd ); + lua_pushvalue ( L, 2 ); + lua_settable ( L, -3 ); + lua_pop ( L, 1 ); + ready = true; } - if (!lua_isnoneornil(L, 3)) { - lua_pushlightuserdata(L, (void *) user_obs_writey); - lua_gettable(L, LUA_REGISTRYINDEX); - if lua_isnil(L, -1) { - lua_pop(L, 1); - lua_newtable(L); - lua_pushlightuserdata(L, (void *) user_obs_writey); - lua_pushvalue(L, -2); - lua_settable(L, LUA_REGISTRYINDEX); + if( !lua_isnoneornil( L, 3 ) ) + { + lua_pushlightuserdata( L, (void *) user_obs_writey ); + + lua_gettable (L, LUA_REGISTRYINDEX ); + + if( lua_isnil(L, -1) ) + { + lua_pop ( L, 1 ); + lua_newtable ( L ); + lua_pushlightuserdata ( L, (void *) user_obs_writey ); + lua_pushvalue ( L, -2 ); + lua_settable ( L, LUA_REGISTRYINDEX ); } - lua_pushnumber(L, fd); - lua_pushvalue(L, 3); - lua_settable(L, -3); - lua_pop(L, 1); + + lua_pushnumber ( L, fd ); + lua_pushvalue ( L, 3 ); + lua_settable ( L, -3 ); + lua_pop ( L, 1 ); + writey = true; } - /* tells the core to watch the fd */ - observe_fd(fd, - ready ? user_obs_ready : NULL, - writey ? user_obs_writey : NULL, - user_obs_tidy, - NULL); + + // tells the core to watch the fd + observe_fd( + fd, + ready ? user_obs_ready : NULL, + writey ? user_obs_writey : NULL, + user_obs_tidy, + NULL + ); + return 0; } -/** - * Removes a user observance - * @param (Lua stack) filedescriptor. - */ +/* +| Removes a user observance +| +| Params on Lua stack: +| 1: exitcode of Lsyncd. +*/ extern int -l_nonobserve_fd(lua_State *L) +l_nonobserve_fd( lua_State *L ) { - int fd = luaL_checknumber(L, 1); + int fd = luaL_checknumber( L, 1 ); // removes the read function - lua_pushlightuserdata(L, (void *) user_obs_ready); - lua_gettable(L, LUA_REGISTRYINDEX); - if (!lua_isnil(L, -1)) { - lua_pushnumber(L, fd); - lua_pushnil(L); - lua_settable(L, -2); - } - lua_pop(L, 1); + lua_pushlightuserdata( L, (void *) user_obs_ready ); + lua_gettable( L, LUA_REGISTRYINDEX ); - lua_pushlightuserdata(L, (void *) user_obs_writey); - lua_gettable(L, LUA_REGISTRYINDEX); - if (!lua_isnil(L, -1)) { - lua_pushnumber(L, fd); - lua_pushnil(L); - lua_settable(L, -2); + if( !lua_isnil( L, -1 ) ) + { + lua_pushnumber ( L, fd ); + lua_pushnil ( L ); + lua_settable ( L, -2 ); } - lua_pop(L, 1); + lua_pop( L, 1 ); - nonobserve_fd(fd); + lua_pushlightuserdata( L, (void *) user_obs_writey ); + lua_gettable( L, LUA_REGISTRYINDEX ); + if ( !lua_isnil( L, -1 ) ) + { + lua_pushnumber ( L, fd ); + lua_pushnil ( L ); + lua_settable ( L, -2 ); + } + lua_pop( L, 1 ); + + nonobserve_fd( fd ); return 0; } -static const luaL_Reg lsyncdlib[] = { - {"configure", l_configure }, - {"exec", l_exec }, - {"log", l_log }, - {"now", l_now }, - {"nonobserve_fd", l_nonobserve_fd }, - {"observe_fd", l_observe_fd }, - {"readdir", l_readdir }, - {"realdir", l_realdir }, - {"stackdump", l_stackdump }, - {"terminate", l_terminate }, - {NULL, NULL} +/* +| The Lsnycd's core library +*/ +static const luaL_Reg lsyncdlib[] = +{ + { "configure", l_configure }, + { "exec", l_exec }, + { "log", l_log }, + { "now", l_now }, + { "nonobserve_fd", l_nonobserve_fd }, + { "observe_fd", l_observe_fd }, + { "readdir", l_readdir }, + { "realdir", l_realdir }, + { "stackdump", l_stackdump }, + { "terminate", l_terminate }, + { NULL, NULL } }; -/** - * Adds two jiffies or a number to a jiffy - */ +/* +| Adds a number in seconds to a jiffy timestamp. +*/ static int -l_jiffies_add(lua_State *L) +l_jiffies_add( lua_State *L ) { - clock_t *p1 = (clock_t *) lua_touserdata(L, 1); - clock_t *p2 = (clock_t *) lua_touserdata(L, 2); - if (p1 && p2) { - logstring("Error", "Cannot add to timestamps!"); - exit(-1); // ERRNO - } + clock_t *p1 = ( clock_t * ) lua_touserdata( L, 1 ); + clock_t *p2 = ( clock_t * ) lua_touserdata( L, 2 ); + + if( p1 && p2 ) { - clock_t a1 = p1 ? *p1 : luaL_checknumber(L, 1) * clocks_per_sec; - clock_t a2 = p2 ? *p2 : luaL_checknumber(L, 2) * clocks_per_sec; - clock_t *r = (clock_t *) lua_newuserdata(L, sizeof(clock_t)); - luaL_getmetatable(L, "Lsyncd.jiffies"); - lua_setmetatable(L, -2); + logstring( "Error", "Cannot add two timestamps!" ); + exit( -1 ); + } + + { + clock_t a1 = + p1 ? *p1 : luaL_checknumber( L, 1 ) * clocks_per_sec; + + clock_t a2 = + p2 ? *p2 : luaL_checknumber( L, 2 ) * clocks_per_sec; + + clock_t *r = + ( clock_t * ) lua_newuserdata( L, sizeof( clock_t ) ); + + luaL_getmetatable( L, "Lsyncd.jiffies" ); + lua_setmetatable( L, -2 ); *r = a1 + a2; return 1; } } -/** - * Adds two jiffies or a number to a jiffy - */ +/* +| Subracts two jiffy timestamps resulting in a number in seconds +| or substracts a jiffy by a number in seconds resulting a jiffy timestamp. +*/ static int -l_jiffies_sub(lua_State *L) +l_jiffies_sub( lua_State *L ) { - clock_t *p1 = (clock_t *) lua_touserdata(L, 1); - clock_t *p2 = (clock_t *) lua_touserdata(L, 2); - if (p1 && p2) { + clock_t *p1 = ( clock_t * ) lua_touserdata( L, 1 ); + clock_t *p2 = ( clock_t * ) lua_touserdata( L, 2 ); + + if( p1 && p2 ) + { // substracting two timestamps result in a timespan in seconds clock_t a1 = *p1; clock_t a2 = *p2; lua_pushnumber(L, ((double) (a1 -a2)) / clocks_per_sec); return 1; } + // makes a timestamp earlier by NUMBER seconds - clock_t a1 = p1 ? *p1 : luaL_checknumber(L, 1) * clocks_per_sec; - clock_t a2 = p2 ? *p2 : luaL_checknumber(L, 2) * clocks_per_sec; - clock_t *r = (clock_t *) lua_newuserdata(L, sizeof(clock_t)); - luaL_getmetatable(L, "Lsyncd.jiffies"); - lua_setmetatable(L, -2); + clock_t a1 = p1 ? *p1 : luaL_checknumber( L, 1 ) * clocks_per_sec; + clock_t a2 = p2 ? *p2 : luaL_checknumber( L, 2 ) * clocks_per_sec; + + clock_t *r = (clock_t *) lua_newuserdata( L, sizeof( clock_t ) ); + luaL_getmetatable( L, "Lsyncd.jiffies" ); + lua_setmetatable( L, -2 ); + *r = a1 - a2; + return 1; } -/** - * Substracts two jiffies or a number to a jiffy - */ +/* +| Compares two jiffy timestamps +*/ static int -l_jiffies_eq(lua_State *L) +l_jiffies_eq( lua_State *L ) { - clock_t a1 = (*(clock_t *) luaL_checkudata(L, 1, "Lsyncd.jiffies")); - clock_t a2 = (*(clock_t *) luaL_checkudata(L, 2, "Lsyncd.jiffies")); - lua_pushboolean(L, a1 == a2); + clock_t a1 = ( *( clock_t * ) luaL_checkudata( L, 1, "Lsyncd.jiffies" ) ); + clock_t a2 = ( *( clock_t * ) luaL_checkudata( L, 2, "Lsyncd.jiffies" ) ); + + lua_pushboolean( L, a1 == a2 ); + return 1; } -/** - * True if jiffy1 before jiffy2 - */ +/* +* True if jiffy1 timestamp is eariler than jiffy2 timestamp +*/ static int -l_jiffies_lt(lua_State *L) +l_jiffies_lt( lua_State *L ) { - clock_t a1 = (*(clock_t *) luaL_checkudata(L, 1, "Lsyncd.jiffies")); - clock_t a2 = (*(clock_t *) luaL_checkudata(L, 2, "Lsyncd.jiffies")); - lua_pushboolean(L, time_before(a1, a2)); + clock_t a1 = ( *( clock_t * ) luaL_checkudata( L, 1, "Lsyncd.jiffies" ) ); + clock_t a2 = ( *( clock_t * ) luaL_checkudata( L, 2, "Lsyncd.jiffies" ) ); + + lua_pushboolean( L, time_before( a1, a2 ) ); + return 1; } -/** - * True if jiffy1 before or == jiffy2 - */ +/* +| True if jiffy1 before or equals jiffy2 +*/ static int l_jiffies_le(lua_State *L) { - clock_t a1 = (*(clock_t *) luaL_checkudata(L, 1, "Lsyncd.jiffies")); - clock_t a2 = (*(clock_t *) luaL_checkudata(L, 2, "Lsyncd.jiffies")); - lua_pushboolean(L, (a1 == a2) || time_before(a1, a2)); + clock_t a1 = ( *( clock_t * ) luaL_checkudata( L, 1, "Lsyncd.jiffies" ) ); + clock_t a2 = ( *( clock_t * ) luaL_checkudata( L, 2, "Lsyncd.jiffies" ) ); + + lua_pushboolean( L, ( a1 == a2 ) || time_before( a1, a2 ) ); return 1; } -/** - * Registers the lsyncd lib - */ +/* +| Registers the Lsyncd's core library. +*/ void -register_lsyncd(lua_State *L) +register_lsyncd( lua_State *L ) { - luaL_register(L, LSYNCD_LIBNAME, lsyncdlib); - lua_setglobal(L, LSYNCD_LIBNAME); + luaL_register( L, LSYNCD_LIBNAME, lsyncdlib ); + lua_setglobal( L, LSYNCD_LIBNAME ); - // creates the metatable for jiffies userdata - luaL_newmetatable(L, "Lsyncd.jiffies"); - int mt = lua_gettop(L); + // creates the metatable for the jiffies ( timestamps ) userdata + luaL_newmetatable( L, "Lsyncd.jiffies" ); + int mt = lua_gettop( L ); - lua_pushcfunction(L, l_jiffies_add); - lua_setfield(L, mt, "__add"); + lua_pushcfunction( L, l_jiffies_add ); + lua_setfield( L, mt, "__add" ); - lua_pushcfunction(L, l_jiffies_sub); - lua_setfield(L, mt, "__sub"); + lua_pushcfunction( L, l_jiffies_sub ); + lua_setfield( L, mt, "__sub" ); - lua_pushcfunction(L, l_jiffies_lt); - lua_setfield(L, mt, "__lt"); + lua_pushcfunction( L, l_jiffies_lt ); + lua_setfield( L, mt, "__lt" ); - lua_pushcfunction(L, l_jiffies_le); - lua_setfield(L, mt, "__le"); + lua_pushcfunction( L, l_jiffies_le ); + lua_setfield( L, mt, "__le" ); - lua_pushcfunction(L, l_jiffies_eq); - lua_setfield(L, mt, "__eq"); + lua_pushcfunction( L, l_jiffies_eq ); + lua_setfield( L, mt, "__eq" ); - lua_pop(L, 1); // pop(mt) + lua_pop( L, 1 ); // pop(mt) #ifdef LSYNCD_WITH_INOTIFY - lua_getglobal(L, LSYNCD_LIBNAME); - register_inotify(L); - lua_setfield(L, -2, LSYNCD_INOTIFYLIBNAME); - lua_pop(L, 1); + + lua_getglobal( L, LSYNCD_LIBNAME ); + register_inotify( L ); + lua_setfield( L, -2, LSYNCD_INOTIFYLIBNAME ); + lua_pop( L, 1 ); + #endif - if (lua_gettop(L)) { - logstring("Error", "internal, stack not empty in lsyncd_register()"); - exit(-1); // ERRNO + if( lua_gettop( L ) ) + { + logstring( + "Error", + "internal, stack not empty in lsyncd_register( )" + ); + + exit( -1 ); } } +/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~* +( Lsyncd Core ) + *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/ -/***************************************************************************** - * Lsyncd Core -****************************************************************************/ -/** - * Pushes a function from the runner on the stack. - * Prior it pushed the callError handler. - */ +/* +| Pushes a function from the runner on the stack. +| As well as the callError handler. +*/ extern void -load_runner_func(lua_State *L, - const char *name) +load_runner_func( + lua_State * L, + const char * name +) { - printlogf(L, "Call", "%s()", name); + printlogf( L, "Call", "%s( )", name ); // pushes the error handler - lua_pushlightuserdata(L, (void *) &callError); - lua_gettable(L, LUA_REGISTRYINDEX); + lua_pushlightuserdata( L, (void *) &callError ); + lua_gettable( L, LUA_REGISTRYINDEX ); // pushes the function - lua_pushlightuserdata(L, (void *) &runner); - lua_gettable(L, LUA_REGISTRYINDEX); - lua_pushstring(L, name); - lua_gettable(L, -2); - lua_remove(L, -2); + lua_pushlightuserdata( L, (void *) &runner ); + lua_gettable( L, LUA_REGISTRYINDEX ); + lua_pushstring( L, name ); + lua_gettable( L, -2 ); + lua_remove( L, -2 ); } -/** - * Daemonizes. - * - * Lsyncds own implementation over daemon(0, 0) since - * a) OSX keeps bugging about it being deprecated - * b) for reason since blindly closing stdin/out/err is unsafe, since - * they might not have existed and actually close the monitors fd! - */ +/* +| Daemonizes. +| +| Lsyncds own implementation over daemon(0, 0) since +| a) OSX keeps bugging about it being deprecated +| b) for a reason, since blindly closing stdin/out/err +| is unsafe, since they might not have existed and +| might actually close the monitors fd! +*/ static void -daemonize(lua_State *L) +daemonize( lua_State *L ) { pid_t pid, sid; - pid = fork(); + pid = fork( ); - if (pid < 0) { - printlogf(L, "Error", "Failure in daemonize at fork: %s", strerror(errno)); - exit(-1); // ERRNO + if( pid < 0 ) + { + printlogf( + L, "Error", + "Failure in daemonize at fork: %s", + strerror( errno ) + ); + + exit( -1 ); } - if (pid > 0) exit(0); // return parent to shell - - sid = setsid(); - if (sid < 0) { - printlogf(L, "Error", "Failure in daemonize at setsid: %s", strerror(errno)); - exit(-1); // ERRNO + if (pid > 0) + { + // parent process returns to shell + exit( 0 ); } - // goto root dir - if ((chdir("/")) < 0) { - printlogf(L, "Error", "Failure in daemonize at chdir(\"/\"): %s", strerror(errno)); - exit(-1); // ERRNO + // detaches the new process from the parent process + sid = setsid( ); + if( sid < 0 ) + { + printlogf( + L, "Error", + "Failure in daemonize at setsid: %s", + strerror( errno ) + ); + + exit( -1 ); } - // does what clibs daemon(0, 0) cannot do, - // checks if there were no stdstreams and it might close used fds! - if (observances_len && observances->fd < 3) { - printlogf(L, "Normal", - "daemonize not closing stdin/out/err, since there seem to none."); + // goes to root dir + if( chdir( "/" ) < 0 ) + { + printlogf( + L, "Error", + "Failure in daemonize at chdir( \"/\" ): %s", + strerror( errno ) + ); + + exit( -1 ); + } + + // does what clibs daemon( 0, 0 ) cannot do, + // checks if there were no stdstreams and it might close used fds + if( observances_len && observances->fd < 3 ) + { + printlogf( + L, "Normal", + "daemonize not closing stdin/out/err, since there seem to none." + ); + return; } // disconnects stdstreams - if (!freopen("/dev/null", "r", stdin) || - !freopen("/dev/null", "w", stdout) || - !freopen("/dev/null", "w", stderr) - ) { - printlogf(L, "Error", "Failure in daemonize at freopen(/dev/null, std[in|out|err])"); + if ( + !freopen( "/dev/null", "r", stdin ) || + !freopen( "/dev/null", "w", stdout ) || + !freopen( "/dev/null", "w", stderr ) + ) + { + printlogf( + L, "Error", + "Failure in daemonize at freopen( /dev/null, std[in|out|err] )" + ); } is_daemon = true; } -/** - * Normal operation happens in here. - */ + +/* +| Normal operation happens in here. +*/ static void masterloop(lua_State *L) { - while(true) { + while( true ) + { bool have_alarm; bool force_alarm = false; - clock_t now = times(dummy_tms); + clock_t now = times( dummy_tms ); clock_t alarm_time = 0; // memory usage debugging - // lua_gc(L, LUA_GCCOLLECT, 0); - // printf("gccount: %d\n", lua_gc(L, LUA_GCCOUNT, 0) * 1024 + lua_gc(L, LUA_GCCOUNTB, 0)); + // lua_gc( L, LUA_GCCOLLECT, 0 ); + // printf( + // "gccount: %d\n", + // lua_gc( L, LUA_GCCOUNT, 0 ) * 1024 + lua_gc( L, LUA_GCCOUNTB, 0 ) ); - // queries runner about soonest alarm - load_runner_func(L, "getAlarm"); - if (lua_pcall(L, 0, 1, -2)) exit(-1); // ERRNO + // + // queries the runner about the soonest alarm + // + load_runner_func( L, "getAlarm" ); + if( lua_pcall( L, 0, 1, -2 ) ) + { exit( -1 ); } - if (lua_type(L, -1) == LUA_TBOOLEAN) { - have_alarm = false; - force_alarm = lua_toboolean(L, -1); - } else { - have_alarm = true; - alarm_time = *((clock_t *) luaL_checkudata(L, -1, "Lsyncd.jiffies")); + if( lua_type( L, -1 ) == LUA_TBOOLEAN) + { + have_alarm = false; + force_alarm = lua_toboolean( L, -1 ); } - lua_pop(L, 2); + else + { + have_alarm = true; + alarm_time = *( ( clock_t * ) luaL_checkudata( L, -1, "Lsyncd.jiffies" ) ); + } + lua_pop( L, 2 ); - if (force_alarm || (have_alarm && time_before_eq(alarm_time, now))) { + if( + force_alarm || + ( have_alarm && time_before_eq( alarm_time, now ) ) + ) + { // there is a delay that wants to be handled already thus instead // of reading/writing from observances it jumps directly to // handling // TODO: Actually it might be smarter to handler observances // eitherway. since event queues might overflow. - logstring("Masterloop", "immediately handling delays."); - } else { - // use select() to determine what happens next - // + a new event on an observance - // + an alarm on timeout - // + the return of a child process + logstring( "Masterloop", "immediately handling delays." ); + } + else + { + // uses select( ) to determine what happens next: + // a) a new event on an observance + // b) an alarm on timeout + // c) the return of a child process struct timespec tv; - if (have_alarm) { + if( have_alarm ) + { // TODO use trunc instead of long converstions - double d = ((double)(alarm_time - now)) / clocks_per_sec; + double d = ( (double )( alarm_time - now ) ) / clocks_per_sec; tv.tv_sec = d; - tv.tv_nsec = ((d - (long) d)) * 1000000000.0; - printlogf(L, "Masterloop", - "going into select (timeout %f seconds)", d); - } else { - logstring("Masterloop", "going into select (no timeout)."); + tv.tv_nsec = ( (d - ( long ) d) ) * 1000000000.0; + printlogf( + L, "Masterloop", + "going into select ( timeout %f seconds )", + d + ); } - // time for Lsyncd to try to put itself to rest into a select(), - // configures timeouts, filedescriptors and signals - // that will wake it + else + { + logstring( + "Masterloop", + "going into select ( no timeout )" + ); + } + + // time for Lsyncd to try to put itself to rest into the big select( ) + // this configures: + // timeouts, + // filedescriptors and + // signals + // that will wake Lsyncd { fd_set rfds; fd_set wfds; sigset_t sigset; int pi, pr; - sigemptyset(&sigset); - FD_ZERO(&rfds); - FD_ZERO(&wfds); + sigemptyset( &sigset ); + FD_ZERO( &rfds ); + FD_ZERO( &wfds ); - for(pi = 0; pi < observances_len; pi++) { + for( pi = 0; pi < observances_len; pi++ ) + { struct observance *obs = observances + pi; - if (obs->ready) FD_SET(obs->fd, &rfds); - if (obs->writey) FD_SET(obs->fd, &wfds); + if ( obs->ready ) + { + FD_SET( obs->fd, &rfds ); + } + + if ( obs->writey ) + { + FD_SET( obs->fd, &wfds ); + } } - if (!observances_len) { - logstring("Error", - "Internal fail, no observances, no monitor!"); - exit(-1); + if( !observances_len ) + { + logstring( + "Error", + "Internal fail, no observances, no monitor!" + ); + + exit( -1 ); } - // the great select, this is the very heart beat + + // the great select, this is the very heart beat of Lsyncd + // that puts Lsyncd to sleep until anything worth noticing + // happens + pr = pselect( - observances[observances_len - 1].fd + 1, - &rfds, &wfds, NULL, - have_alarm ? &tv : NULL, &sigset); - if (pr >= 0) { + observances[ observances_len - 1 ].fd + 1, + &rfds, + &wfds, + NULL, + have_alarm ? &tv : NULL, + &sigset + ); + + // something happened! + + if (pr >= 0) + { // walks through the observances calling ready/writey observance_action = true; - for(pi = 0; pi < observances_len; pi++) { + for( pi = 0; pi < observances_len; pi++ ) + { struct observance *obs = observances + pi; - if (hup || term) break; - if (obs->ready && FD_ISSET(obs->fd, &rfds)) { + // Checks for signals + if( hup || term ) + { break; } + + // a file descriptor became read-ready + if( obs->ready && FD_ISSET( obs->fd, &rfds ) ) + { obs->ready(L, obs); } - if (hup || term) break; + // Checks for signals, again, better safe than sorry + if (hup || term) + { break; } - if (nonobservances_len > 0 && - nonobservances[nonobservances_len-1] == obs->fd) { - // TODO breaks if more nonobserves - // ready() nonobserved itself - // --- what? + // FIXME breaks on multiple nonobservances in one beat + if( + nonobservances_len > 0 && + nonobservances[ nonobservances_len - 1 ] == obs->fd + ) + { continue; } - if (obs->writey && FD_ISSET(obs->fd, &wfds)) { - obs->writey(L, obs); + + // a file descriptor became write-ready + if( obs->writey && FD_ISSET( obs->fd, &wfds ) ) + { + obs->writey( L, obs ); } } + observance_action = false; - // work through delayed nonobserve_fd() calls - for (pi = 0; pi < nonobservances_len; pi++) { + + // works through delayed nonobserve_fd() calls + for (pi = 0; pi < nonobservances_len; pi++) + { nonobserve_fd(nonobservances[pi]); } + nonobservances_len = 0; } } } // collects zombified child processes - while(1) { + while( 1 ) + { int status; - pid_t pid = waitpid(0, &status, WNOHANG); - if (pid <= 0) break; + pid_t pid = waitpid( 0, &status, WNOHANG ); - load_runner_func(L, "collectProcess"); - lua_pushinteger(L, pid); - lua_pushinteger(L, WEXITSTATUS(status)); - if (lua_pcall(L, 2, 0, -4)) { - exit(-1); // ERRNO + if (pid <= 0) + { + // no more zombies + break; } - lua_pop(L, 1); + + // calls the runner to handle the collection + load_runner_func( L, "collectProcess" ); + lua_pushinteger( L, pid ); + lua_pushinteger( L, WEXITSTATUS( status ) ); + + if ( lua_pcall( L, 2, 0, -4 ) ) + { exit(-1); } + + lua_pop( L, 1 ); } - // reacts on HUP signal - if (hup) { - load_runner_func(L, "hup"); - if (lua_pcall(L, 0, 0, -2)) { - exit(-1); // ERRNO + // reacts on HUP signals + if( hup ) + { + load_runner_func( L, "hup" ); + if( lua_pcall( L, 0, 0, -2 ) ) + { + exit( -1 ); } - lua_pop(L, 1); + lua_pop( L, 1 ); hup = 0; } - // reacts on TERM signal - if (term == 1) { - load_runner_func(L, "term"); - if (lua_pcall(L, 0, 0, -2)) { - exit(-1); // ERRNO + // reacts on TERM signals + if( term == 1 ) + { + load_runner_func( L, "term" ); + if( lua_pcall( L, 0, 0, -2 ) ) + { + exit( -1 ); } - lua_pop(L, 1); + lua_pop( L, 1 ); term = 2; } // lets the runner do stuff every cycle, // like starting new processes, writing the statusfile etc. - load_runner_func(L, "cycle"); - l_now(L); - if (lua_pcall(L, 1, 1, -3)) exit(-1); // ERRNO - if (!lua_toboolean(L, -1)) { + load_runner_func( L, "cycle" ); + l_now( L ); + if( lua_pcall( L, 1, 1, -3 ) ) + { exit( -1 ); } + + if( !lua_toboolean( L, -1 ) ) + { // cycle told core to break mainloop - lua_pop(L, 2); + lua_pop( L, 2 ); return; } - lua_pop(L, 2); + lua_pop( L, 2 ); - if (lua_gettop(L)) { - logstring("Error", "internal, stack is dirty.") - l_stackdump(L); - exit(-1); // ERRNO + if( lua_gettop( L ) ) + { + logstring( + "Error", + "internal, stack is dirty." + ); + l_stackdump( L ); + exit( -1 ); } } } -/** - * The effective main for one run. - * HUP signals may cause several runs of the one main. - */ +/* +| The effective main for one run. +| HUP signals may cause several runs of the one main. +*/ int -main1(int argc, char *argv[]) +main1( int argc, char *argv[] ) { // the Lua interpreter - lua_State* L; + lua_State * L; - // scripts + // the runner file char * lsyncd_runner_file = NULL; int argp = 1; // load Lua - L = luaL_newstate(); - luaL_openlibs(L); + L = luaL_newstate( ); + luaL_openlibs( L ); { // checks the lua version - const char *version; + const char * version; int major, minor; - lua_getglobal(L, "_VERSION"); - version = luaL_checkstring(L, -1); - if (sscanf(version, "Lua %d.%d", &major, &minor) != 2) { - fprintf(stderr, "cannot parse lua library version!\n"); - exit(-1); // ERRNO + lua_getglobal( L, "_VERSION" ); + version = luaL_checkstring( L, -1 ); + + if( + sscanf( + version, + "Lua %d.%d", + &major, &minor + ) != 2 + ) + { + fprintf( + stderr, + "cannot parse lua library version!\n" + ); + exit (-1 ); } - if ((major < 5) || (major == 5 && minor < 1)) { - fprintf(stderr, "lua library is too old. Need 5.1 at least"); - exit(-1); // ERRNO + + if( + major < 5 || + (major == 5 && minor < 1) + ) { + fprintf( + stderr, + "Lua library is too old. Needs 5.1 at least" + ); + exit( -1 ); } - lua_pop(L, 1); + + lua_pop( L, 1 ); } { - // prepares logging early + // logging is prepared quite early int i = 1; - add_logcat("Normal", LOG_NOTICE); - add_logcat("Warn", LOG_WARNING); - add_logcat("Error", LOG_ERR); - while (i < argc) { - if (strcmp(argv[i], "-log") && strcmp(argv[i], "--log")) { - i++; continue; + add_logcat( "Normal", LOG_NOTICE ); + add_logcat( "Warn", LOG_WARNING ); + add_logcat( "Error", LOG_ERR ); + + while( i < argc ) + { + if( + strcmp( argv[ i ], "-log" ) && + strcmp( argv[ i ], "--log" ) + ) + { + // arg is neither -log or --log + i++; + continue; } - if (++i >= argc) break; + if( ++i >= argc ) + { + // -(-)log was last argument + break; + } - if (!add_logcat(argv[i], LOG_NOTICE)) { - printlogf(L, "Error", "'%s' is not a valid logging category", argv[i]); - exit(-1); // ERRNO + if( !add_logcat( argv[ i ], LOG_NOTICE ) ) + { + printlogf( + L, "Error", + "'%s' is not a valid logging category", + argv[ i ] + ); + exit( -1 ); } } } - // registers lsycnd core - register_lsyncd(L); + // registers Lsycnd's core library + register_lsyncd( L ); - if (check_logcat("Debug") <= settings.log_level) { + + if( check_logcat( "Debug" ) <= settings.log_level ) + { // printlogf doesnt support %ld :-( - printf("kernels clocks_per_sec=%ld\n", clocks_per_sec); + printf( + "kernels clocks_per_sec=%ld\n", + clocks_per_sec + ); } - // checks if the user overrode default runner file - if (argp < argc && !strcmp(argv[argp], "--runner")) { - if (argp + 1 >= argc) { - logstring("Error", "Lsyncd Lua-runner file missing after --runner."); - logstring("Error", "Using a statically included runner as default."); - exit(-1); //ERRNO + // checks if the user overrode the default runner file + if( + argp < argc && + !strcmp( argv[ argp ], "--runner" ) + ) + { + if (argp + 1 >= argc) + { + logstring( + "Error", + "Lsyncd Lua-runner file missing after --runner " + ); + + exit( -1 ); } - lsyncd_runner_file = argv[argp + 1]; + + lsyncd_runner_file = argv[ argp + 1 ]; argp += 2; } - if (lsyncd_runner_file) { + if( lsyncd_runner_file ) + { // checks if the runner file exists struct stat st; - if (stat(lsyncd_runner_file, &st)) { - printlogf(L, "Error", "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]); - exit(-1); // ERRNO + + if( stat( lsyncd_runner_file, &st ) ) + { + printlogf( + L, "Error", + "Cannot see a runner at '%s'.", + lsyncd_runner_file + ); + exit( -1 ); } // 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)); - exit(-1); // ERRNO + if( luaL_loadfile(L, lsyncd_runner_file ) ) + { + printlogf( + L, "Error", + "error loading '%s': %s", + lsyncd_runner_file, + lua_tostring( L, -1 ) + ); + + exit( -1 ); } - } else { + + } + else + { // loads the runner from binary - if (luaL_loadbuffer(L, runner_out, runner_size, "runner")) { - printlogf(L, "Error", "loading precompiled runner: %s", lua_tostring(L, -1)); - exit(-1); // ERRNO + if( luaL_loadbuffer( L, runner_out, runner_size, "runner" ) ) + { + printlogf( + L, "Error", + "error loading precompiled runner: %s", + lua_tostring( L, -1 ) + ); + + exit( -1 ); } } + // prepares the runner executing the script { - // place to store the lua runners functions - // executes the runner defining all its functions - if (lua_pcall(L, 0, LUA_MULTRET, 0)) { - printlogf(L, "Error", "preparing runner: %s", lua_tostring(L, -1)); - exit(-1); // ERRNO + if( lua_pcall( L, 0, LUA_MULTRET, 0 ) ) + { + printlogf( + L, "Error", + "preparing runner: %s", + lua_tostring( L, -1 ) + ); + + exit( -1 ); } - lua_pushlightuserdata(L, (void *)&runner); - // switches the value (result of preparing) and the key &runner - lua_insert(L, 1); + + lua_pushlightuserdata( L, (void *) & runner ); + + // switches the value ( result of preparing ) and the key &runner + lua_insert( L, 1 ); + // saves the table of the runners functions in the lua registry - lua_settable(L, LUA_REGISTRYINDEX); + lua_settable( L, LUA_REGISTRYINDEX ); - // saves the error function extra - lua_pushlightuserdata(L, (void *) &callError); // &callError is the key - lua_pushlightuserdata(L, (void *) &runner); // &runner[callError] the value - lua_gettable(L, LUA_REGISTRYINDEX); - lua_pushstring(L, "callError"); - lua_gettable(L, -2); - lua_remove(L, -2); - lua_settable(L, LUA_REGISTRYINDEX); + // saves the error function extras + + // &callError is the key + lua_pushlightuserdata ( L, (void *) &callError ); + + // &runner[ callError ] the value + lua_pushlightuserdata ( L, (void *) &runner ); + lua_gettable ( L, LUA_REGISTRYINDEX ); + lua_pushstring ( L, "callError" ); + lua_gettable ( L, -2 ); + lua_remove ( L, -2 ); + + lua_settable ( L, LUA_REGISTRYINDEX ); } + // asserts the Lsyncd's version matches + // between runner and core { - // asserts version match between runner and core const char *lversion; - lua_getglobal(L, "lsyncd_version"); - lversion = luaL_checkstring(L, -1); - if (strcmp(lversion, PACKAGE_VERSION)) { - printlogf(L, "Error", + + lua_getglobal( L, "lsyncd_version" ); + lversion = luaL_checkstring( L, -1 ); + + if( strcmp( lversion, PACKAGE_VERSION ) ) + { + printlogf( + L, "Error", "Version mismatch '%s' is '%s', but core is '%s'", - lsyncd_runner_file ? lsyncd_runner_file : "internal runner", - lversion, PACKAGE_VERSION); - exit(-1); // ERRNO + lsyncd_runner_file ? lsyncd_runner_file : "( internal runner )", + lversion, PACKAGE_VERSION + ); + + exit( -1 ); } - lua_pop(L, 1); + + lua_pop( L, 1 ); } + // loads the defaults from binary { - // loads the defaults from binary - if (luaL_loadbuffer(L, defaults_out, defaults_size, "defaults")) { - printlogf(L, "Error", "loading defaults: %s", lua_tostring(L, -1)); - exit(-1); // ERRNO + if( luaL_loadbuffer( L, defaults_out, defaults_size, "defaults" ) ) + { + printlogf( + L, "Error", + "loading defaults: %s", + lua_tostring( L, -1 ) + ); + + exit( -1 ); } // prepares the defaults - if (lua_pcall(L, 0, 0, 0)) { - printlogf(L, "Error", "preparing defaults: %s", lua_tostring(L, -1)); - exit(-1); // ERRNO + if (lua_pcall( L, 0, 0, 0 ) ) + { + printlogf( + L, "Error", + "preparing defaults: %s", + lua_tostring( L, -1 ) + ); + exit( -1 ); } } + // checks if there is a "-help" or "--help" { - // checks if there is a "-help" or "--help" int i; - for(i = argp; i < argc; i++) { - if (!strcmp(argv[i],"-help") || !strcmp(argv[i],"--help")) { + for( i = argp; i < argc; i++ ) + { + if ( + !strcmp( argv[ i ], "-help" ) || + !strcmp( argv[ i ], "--help" ) + ) + { load_runner_func(L, "help"); - if (lua_pcall(L, 0, 0, -2)) { - exit(-1); // ERRNO - } - lua_pop(L, 1); - exit(-1); // ERRNO + + if (lua_pcall(L, 0, 0, -2)) + { exit( -1 ); } + + lua_pop( L, 1 ); + exit( 0 ); } } } + // starts the option parser in Lua script { - // starts the option parser in lua script int idx = 1; const char *s; + // creates a table with all remaining argv option arguments - load_runner_func(L, "configure"); - lua_newtable(L); - while(argp < argc) { - lua_pushnumber(L, idx++); - lua_pushstring(L, argv[argp++]); - lua_settable(L, -3); + load_runner_func( L, "configure" ); + lua_newtable( L ); + + while( argp < argc ) + { + lua_pushnumber ( L, idx++ ); + lua_pushstring ( L, argv[ argp++ ] ); + lua_settable ( L, -3 ); } + // creates a table with the cores event monitor interfaces idx = 0; - lua_newtable(L); - while (monitors[idx]) { - lua_pushnumber(L, idx + 1); - lua_pushstring(L, monitors[idx++]); - lua_settable(L, -3); + lua_newtable( L ); + + while( monitors[ idx ] ) + { + lua_pushnumber ( L, idx + 1 ); + lua_pushstring ( L, monitors[ idx++ ] ); + lua_settable ( L, -3 ); } - if (lua_pcall(L, 2, 1, -3)) { - exit(-1); // ERRNO - } - if (first_time) { + + if( lua_pcall( L, 2, 1, -3 ) ) + { exit( -1 ); } + + if( first_time ) + { // If not first time, simply retains the config file given s = lua_tostring(L, -1); - if (s) lsyncd_config_file = s_strdup(s); + if( s ) + { + lsyncd_config_file = s_strdup( s ); + } } - lua_pop(L, 2); + lua_pop( L, 2 ); } // checks existence of the config file - if (lsyncd_config_file) { + if( lsyncd_config_file ) + { struct stat st; // gets the absolute path to the config file // so in case of HUPing the daemon, it finds it again - char * apath = get_realpath(lsyncd_config_file); - if (!apath) { - printlogf(L, "Error", "Cannot find config file at '%s'.", lsyncd_config_file); - exit(-1); // ERRNO + char * apath = get_realpath( lsyncd_config_file ); + if( !apath ) + { + printlogf( + L, "Error", + "Cannot find config file at '%s'.", + lsyncd_config_file + ); + + exit( -1 ); } - free(lsyncd_config_file); + + free( lsyncd_config_file ); lsyncd_config_file = apath; - if (stat(lsyncd_config_file, &st)) { - printlogf(L, "Error", "Cannot find config file at '%s'.", lsyncd_config_file); - exit(-1); // ERRNO + if( stat( lsyncd_config_file, &st ) ) + { + printlogf( + L, "Error", + "Cannot find config file at '%s'.", + lsyncd_config_file + ); + + exit( -1 ); } // loads and executes the config file - if (luaL_loadfile(L, lsyncd_config_file)) { - printlogf(L, "Error", - "error loading %s: %s", lsyncd_config_file, lua_tostring(L, -1)); - exit(-1); // ERRNO + if( luaL_loadfile( L, lsyncd_config_file ) ) + { + printlogf( + L, "Error", + "error loading %s: %s", + lsyncd_config_file, + lua_tostring( L, -1 ) + ); + + exit( -1 ); } - if (lua_pcall(L, 0, LUA_MULTRET, 0)) { - printlogf(L, "Error", - "error preparing %s: %s", lsyncd_config_file, lua_tostring(L, -1)); - exit(-1); // ERRNO + if( lua_pcall( L, 0, LUA_MULTRET, 0) ) + { + printlogf( + L, "Error", + "error preparing %s: %s", + lsyncd_config_file, + lua_tostring( L, -1 ) + ); + + exit( -1 ); } } #ifdef LSYNCD_WITH_INOTIFY - open_inotify(L); + + open_inotify( L ); + #endif + #ifdef LSYNCD_WITH_FSEVENTS - open_fsevents(L); + + open_fsevents( L ); + #endif + // adds signal handlers + // listens to SIGCHLD, but blocks it until pselect( ) + // opens the signal handler up { - /* adds signal handlers * - * listens to SIGCHLD, but blocks it until pselect() - * opens up*/ sigset_t set; - sigemptyset(&set); - sigaddset(&set, SIGCHLD); - signal(SIGCHLD, sig_child); - sigprocmask(SIG_BLOCK, &set, NULL); + sigemptyset( &set ); + sigaddset( &set, SIGCHLD ); + signal( SIGCHLD, sig_child ); + sigprocmask( SIG_BLOCK, &set, NULL ); - signal(SIGHUP, sig_handler); - signal(SIGTERM, sig_handler); + signal( SIGHUP, sig_handler ); + signal( SIGTERM, sig_handler ); } + // runs initializations from runner + // it will set the configuration and add watches { - /* runs initialitions from runner - * lua code will set configuration and add watches */ - load_runner_func(L, "initialize"); - lua_pushboolean(L, first_time); - if (lua_pcall(L, 1, 0, -3)) exit(-1); // ERRNO - lua_pop(L, 1); + load_runner_func( L, "initialize" ); + lua_pushboolean( L, first_time ); + + if( lua_pcall( L, 1, 0, -3 ) ) + { exit( -1 ); } + + lua_pop( L, 1 ); } - masterloop(L); + // + // enters the master loop + // + masterloop( L ); + // // cleanup + // + + // tidies up all observances { - // tidies up all observances int i; - for(i = 0; i < observances_len; i++) { + for( i = 0; i < observances_len; i++ ) + { struct observance *obs = observances + i; - obs->tidy(obs); + obs->tidy( obs ); } - observances_len = 0; + + observances_len = 0; nonobservances_len = 0; } + // frees logging categories { - // frees logging categories int ci; struct logcat *lc; - for(ci = 'A'; ci <= 'Z'; ci++) { - for(lc = logcats[ci - 'A']; lc && lc->name; lc++) { - free(lc->name); + for( ci = 'A'; ci <= 'Z'; ci++ ) + { + for( lc = logcats[ ci - 'A' ]; lc && lc->name; lc++) + { + free( lc->name ); lc->name = NULL; } - if (logcats[ci - 'A']) { - free(logcats[ci - 'A']); - logcats[ci - 'A'] = NULL; + + if( logcats[ci - 'A' ] ) + { + free( logcats[ ci - 'A' ] ); + logcats[ ci - 'A' ] = NULL; } } } - lua_close(L); + lua_close( L ); return 0; } -/** - * Main - */ +/* +| Main +*/ int -main(int argc, char *argv[]) +main( int argc, char * argv[ ] ) { // gets a kernel parameter - clocks_per_sec = sysconf(_SC_CLK_TCK); + clocks_per_sec = sysconf( _SC_CLK_TCK ); - while(!term) main1(argc, argv); - return 0; + while( !term ) { + main1( argc, argv ); + } + + // exits with 143 since it got a kill signal + return 143; } From 1217fef9ba5e9aee6cec744f7a3c6a4168f9c6ec Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Sun, 7 Oct 2012 19:49:33 +0200 Subject: [PATCH 17/28] fixing default.direct regression --- default-direct.lua | 21 +++++++++++++++++++++ default.lua | 3 +-- tests/schedule.lua | 2 +- 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/default-direct.lua b/default-direct.lua index 29b6c08..74f5f28 100644 --- a/default-direct.lua +++ b/default-direct.lua @@ -34,6 +34,21 @@ local direct = { } default.direct = direct + +-- +-- known configuration parameters +-- +direct.checkgauge = { + -- + -- inherits rsync config params + -- + default.rsync.checkgauge, + + rsyncExitCodes = true, + onMove = true, +} + + -- -- Spawns rsync for a list of events -- @@ -154,6 +169,12 @@ direct.delay = 1 -- direct.onMove = true +-- +-- Rsync configuration for startup. +-- +direct.rsync = default.rsync.rsync +direct.rsyncExitCodes = default.rsyncExitCodes + -- -- By default do deletes. -- diff --git a/default.lua b/default.lua index b801c56..f3c1191 100644 --- a/default.lua +++ b/default.lua @@ -48,9 +48,8 @@ default.checkgauge = { onStartup = true, onMove = true, prepare = true, - -- rsyncExitCodes = true, -- TODO source = true, - -- sshExitCodes = true -- TODO + target = true, } -- diff --git a/tests/schedule.lua b/tests/schedule.lua index 074468a..f00b54e 100755 --- a/tests/schedule.lua +++ b/tests/schedule.lua @@ -47,7 +47,7 @@ sync {ccircuit, source ="]]..srcdir..[[", target = "]]..trgdir..[["} -- test if the filename exists, fails if this is different to expect -local function testfile(filename) +local function testfile(filename) local stat, err = posix.stat(filename) if not stat then cwriteln("failure: ",filename," missing"); From 998f9ab3d08180b6dd864e1fc3f633d0dd719ca3 Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Sun, 7 Oct 2012 20:48:09 +0200 Subject: [PATCH 18/28] adapting test scripts --- tests/churn-direct.lua | 3 ++- tests/churn-rsync.lua | 50 ++++++++++++++++++++++---------------- tests/churn-rsyncssh.lua | 1 + tests/exclude-rsync.lua | 30 +++++++++++++---------- tests/exclude-rsyncssh.lua | 9 ++++--- tests/schedule.lua | 29 ++++++++++++---------- 6 files changed, 71 insertions(+), 51 deletions(-) diff --git a/tests/churn-direct.lua b/tests/churn-direct.lua index 8ef5339..42c104c 100755 --- a/tests/churn-direct.lua +++ b/tests/churn-direct.lua @@ -14,7 +14,8 @@ local tdir, srcdir, trgdir = mktemps() -- makes some startup data churn(srcdir, 10) -local logs = {'-log', 'Exec', '-log', 'Delay' } +local logs = { } +--local logs = {'-log', 'Exec', '-log', 'Delay' } local pid = spawn( './lsyncd', '-nodaemon', diff --git a/tests/churn-rsync.lua b/tests/churn-rsync.lua index d7378a2..b3af183 100755 --- a/tests/churn-rsync.lua +++ b/tests/churn-rsync.lua @@ -1,42 +1,50 @@ #!/usr/bin/lua -- a heavy duty test. -- makes thousends of random changes to the source tree -require("posix") -dofile("tests/testlib.lua") +require( "posix" ) +dofile( "tests/testlib.lua" ) -cwriteln("****************************************************************") -cwriteln(" Testing default.rsync with random data activity") -cwriteln("****************************************************************") +cwriteln( "****************************************************************" ) +cwriteln( " Testing default.rsync with random data activity" ) +cwriteln( "****************************************************************" ) -local tdir, srcdir, trgdir = mktemps() +local tdir, srcdir, trgdir = mktemps( ) -- makes some startup data -churn(srcdir, 100) +churn( srcdir, 100 ) -local logs = {} --- logs = {"-log", "Delay", "-log", "Fsevents" } -local pid = spawn("./lsyncd", "-nodaemon", "-delay", "5", - "-rsync", srcdir, trgdir, unpack(logs)) +local logs = { } +-- logs = { "-log", "Delay", "-log", "Fsevents" } +local pid = spawn( + "./lsyncd", + "-nodaemon", + "-delay", "5", + "-rsync", srcdir, trgdir, + unpack( logs ) +) -cwriteln("waiting for Lsyncd to startup") -posix.sleep(1) +cwriteln( "waiting for Lsyncd to startup" ) -churn(srcdir, 500) +posix.sleep( 1 ) -cwriteln("waiting for Lsyncd to finish its jobs.") -posix.sleep(10) +churn( srcdir, 500 ) + +cwriteln( "waiting for Lsyncd to finish its jobs." ) + +posix.sleep( 10 ) + +cwriteln( "killing the Lsyncd daemon" ) -cwriteln("killing the Lsyncd daemon") posix.kill(pid) -local _, exitmsg, lexitcode = posix.wait(lpid) +local _, exitmsg, lexitcode = posix.wait( lpid ) cwriteln("Exitcode of Lsyncd = ", exitmsg, " ", lexitcode) -exitcode = os.execute("diff -r "..srcdir.." "..trgdir) -cwriteln("Exitcode of diff = '", exitcode, "'") +exitcode = os.execute( "diff -r "..srcdir.." "..trgdir ) +cwriteln( "Exitcode of diff = '", exitcode, "'" ) + if exitcode ~= 0 then os.exit(1) else os.exit(0) end - diff --git a/tests/churn-rsyncssh.lua b/tests/churn-rsyncssh.lua index 3300019..715b919 100755 --- a/tests/churn-rsyncssh.lua +++ b/tests/churn-rsyncssh.lua @@ -37,6 +37,7 @@ cwriteln("Exitcode of Lsyncd = ", exitmsg, " ", lexitcode) exitcode = os.execute("diff -r "..srcdir.." "..trgdir) cwriteln("Exitcode of diff = '", exitcode, "'") + if exitcode ~= 0 then os.exit(1) else diff --git a/tests/exclude-rsync.lua b/tests/exclude-rsync.lua index 4bf6bb2..7a29aab 100755 --- a/tests/exclude-rsync.lua +++ b/tests/exclude-rsync.lua @@ -79,11 +79,13 @@ posix.sleep(3) cwriteln("testing excludes after startup"); testfiles(); cwriteln("ok, removing sources"); + if srcdir:sub(1,4) ~= "/tmp" then -- just to make sure before rm -rf cwriteln("exist before drama, srcdir is '", srcdir, "'"); os.exit(1); end + os.execute("rm -rf "..srcdir.."/*"); cwriteln("waiting for Lsyncd to remove destination"); posix.sleep(5); @@ -92,20 +94,22 @@ if os.execute("diff -urN "..srcdir.." "..trgdir) ~= 0 then os.exit(1); end -cwriteln("writing files after startup"); -writefiles(); -cwriteln("waiting for Lsyncd to transmit changes"); -posix.sleep(5); -testfiles(); +cwriteln( "writing files after startup" ); +writefiles( ); +cwriteln( "waiting for Lsyncd to transmit changes" ); +posix.sleep( 5 ); +testfiles( ); -cwriteln("killing started Lsyncd"); -posix.kill(pid); -local _, exitmsg, lexitcode = posix.wait(lpid); -cwriteln("Exitcode of Lsyncd = ", exitmsg, " ", lexitcode); -posix.sleep(1); -if lexitcode == 0 then - cwriteln("OK"); +cwriteln( "killing started Lsyncd" ); +posix.kill( pid ); +local _, exitmsg, lexitcode = posix.wait( lpid ); +cwriteln( "Exitcode of Lsyncd = ", exitmsg, " ", lexitcode ); + +if lexitcode == 143 then + cwriteln( "OK" ); + os.exit( 0 ); +else + os.exit( 1 ); end -os.exit(lexitcode); -- TODO remove temp diff --git a/tests/exclude-rsyncssh.lua b/tests/exclude-rsyncssh.lua index c9e1fd7..b9f37ac 100755 --- a/tests/exclude-rsyncssh.lua +++ b/tests/exclude-rsyncssh.lua @@ -108,9 +108,12 @@ posix.kill(pid); local _, exitmsg, lexitcode = posix.wait(lpid); cwriteln('Exitcode of Lsyncd = ', exitmsg, ' ', lexitcode); posix.sleep(1); -if lexitcode == 0 then - cwriteln('OK'); + +if lexitcode == 143 then + cwriteln( 'OK' ); + os.exit( 0 ); +else + os.exit( 1 ); end -os.exit(lexitcode); -- TODO remove temp diff --git a/tests/schedule.lua b/tests/schedule.lua index f00b54e..626d110 100755 --- a/tests/schedule.lua +++ b/tests/schedule.lua @@ -50,27 +50,30 @@ sync {ccircuit, source ="]]..srcdir..[[", target = "]]..trgdir..[["} local function testfile(filename) local stat, err = posix.stat(filename) if not stat then - cwriteln("failure: ",filename," missing"); - os.exit(1); + cwriteln("failure: ",filename," missing") + os.exit(1) end end -cwriteln("starting Lsyncd"); -local pid = spawn("./lsyncd", cfgfile, unpack(logs)); -cwriteln("waiting for Lsyncd to do a few cycles"); +cwriteln("starting Lsyncd") +local pid = spawn("./lsyncd", cfgfile, unpack(logs)) +cwriteln("waiting for Lsyncd to do a few cycles") posix.sleep(30) -cwriteln("look if every circle got a chance to run"); +cwriteln("look if every circle got a chance to run") testfile(srcdir.."a") testfile(srcdir.."b") testfile(srcdir.."c") -cwriteln("killing started Lsyncd"); -posix.kill(pid); -local _, exitmsg, lexitcode = posix.wait(lpid); -cwriteln("Exitcode of Lsyncd = ", exitmsg, " ", lexitcode); +cwriteln("killing started Lsyncd") +posix.kill(pid) +local _, exitmsg, lexitcode = posix.wait(lpid) +cwriteln("Exitcode of Lsyncd = ", exitmsg, " ", lexitcode) posix.sleep(1); -if lexitcode == 0 then - cwriteln("OK"); + +if lexitcode == 143 then + cwriteln("OK") + os.exit( 0 ) +else + os.exit( 1 ) end -os.exit(lexitcode); -- TODO remove temp From 23dbbe5ecdc196f838d2976253506e7c9d797609 Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Sun, 7 Oct 2012 21:40:05 +0200 Subject: [PATCH 19/28] allowing command line delay override the config file again --- lsyncd.lua | 28 +++++++++++++++++++--------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/lsyncd.lua b/lsyncd.lua index f779881..1b567f5 100644 --- a/lsyncd.lua +++ b/lsyncd.lua @@ -65,6 +65,12 @@ local Monitors -- local processCount = 0 +-- +-- Settings specified by command line. +-- +local clSettings = { } + + --============================================================================ -- Lsyncd Prototypes --============================================================================ @@ -2270,7 +2276,8 @@ local Syncs = ( function( ) 'maxDelays', 'maxProcesses' } - -- Lets settings or commandline override these values. + + -- Lets settings override these values. if settings then for _, v in ipairs( inheritSettings ) do if settings[ v ] then @@ -2279,6 +2286,15 @@ local Syncs = ( function( ) end end + -- Lets commandline override these values. + if clSettings then + for _, v in ipairs( inheritSettings ) do + if clSettings[ v ] then + config[ v ] = clSettings[ v ] + end + end + end + -- -- lets the userscript 'prepare' function -- check and complete the config @@ -3520,11 +3536,6 @@ SEE: end --- --- Settings specified by command line. --- -local clSettings = { } - -- -- Called from core to parse the command line arguments -- @@ -3535,7 +3546,7 @@ local clSettings = { } -- function runner.configure( args, monitors ) - Monitors.initialize (monitors ) + Monitors.initialize( monitors ) -- a list of all valid options -- @@ -3760,7 +3771,7 @@ function runner.initialize( firstTime ) -- -- creates settings if user didnt -- - settings = settings or {} + settings = settings or { } -- -- From this point on, no globals may be created anymore @@ -3790,7 +3801,6 @@ function runner.initialize( firstTime ) if k ~= 'syncs' then settings[ k ] = v end - end -- From f9231a11b27e68129d53699af709279aff883308 Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Mon, 8 Oct 2012 08:06:34 +0200 Subject: [PATCH 20/28] enabling port configuration for rsyncssh --- default-rsyncssh.lua | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/default-rsyncssh.lua b/default-rsyncssh.lua index e02cc6b..e5cabd8 100644 --- a/default-rsyncssh.lua +++ b/default-rsyncssh.lua @@ -79,7 +79,7 @@ rsyncssh.action = function( inlet ) spawn( event, config.ssh.binary, --- config.ssh._computed, TODO XXX + config.ssh._computed, config.host, 'mv', '\"' .. config.targetdir .. event.path .. '\"', @@ -138,7 +138,7 @@ rsyncssh.action = function( inlet ) config.ssh.binary, '<', table.concat(paths, config.xargs.delimiter), params, - -- config.ssh._computed, TODO XXX + config.ssh._computed, config.host, config.xargs.binary, config.xargs._extra @@ -261,15 +261,45 @@ rsyncssh.prepare = function( config, level ) default.rsync.prepare( config, level + 1, true ) if not config.host then - error('default.rsyncssh needs "host" configured', 4) + error( + 'default.rsyncssh needs "host" configured', + level + ) end if not config.targetdir then - error('default.rsyncssh needs "targetdir" configured', 4) + error( + 'default.rsyncssh needs "targetdir" configured', + level + ) end - if config.rsyncOps then - error('did you mean rsyncOpts with "t"?', 4) + -- + -- computes the ssh options + -- + if config.ssh._computed then + error( + 'please do not use the internal rsync._computed parameter', + level + ) + end + + local cssh = config.rsync; + cssh._computed = { } + local computed = cssh._computed + local computedN = 1 + + if cssh._extra then + for k, v in ipairs( cssh._extra ) do + computed[ computedN ] = v + computedN = computedN + 1 + end + end + + if cssh.port then + computed[ computedN ] = '-p' + computed[ computedN + 1 ] = cssh.port + computedN = computedN + 2 end -- appends a slash to the targetdir if missing From a82f4da7cd9a08b32ed012335e369ff4a6e2ecba Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Mon, 8 Oct 2012 09:10:03 +0200 Subject: [PATCH 21/28] making settings{} a function --- lsyncd.c | 2 +- lsyncd.lua | 134 ++++++++++++++++++++++++++++++++--------------------- 2 files changed, 81 insertions(+), 55 deletions(-) diff --git a/lsyncd.c b/lsyncd.c index 8916d96..0e230c7 100644 --- a/lsyncd.c +++ b/lsyncd.c @@ -2571,7 +2571,7 @@ main1( int argc, char *argv[] ) lua_settable ( L, -3 ); } - if( lua_pcall( L, 2, 1, -3 ) ) + if( lua_pcall( L, 2, 1, -4 ) ) { exit( -1 ); } if( first_time ) diff --git a/lsyncd.lua b/lsyncd.lua index 1b567f5..ebbcbc1 100644 --- a/lsyncd.lua +++ b/lsyncd.lua @@ -70,6 +70,17 @@ local processCount = 0 -- local clSettings = { } +-- +-- Settings specified by config scripts. +-- +local uSettings = { } + +-- +-- A copy of the settings function to see if the +-- user script replaced the settings() by a table +-- ( pre Lsyncd 2.1 style ) +-- +local settingsSafe --============================================================================ -- Lsyncd Prototypes @@ -1926,8 +1937,8 @@ local Sync = ( function( ) for _, d in Queue.qpairs( self.delays ) do -- if reached the global limit return - if settings.maxProcesses and - processCount >= settings.maxProcesses + if uSettings.maxProcesses and + processCount >= uSettings.maxProcesses then log('Alarm', 'at global process limit.') return @@ -2278,20 +2289,16 @@ local Syncs = ( function( ) } -- Lets settings override these values. - if settings then - for _, v in ipairs( inheritSettings ) do - if settings[ v ] then - config[ v ] = settings[ v ] - end + for _, v in ipairs( inheritSettings ) do + if uSettings[ v ] then + config[ v ] = uSettings[ v ] end end -- Lets commandline override these values. - if clSettings then - for _, v in ipairs( inheritSettings ) do - if clSettings[ v ] then - config[ v ] = clSettings[ v ] - end + for _, v in ipairs( inheritSettings ) do + if clSettings[ v ] then + config[ v ] = clSettings[ v ] end end @@ -2352,14 +2359,9 @@ local Syncs = ( function( ) terminate( -1 ) end - -- loads a default value for an option if not existent - if not settings then - settings = {} - end - -- the monitor to use config.monitor = - settings.monitor or + uSettings.monitor or config.monitor or Monitors.default( ) @@ -2520,7 +2522,8 @@ local Inotify = ( function( ) end -- registers the watch - local inotifyMode = ( settings and settings.inotifyMode ) or ''; + local inotifyMode = ( uSettings and uSettings.inotifyMode ) or ''; + local wd = lsyncd.inotify.addwatch( path, inotifyMode) ; if wd < 0 then @@ -3194,9 +3197,10 @@ local StatusFile = ( function( ) ' )' ) + -- -- takes care not write too often - - if settings.statusInterval > 0 then + -- + if uSettings.statusInterval > 0 then -- already waiting? if alarm and timestamp < alarm then @@ -3213,8 +3217,10 @@ local StatusFile = ( function( ) -- determines when a next write will be possible if not alarm then + local nextWrite = - lastWritten and timestamp + settings.statusInterval + lastWritten and timestamp + + uSettings.statusInterval if nextWrite and timestamp < nextWrite then log( @@ -3234,13 +3240,13 @@ local StatusFile = ( function( ) log( 'Statusfile', 'writing now' ) - local f, err = io.open( settings.statusFile, 'w' ) + local f, err = io.open( uSettings.statusFile, 'w' ) if not f then log( 'Error', 'Cannot open status file "' .. - settings.statusFile .. + uSettings.statusFile .. '" :' .. err ) @@ -3455,8 +3461,8 @@ function runner.cycle( -- not at global limit -- if - not settings.maxProcesses or - processCount < settings.maxProcesses + not uSettings.maxProcesses or + processCount < uSettings.maxProcesses then local start = Syncs.getRound( ) @@ -3479,7 +3485,7 @@ function runner.cycle( UserAlarms.invoke( timestamp ) - if settings.statusFile then + if uSettings.statusFile then StatusFile.write( timestamp ) end @@ -3548,6 +3554,7 @@ function runner.configure( args, monitors ) Monitors.initialize( monitors ) + -- -- a list of all valid options -- -- first paramter is the number of parameters an option takes @@ -3768,10 +3775,16 @@ end -- function runner.initialize( firstTime ) - -- - -- creates settings if user didnt - -- - settings = settings or { } + if settings ~= settingsSafe then + log( + 'Warn', + 'settings = { ... } is deprecated.\n'.. + ' please use settings{ ... } (without the equal sign)' + ) + + uSettings = settings + + end -- -- From this point on, no globals may be created anymore @@ -3781,9 +3794,12 @@ function runner.initialize( firstTime ) -- -- copies simple settings with numeric keys to 'key = true' settings. -- - for k, v in ipairs( settings ) do + -- FIXME this can be removed when + -- Lsyncd 2.0.x backwards compatibility is dropped + -- + for k, v in ipairs( uSettings ) do - if settings[ v ] then + if uSettings[ v ] then log( 'Error', 'Double setting "' .. v.. '"' @@ -3791,7 +3807,8 @@ function runner.initialize( firstTime ) os.exit( -1 ) end - settings[ v ]= true + uSettings[ v ]= true + end -- @@ -3799,7 +3816,7 @@ function runner.initialize( firstTime ) -- for k, v in pairs( clSettings ) do if k ~= 'syncs' then - settings[ k ] = v + uSettings[ k ] = v end end @@ -3807,7 +3824,7 @@ function runner.initialize( firstTime ) -- implicitly forces 'insist' on Lsyncd resets. -- if not firstTime then - settings.insist = true + uSettings.insist = true end -- @@ -3847,31 +3864,31 @@ function runner.initialize( firstTime ) end - if settings.nodaemon then + if uSettings.nodaemon then lsyncd.configure( 'nodaemon' ) end - if settings.logfile then - lsyncd.configure( 'logfile', settings.logfile ) + if uSettings.logfile then + lsyncd.configure( 'logfile', uSettings.logfile ) end - if settings.logident then - lsyncd.configure( 'logident', settings.logident ) + if uSettings.logident then + lsyncd.configure( 'logident', uSettings.logident ) end - if settings.logfacility then - lsyncd.configure( 'logfacility', settings.logfacility ) + if uSettings.logfacility then + lsyncd.configure( 'logfacility', uSettings.logfacility ) end - if settings.pidfile then - lsyncd.configure( 'pidfile', settings.pidfile ) + if uSettings.pidfile then + lsyncd.configure( 'pidfile', uSettings.pidfile ) end -- - -- Transfers some defaults to settings + -- Transfers some defaults to uSettings -- - if settings.statusInterval == nil then - settings.statusInterval = default.statusInterval + if uSettings.statusInterval == nil then + uSettings.statusInterval = default.statusInterval end -- makes sure the user gave Lsyncd anything to do @@ -3991,8 +4008,8 @@ function runner.getAlarm( ) -- but only if the global process limit is not yet reached. -- if - not settings.maxProcesses or - processCount < settings.maxProcesses + not uSettings.maxProcesses or + processCount < uSettings.maxProcesses then for _, s in Syncs.iwalk( ) do checkAlarm( s:getAlarm ( )) @@ -4182,8 +4199,8 @@ function spawn( processCount = processCount + 1 if - settings.maxProcesses and - processCount > settings.maxProcesses + uSettings.maxProcesses and + processCount > uSettings.maxProcesses then error( 'Spawned too much processes!' ) end @@ -4281,9 +4298,18 @@ function string.ends( String, End ) end -- --- Provides a default empty settings table. +-- The Lsyncd 2.1 settings call -- -settings = { } +function settings( a1 ) + for k, v in pairs( a1 ) do + if type( k ) ~= 'number' then + uSettings[ k ] = v + else + uSettings[ v ] = true + end + end +end +settingsSafe = settings -- -- Returns the core the runners function interface. From 5f01a04335aac51c6204927e846fa2a7d9f2d9da Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Tue, 9 Oct 2012 17:47:39 +0200 Subject: [PATCH 22/28] allow true, false, 'running' and 'startup' for the delete parameter --- default-direct.lua | 26 +++++++++++++++++++++++--- default-rsync.lua | 4 ++-- default-rsyncssh.lua | 6 +++++- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/default-direct.lua b/default-direct.lua index 74f5f28..a7f6509 100644 --- a/default-direct.lua +++ b/default-direct.lua @@ -83,29 +83,48 @@ direct.action = function(inlet) event.targetPathdir ) elseif event.etype == 'Delete' then - if not config.delete then + + if + config.delete ~= true and + config.delete ~= 'running' + then inlet.discardEvent(event) + return end local tp = event.targetPath + -- extra security check if tp == '' or tp == '/' or not tp then error('Refusing to erase your harddisk!') end + spawn(event, '/bin/rm', '-rf', tp) + elseif event.etype == 'Move' then local tp = event.targetPath + -- extra security check if tp == '' or tp == '/' or not tp then error('Refusing to erase your harddisk!') end + local command = '/bin/mv $1 $2 || /bin/rm -rf $1' - if not config.delete then command = '/bin/mv $1 $2'; end + + if + config.delete ~= true and + config.delete ~= 'running' + then + command = '/bin/mv $1 $2' + end + spawnShell( event, command, event.targetPath, - event2.targetPath) + event2.targetPath + ) + else log('Warn', 'ignored an event of type "',event.etype, '"') inlet.discardEvent(event) @@ -116,6 +135,7 @@ end -- Called when collecting a finished child process -- direct.collect = function(agent, exitcode) + local config = agent.config if not agent.isList and agent.etype == 'Init' then diff --git a/default-rsync.lua b/default-rsync.lua index 4ceb233..c4ea741 100644 --- a/default-rsync.lua +++ b/default-rsync.lua @@ -192,7 +192,7 @@ rsync.action = function( inlet ) local config = inlet.getConfig( ) local delete = nil - if config.delete then + if config.delete == true or config.delete == 'running' then delete = { '--delete', '--ignore-errors' } end @@ -233,7 +233,7 @@ rsync.init = function(event) target = config.host .. ':' .. config.targetdir end - if config.delete then + if config.delete == true or config.delete == 'startup' then delete = { '--delete', '--ignore-errors' } end diff --git a/default-rsyncssh.lua b/default-rsyncssh.lua index e5cabd8..1cd36b4 100644 --- a/default-rsyncssh.lua +++ b/default-rsyncssh.lua @@ -93,7 +93,11 @@ rsyncssh.action = function( inlet ) -- instead of constructing rsync filters if event.etype == 'Delete' then - if not config.delete then + + if + config.delete ~= true and + config.delete ~= 'running' + then inlet.discardEvent(event) return end From f09ac240a792d673754d081b0230c3969fe020ac Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Tue, 9 Oct 2012 18:06:46 +0200 Subject: [PATCH 23/28] properly reconstruct settings variable in case of backward compatibly accepting it as deprecated variable --- lsyncd.lua | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/lsyncd.lua b/lsyncd.lua index ebbcbc1..543a183 100644 --- a/lsyncd.lua +++ b/lsyncd.lua @@ -2269,6 +2269,22 @@ local Syncs = ( function( ) -- local function add( config ) + -- workaround for backwards compatibility + -- FIXME: remove when dropping that + if settings ~= settingsSafe then + log( + 'Warn', + 'settings = { ... } is deprecated.\n'.. + ' please use settings{ ... } (without the equal sign)' + ) + + for k, v in pairs( settings ) do + uSettings[ k ] = v + end + + settings = settingsSafe + end + -- Creates a new config table which inherits all keys/values -- from integer keyed tables local uconfig = config @@ -3782,7 +3798,9 @@ function runner.initialize( firstTime ) ' please use settings{ ... } (without the equal sign)' ) - uSettings = settings + for k, v in pairs( settings ) do + uSettings[ k ] = v + end end From 19094a9fd47cb25ef4a7cae9506560909dac1b6e Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Tue, 9 Oct 2012 19:54:19 +0200 Subject: [PATCH 24/28] changelog --- ChangeLog | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ChangeLog b/ChangeLog index 23bc30b..b4a959d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,16 @@ +??-??-2012: 2.1.0 + fix: fail startup if settings.inist is false and one of the target hosts fails + enhancement: rsyncOpts has been replaced by rsync = {...} parameter lists + enhancement: default.rsyncssh has now a ssh = {...} parameter similar to default.rsync to + add option to ssh calls. Ditto for xargs = {...} + enhancement: the default.* implementations have a checkgauge erroring on any unknown + parameters to the sync{} call + enhancement: the delete parameter now takes: true, false, 'running' and 'startup' + improvement: Dennis Schridde provided various improvements for Lsyncd's autoconf building + change: Lsyncd is now Lua 5.2 compatible + change: Lsyncd now exits with exitcode 143 on TERM signal + change: settings is now be used as call like settings{...} instead of settings = {...} + 04-04-2012: 2.0.7 fix: closed a memory leak due to not correct configured weak tables fix: default.direct, do not use on OSX unrecognized option -t on modify From 9dbea196c5ba31a8199256bde85ae63345e61f8b Mon Sep 17 00:00:00 2001 From: axel Date: Wed, 10 Oct 2012 10:07:13 +0200 Subject: [PATCH 25/28] fixing Stefanos problem --- default.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/default.lua b/default.lua index f3c1191..6e97062 100644 --- a/default.lua +++ b/default.lua @@ -63,7 +63,7 @@ default.action = function( inlet ) local func = config[ 'on'.. event.etype ] - if func then + if type( func ) == 'function' then func( event, event2 ) end From d3b31bda37b08b1710fe4f47767e58a033eab7ed Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Tue, 23 Oct 2012 07:23:58 +0200 Subject: [PATCH 26/28] added temp_dir rsync parameter --- default-rsync.lua | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/default-rsync.lua b/default-rsync.lua index c4ea741..13f65f2 100644 --- a/default-rsync.lua +++ b/default-rsync.lua @@ -81,6 +81,7 @@ rsync.checkgauge = { -- further rsync options rsh = true, rsync_path = true, + temp_dir = true, }, } @@ -469,6 +470,11 @@ rsync.prepare = function( computedN = computedN + 1 end + if crsync.temp_dir then + computed[ computedN ] = '--temp-dir=' + crsync.temp_dir + computedN = computedN + 1 + end + if shortsN ~= 2 then computed[ 1 ] = table.concat( shorts, '' ) else From 4a7bf07f4fce0a01888a0830f5953fdc63be5701 Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Tue, 23 Oct 2012 09:09:50 +0200 Subject: [PATCH 27/28] 2.1.0 release --- ChangeLog | 2 +- configure.ac | 2 +- lsyncd.lua | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/ChangeLog b/ChangeLog index b4a959d..c48b879 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,4 +1,4 @@ -??-??-2012: 2.1.0 +23-10-2012: 2.1.0 fix: fail startup if settings.inist is false and one of the target hosts fails enhancement: rsyncOpts has been replaced by rsync = {...} parameter lists enhancement: default.rsyncssh has now a ssh = {...} parameter similar to default.rsync to diff --git a/configure.ac b/configure.ac index e5ad276..7e454b4 100644 --- a/configure.ac +++ b/configure.ac @@ -2,7 +2,7 @@ # Process this file with autoconf to produce a configure script. #AC_PREREQ(2.60) -AC_INIT(lsyncd, 2.1.0-beta, axkibe@gmail.com) +AC_INIT(lsyncd, 2.1.0, axkibe@gmail.com) AM_INIT_AUTOMAKE([foreign]) AC_CONFIG_MACRO_DIR([m4]) diff --git a/lsyncd.lua b/lsyncd.lua index 543a183..884dd67 100644 --- a/lsyncd.lua +++ b/lsyncd.lua @@ -30,7 +30,7 @@ if lsyncd_version then lsyncd.terminate( -1 ) end -lsyncd_version = '2.1.0-beta' +lsyncd_version = '2.1.0' -- -- Hides the core interface from user scripts. From beaa258ad0829c7ed7ec7f68db5827726103047c Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Tue, 23 Oct 2012 14:31:54 +0200 Subject: [PATCH 28/28] do not flood log about waiting for processes. Fixing retry startup of rsyncssh --- default-rsyncssh.lua | 2 +- lsyncd.lua | 27 ++++++++++++++++++++------- 2 files changed, 21 insertions(+), 8 deletions(-) diff --git a/default-rsyncssh.lua b/default-rsyncssh.lua index 1cd36b4..94b7eea 100644 --- a/default-rsyncssh.lua +++ b/default-rsyncssh.lua @@ -206,7 +206,7 @@ rsyncssh.collect = function( agent, exitcode ) if rc == 'ok' then log('Normal', 'Startup of "',agent.source,'" finished: ', exitcode) elseif rc == 'again' then - if settings.insist then + if uSettings.insist then log('Normal', 'Retrying startup of "',agent.source,'": ', exitcode) else log('Error', 'Temporary or permanent failure on startup of "', diff --git a/lsyncd.lua b/lsyncd.lua index 884dd67..70d9ef3 100644 --- a/lsyncd.lua +++ b/lsyncd.lua @@ -3386,6 +3386,11 @@ local lsyncdStatus = 'init' -- local runner = { } +-- +-- Last time said to be waiting for more child processes +-- +local lastReportedWaiting = false + -- -- Called from core whenever Lua code failed. -- @@ -3452,12 +3457,19 @@ function runner.cycle( if processCount > 0 then - log( - 'Normal', - 'waiting for ', - processCount, - ' more child processes.' - ) + if + lastReportedWaiting == false or + timestamp >= lastReportedWaiting + 60 + then + lastReportedWaiting = timestamp + + log( + 'Normal', + 'waiting for ', + processCount, + ' more child processes.' + ) + end return true else @@ -3467,7 +3479,6 @@ function runner.cycle( end if lsyncdStatus ~= 'run' then - error( 'runner.cycle() called while not running!' ) end @@ -3804,6 +3815,8 @@ function runner.initialize( firstTime ) end + lastReportedWaiting = false + -- -- From this point on, no globals may be created anymore --