mirror of https://github.com/octoleo/lsyncd.git
splittet default configs into seperated files
This commit is contained in:
parent
b762b6f608
commit
af237aa691
14
ChangeLog
14
ChangeLog
|
@ -1,9 +1,21 @@
|
||||||
??-??-????: 2.1.0
|
??-??-????: 2.0.6
|
||||||
fix: no longer stops syslogging on HUP signals
|
fix: no longer stops syslogging on HUP signals
|
||||||
fix: OSX event watcher no longer misses moves into and out of the watch tree
|
fix: OSX event watcher no longer misses moves into and out of the watch tree
|
||||||
fix: not refinding a relative path to the config file in case of HUP.
|
fix: not refinding a relative path to the config file in case of HUP.
|
||||||
fix: rsync doing error 13 and killing Lsyncd.
|
fix: rsync doing error 13 and killing Lsyncd.
|
||||||
see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=659941
|
see http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=659941
|
||||||
|
fix: no event creation during shutdown (might loop before)
|
||||||
|
fix: no logging due to wrong log levels
|
||||||
|
fix: without-inotify compile option now works to compile on OSX
|
||||||
|
fix: in case of HUP-reset imply insist=true, since startup is known to be
|
||||||
|
configured correctly.
|
||||||
|
fix: a series of typos in comments, manpage etc.
|
||||||
|
change: complain if any "rsyncOps" is given
|
||||||
|
change: splitted the default configurations in their own files.
|
||||||
|
more cleanly seperated from the Lsyncd runner, and highlights it are just
|
||||||
|
Layer 1 configurations that happen to be provided by default.
|
||||||
|
change: Beautified the code, no extra spaces at line end, ' instead of ",
|
||||||
|
supposing 100 char width to view,
|
||||||
change: Lsyncd now remembers the absolute path of its config file during HUPs
|
change: Lsyncd now remembers the absolute path of its config file during HUPs
|
||||||
|
|
||||||
25-08-2011: 2.0.5
|
25-08-2011: 2.0.5
|
||||||
|
|
26
Makefile.am
26
Makefile.am
|
@ -1,7 +1,8 @@
|
||||||
AUTOMAKE_OPTIONS = foreign
|
AUTOMAKE_OPTIONS = foreign
|
||||||
CFLAGS += -Wall $(LUA_CFLAGS)
|
CFLAGS += -Wall $(LUA_CFLAGS)
|
||||||
bin_PROGRAMS = lsyncd
|
bin_PROGRAMS = lsyncd
|
||||||
lsyncd_SOURCES = lsyncd.h lsyncd.c lsyncd.lua
|
lsyncd_SOURCES = lsyncd.h lsyncd.c lsyncd.lua default-rsync.lua
|
||||||
|
|
||||||
if INOTIFY
|
if INOTIFY
|
||||||
lsyncd_SOURCES += inotify.c
|
lsyncd_SOURCES += inotify.c
|
||||||
endif
|
endif
|
||||||
|
@ -38,7 +39,7 @@ doc/lsyncd.1: doc/lsyncd.1.xml
|
||||||
doc/lsyncd.1.xml: doc/lsyncd.1.txt
|
doc/lsyncd.1.xml: doc/lsyncd.1.txt
|
||||||
asciidoc -o $@ -b docbook -d manpage $<
|
asciidoc -o $@ -b docbook -d manpage $<
|
||||||
|
|
||||||
CLEANFILES = luac.out luac.c
|
CLEANFILES = runner.out defaults.out runner.c defaults.c
|
||||||
|
|
||||||
if RUNNER
|
if RUNNER
|
||||||
# installs the runner script
|
# installs the runner script
|
||||||
|
@ -46,15 +47,22 @@ runnerdir = $(RUNNER_DIR)
|
||||||
runner_DATA = lsyncd.lua
|
runner_DATA = lsyncd.lua
|
||||||
else
|
else
|
||||||
# or compiles it into the binary
|
# or compiles it into the binary
|
||||||
lsyncd_LDADD += luac.o
|
lsyncd_LDADD += runner.o defaults.o
|
||||||
|
|
||||||
luac.o: luac.c
|
runner.o: runner.c
|
||||||
|
default.o: default.c
|
||||||
|
|
||||||
luac.c: luac.out bin2carray.lua
|
runner.c: runner.out bin2carray.lua
|
||||||
lua ./bin2carray.lua luac.out luac luac.c
|
lua ./bin2carray.lua $< runner $@
|
||||||
|
|
||||||
luac.out: lsyncd.lua
|
defaults.c: defaults.out bin2carray.lua
|
||||||
luac $<
|
lua ./bin2carray.lua $< defaults $@
|
||||||
|
|
||||||
|
runner.out: lsyncd.lua
|
||||||
|
luac -o $@ $<
|
||||||
|
|
||||||
|
defaults.out: default.lua default-rsync.lua default-rsyncssh.lua default-direct.lua
|
||||||
|
luac -o $@ $^
|
||||||
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,164 @@
|
||||||
|
--==================================================================================================
|
||||||
|
-- default-rsyncssh.lua
|
||||||
|
--
|
||||||
|
-- Keeps two directories with /bin/cp, /bin/rm and /bin/mv in sync.
|
||||||
|
-- Startup still uses rsync tough.
|
||||||
|
--
|
||||||
|
-- A (Layer 1) configuration.
|
||||||
|
--
|
||||||
|
-- Note:
|
||||||
|
-- this is infact just 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 call it differently.
|
||||||
|
--
|
||||||
|
-- License: GPLv2 (see COPYING) or any later version
|
||||||
|
-- Authors: Axel Kittenberger <axkibe@gmail.com>
|
||||||
|
--
|
||||||
|
--==================================================================================================
|
||||||
|
|
||||||
|
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)
|
||||||
|
-- gets all events ready for syncing
|
||||||
|
local event, event2 = inlet.getEvent()
|
||||||
|
|
||||||
|
if event.etype == 'Create' then
|
||||||
|
if event.isdir then
|
||||||
|
spawn(
|
||||||
|
event,
|
||||||
|
'/bin/mkdir',
|
||||||
|
'-p',
|
||||||
|
event.targetPath
|
||||||
|
)
|
||||||
|
else
|
||||||
|
spawn(
|
||||||
|
event,
|
||||||
|
'/bin/cp',
|
||||||
|
'-t',
|
||||||
|
event.targetPathdir,
|
||||||
|
event.sourcePath
|
||||||
|
)
|
||||||
|
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',
|
||||||
|
'-t',
|
||||||
|
event.targetPathdir,
|
||||||
|
event.sourcePath
|
||||||
|
)
|
||||||
|
elseif event.etype == 'Delete' then
|
||||||
|
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
|
||||||
|
spawnShell(
|
||||||
|
event,
|
||||||
|
'/bin/mv $1 $2 || /bin/rm -rf $1',
|
||||||
|
event.targetPath,
|
||||||
|
event2.targetPath)
|
||||||
|
else
|
||||||
|
log('Warn', 'ignored an event of type "',event.etype, '"')
|
||||||
|
inlet.discardEvent(event)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- Called when collecting a finished child process
|
||||||
|
--
|
||||||
|
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', '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
|
||||||
|
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,
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- Checks the configuration.
|
||||||
|
--
|
||||||
|
prepare = function(config)
|
||||||
|
if not config.target then
|
||||||
|
error('default.direct needs "target".', 4)
|
||||||
|
end
|
||||||
|
|
||||||
|
if config.rsyncOps then
|
||||||
|
error('did you mean rsyncOpts with "t"?', 4)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- Default delay is very short.
|
||||||
|
--
|
||||||
|
delay = 1,
|
||||||
|
|
||||||
|
------
|
||||||
|
-- Let the core not split move events.
|
||||||
|
--
|
||||||
|
onMove = true,
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- The rsync binary called.
|
||||||
|
--
|
||||||
|
rsyncBinary = '/usr/bin/rsync',
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- For startup sync
|
||||||
|
--
|
||||||
|
rsyncOpts = '-lts',
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- rsync exit codes
|
||||||
|
--
|
||||||
|
rsyncExitCodes = default.rsyncExitCodes,
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- On many system multiple disk operations just rather slow down
|
||||||
|
-- than speed up.
|
||||||
|
|
||||||
|
maxProcesses = 1,
|
||||||
|
}
|
|
@ -0,0 +1,174 @@
|
||||||
|
--==================================================================================================
|
||||||
|
-- default-rsync.lua
|
||||||
|
--
|
||||||
|
-- Syncs with rsync ("classic" Lsyncd)
|
||||||
|
-- A (Layer 1) configuration.
|
||||||
|
--
|
||||||
|
-- Note:
|
||||||
|
-- this is infact just 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 call it differently.
|
||||||
|
--
|
||||||
|
-- License: GPLv2 (see COPYING) or any later version
|
||||||
|
-- Authors: Axel Kittenberger <axkibe@gmail.com>
|
||||||
|
--
|
||||||
|
--==================================================================================================
|
||||||
|
|
||||||
|
if not default then error('default not loaded'); end
|
||||||
|
if default.rsync then error('default-rsync already loaded'); end
|
||||||
|
|
||||||
|
default.rsync = {
|
||||||
|
-----
|
||||||
|
-- Spawns rsync for a list of events
|
||||||
|
--
|
||||||
|
action = function(inlet)
|
||||||
|
-- gets all events ready for syncing
|
||||||
|
local elist = inlet.getEvents(
|
||||||
|
function(event)
|
||||||
|
return event.etype ~= 'Init' and event.etype ~= 'Blanket'
|
||||||
|
end
|
||||||
|
)
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- 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 etype == 'Delete' and string.byte(path1, -1) == 47 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()
|
||||||
|
spawn(elist, config.rsyncBinary,
|
||||||
|
'<', filter0,
|
||||||
|
config.rsyncOpts,
|
||||||
|
'-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();
|
||||||
|
if #excludes == 0 then
|
||||||
|
log('Normal', 'recursive startup rsync: ', config.source, ' -> ', config.target)
|
||||||
|
spawn(event, config.rsyncBinary,
|
||||||
|
'--delete',
|
||||||
|
config.rsyncOpts,
|
||||||
|
'-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.rsyncBinary,
|
||||||
|
'<', exS,
|
||||||
|
'--exclude-from=-',
|
||||||
|
'--delete',
|
||||||
|
config.rsyncOpts, '-r',
|
||||||
|
config.source,
|
||||||
|
config.target)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- Checks the configuration.
|
||||||
|
--
|
||||||
|
prepare = function(config)
|
||||||
|
if not config.target then
|
||||||
|
error('default.rsync needs "target" configured', 4)
|
||||||
|
end
|
||||||
|
|
||||||
|
if config.rsyncOps then
|
||||||
|
error('did you mean rsyncOpts with "t"?', 4)
|
||||||
|
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
|
||||||
|
----
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- The rsync binary to be called.
|
||||||
|
--
|
||||||
|
rsyncBinary = '/usr/bin/rsync',
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- Calls rsync with this default short opts.
|
||||||
|
--
|
||||||
|
rsyncOpts = '-lts',
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- Exit codes for rsync.
|
||||||
|
--
|
||||||
|
exitcodes = default.rsyncExitCodes,
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- Default delay
|
||||||
|
--
|
||||||
|
delay = 15,
|
||||||
|
}
|
|
@ -0,0 +1,255 @@
|
||||||
|
--==================================================================================================
|
||||||
|
-- default-rsyncssh.lua
|
||||||
|
--
|
||||||
|
-- Improved rsync - sync with rsync, but moves and deletes executed over ssh.
|
||||||
|
-- A (Layer 1) configuration.
|
||||||
|
--
|
||||||
|
-- Note:
|
||||||
|
-- this is infact just 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 call it differently.
|
||||||
|
--
|
||||||
|
-- License: GPLv2 (see COPYING) or any later version
|
||||||
|
-- Authors: Axel Kittenberger <axkibe@gmail.com>
|
||||||
|
--
|
||||||
|
--==================================================================================================
|
||||||
|
|
||||||
|
if not default then error('default not loaded'); end
|
||||||
|
if default.rsyncssh then error('default-rsyncssh already loaded'); end
|
||||||
|
|
||||||
|
default.rsyncssh = {
|
||||||
|
-----
|
||||||
|
-- Spawns rsync for a list of events
|
||||||
|
--
|
||||||
|
action = function(inlet)
|
||||||
|
local event, event2 = inlet.getEvent()
|
||||||
|
local config = inlet.getConfig()
|
||||||
|
|
||||||
|
-- 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 .. '\"')
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- uses ssh to delete files on remote host
|
||||||
|
-- instead of constructing rsync filters
|
||||||
|
if event.etype == 'Delete' then
|
||||||
|
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
|
||||||
|
|
||||||
|
local sPaths = table.concat(paths, '\n')
|
||||||
|
local zPaths = table.concat(paths, config.xargs.delimiter)
|
||||||
|
log('Normal', 'Deleting list\n', sPaths)
|
||||||
|
spawn(elist, '/usr/bin/ssh',
|
||||||
|
'<', zPaths,
|
||||||
|
config.host,
|
||||||
|
config.xargs.binary, config.xargs.xparams)
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
-- for everything else spawn a rsync
|
||||||
|
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 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
|
||||||
|
end
|
||||||
|
local sPaths = table.concat(paths, '\n')
|
||||||
|
local zPaths = table.concat(paths, '\000')
|
||||||
|
log('Normal', 'Rsyncing list\n', sPaths)
|
||||||
|
spawn(
|
||||||
|
elist, config.rsyncBinary,
|
||||||
|
'<', zPaths,
|
||||||
|
config.rsyncOpts,
|
||||||
|
'--from0',
|
||||||
|
'--files-from=-',
|
||||||
|
config.source,
|
||||||
|
config.host .. ':' .. config.targetdir
|
||||||
|
)
|
||||||
|
end,
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- Called when collecting a finished child process
|
||||||
|
--
|
||||||
|
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', '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
|
||||||
|
end
|
||||||
|
|
||||||
|
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
|
||||||
|
log('Error', 'Unknown exitcode (list): ',exitcode)
|
||||||
|
rc = 'die'
|
||||||
|
end
|
||||||
|
return rc
|
||||||
|
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
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- Spawns the recursive startup sync
|
||||||
|
--
|
||||||
|
init = function(event)
|
||||||
|
local config = event.config
|
||||||
|
local inlet = event.inlet
|
||||||
|
local excludes = inlet.getExcludes()
|
||||||
|
local target = config.host .. ':' .. config.targetdir
|
||||||
|
|
||||||
|
if #excludes == 0 then
|
||||||
|
log('Normal', 'Recursive startup rsync: ',config.source,' -> ',target)
|
||||||
|
spawn(
|
||||||
|
event, config.rsyncBinary,
|
||||||
|
'--delete',
|
||||||
|
'-r',
|
||||||
|
config.rsyncOpts,
|
||||||
|
config.source,
|
||||||
|
target
|
||||||
|
)
|
||||||
|
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
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- Checks the configuration.
|
||||||
|
--
|
||||||
|
prepare = function(config)
|
||||||
|
if not config.host then error('default.rsyncssh needs "host" configured', 4) end
|
||||||
|
if not config.targetdir then error('default.rsyncssh needs "targetdir" configured', 4) end
|
||||||
|
|
||||||
|
if config.rsyncOps then
|
||||||
|
error('did you mean rsyncOpts with "t"?', 4)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- appends a slash to the targetdir if missing
|
||||||
|
if string.sub(config.targetdir, -1) ~= '/' then
|
||||||
|
config.targetdir = config.targetdir .. '/'
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- The rsync binary called.
|
||||||
|
--
|
||||||
|
rsyncBinary = '/usr/bin/rsync',
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- Calls rsync with this default short opts.
|
||||||
|
--
|
||||||
|
rsyncOpts = '-lts',
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- allow processes
|
||||||
|
--
|
||||||
|
maxProcesses = 1,
|
||||||
|
|
||||||
|
------
|
||||||
|
-- Let the core not split move events.
|
||||||
|
--
|
||||||
|
onMove = true,
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- Default delay.
|
||||||
|
--
|
||||||
|
delay = 15,
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- rsync exit codes
|
||||||
|
--
|
||||||
|
rsyncExitCodes = default.rsyncExitCodes,
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- Delimiter, the binary and the paramters passed to xargs
|
||||||
|
-- xargs is used to delete multiple remote files, when ssh access is
|
||||||
|
-- available this is simpler than to build filters for rsync for this.
|
||||||
|
-- Default uses '0' as limiter, you might override this for old systems.
|
||||||
|
--
|
||||||
|
xargs = {
|
||||||
|
binary = '/usr/bin/xargs',
|
||||||
|
delimiter = '\000',
|
||||||
|
xparams = {'-0', 'rm -rf'}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,166 @@
|
||||||
|
--============================================================================
|
||||||
|
-- default.lua Live (Mirror) Syncing Demon
|
||||||
|
--
|
||||||
|
-- The default table for the user to access.
|
||||||
|
-- This default layer 1 functions provide the higher layer functionality.
|
||||||
|
--
|
||||||
|
-- License: GPLv2 (see COPYING) or any later version
|
||||||
|
-- Authors: Axel Kittenberger <axkibe@gmail.com>
|
||||||
|
--============================================================================
|
||||||
|
|
||||||
|
if default then 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 collector.
|
||||||
|
--
|
||||||
|
-- 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
|
||||||
|
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
|
||||||
|
log('Normal', 'Retrying startup of "',agent.source,'".')
|
||||||
|
return "again"
|
||||||
|
elseif rc == 'die' then
|
||||||
|
log('Error', 'Failure on startup of "',agent.source,'".')
|
||||||
|
terminate(-1) -- ERRNO
|
||||||
|
else
|
||||||
|
log('Error', 'Unknown exitcode "',exitcode,'" on startup of "',agent.source,'".')
|
||||||
|
return 'die'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if agent.isList then
|
||||||
|
if rc == 'ok' then log('Normal', 'Finished a list = ',exitcode)
|
||||||
|
elseif rc == 'again' then log('Normal', 'Retrying a list on exitcode = ',exitcode)
|
||||||
|
elseif rc == 'die' then log('Error', 'Failure with a list on exitcode = ',exitcode)
|
||||||
|
else
|
||||||
|
log('Error', 'Unknown exitcode "',exitcode,'" with a list')
|
||||||
|
rc = 'die'
|
||||||
|
end
|
||||||
|
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
|
||||||
|
return rc
|
||||||
|
end,
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- 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".
|
||||||
|
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 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..
|
||||||
|
--
|
||||||
|
maxDelays = 1000,
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- a default configuration using /bin/cp|rm|mv.
|
||||||
|
--
|
||||||
|
direct = default_direct,
|
||||||
|
|
||||||
|
------
|
||||||
|
-- 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',
|
||||||
|
},
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- Exitcodes of ssh and what to do.
|
||||||
|
--
|
||||||
|
sshExitCodes = {
|
||||||
|
[0] = 'ok',
|
||||||
|
[255] = 'again',
|
||||||
|
},
|
||||||
|
|
||||||
|
-----
|
||||||
|
-- Minimum seconds between two writes of a status file.
|
||||||
|
--
|
||||||
|
statusInterval = 10,
|
||||||
|
}
|
46
lsyncd.c
46
lsyncd.c
|
@ -49,10 +49,13 @@
|
||||||
* The Lua part of lsyncd if compiled into the binary.
|
* The Lua part of lsyncd if compiled into the binary.
|
||||||
*/
|
*/
|
||||||
#ifndef LSYNCD_DEFAULT_RUNNER_FILE
|
#ifndef LSYNCD_DEFAULT_RUNNER_FILE
|
||||||
extern const char luac_out[];
|
extern const char runner_out[];
|
||||||
extern size_t luac_size;
|
extern size_t runner_size;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
extern const char defaults_out[];
|
||||||
|
extern size_t defaults_size;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Makes sure there is one monitor.
|
* Makes sure there is one monitor.
|
||||||
*/
|
*/
|
||||||
|
@ -68,15 +71,19 @@
|
||||||
* All monitors supported by this Lsyncd.
|
* All monitors supported by this Lsyncd.
|
||||||
*/
|
*/
|
||||||
static char *monitors[] = {
|
static char *monitors[] = {
|
||||||
|
|
||||||
#ifdef LSYNCD_WITH_INOTIFY
|
#ifdef LSYNCD_WITH_INOTIFY
|
||||||
"inotify",
|
"inotify",
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef LSYNCD_WITH_FANOTIFY
|
#ifdef LSYNCD_WITH_FANOTIFY
|
||||||
"fanotify",
|
"fanotify",
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#ifdef LSYNCD_WITH_FSEVENTS
|
#ifdef LSYNCD_WITH_FSEVENTS
|
||||||
"fsevents",
|
"fsevents",
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
NULL,
|
NULL,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -96,7 +103,7 @@ struct settings settings = {
|
||||||
* True when lsyncd daemonized itself.
|
* True when lsyncd daemonized itself.
|
||||||
*/
|
*/
|
||||||
static bool is_daemon = false;
|
static bool is_daemon = false;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The config file loaded by Lsyncd.
|
* The config file loaded by Lsyncd.
|
||||||
* Global so it is retained during HUPs
|
* Global so it is retained during HUPs
|
||||||
|
@ -1735,7 +1742,8 @@ main1(int argc, char *argv[])
|
||||||
printlogf(L, "Error", "%s --runner RUNNER_FILE CONFIG_FILE", argv[0]);
|
printlogf(L, "Error", "%s --runner RUNNER_FILE CONFIG_FILE", argv[0]);
|
||||||
exit(-1); // ERRNO
|
exit(-1); // ERRNO
|
||||||
}
|
}
|
||||||
/* loads the runner file */
|
|
||||||
|
// loads the runner file
|
||||||
if (luaL_loadfile(L, lsyncd_runner_file)) {
|
if (luaL_loadfile(L, lsyncd_runner_file)) {
|
||||||
printlogf(L, "Error",
|
printlogf(L, "Error",
|
||||||
"error loading '%s': %s", lsyncd_runner_file, lua_tostring(L, -1));
|
"error loading '%s': %s", lsyncd_runner_file, lua_tostring(L, -1));
|
||||||
|
@ -1744,10 +1752,8 @@ main1(int argc, char *argv[])
|
||||||
} else {
|
} else {
|
||||||
#ifndef LSYNCD_DEFAULT_RUNNER_FILE
|
#ifndef LSYNCD_DEFAULT_RUNNER_FILE
|
||||||
// loads the runner from binary
|
// loads the runner from binary
|
||||||
if (luaL_loadbuffer(L, luac_out, luac_size, "lsyncd.lua")) {
|
if (luaL_loadbuffer(L, runner_out, runner_size, "runner")) {
|
||||||
printlogf(L, "Error",
|
printlogf(L, "Error", "loading precompiled runner: %s", lua_tostring(L, -1));
|
||||||
"error loading precompiled lsyncd.lua runner: %s",
|
|
||||||
lua_tostring(L, -1));
|
|
||||||
exit(-1); // ERRNO
|
exit(-1); // ERRNO
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
@ -1761,10 +1767,7 @@ main1(int argc, char *argv[])
|
||||||
// place to store the lua runners functions
|
// place to store the lua runners functions
|
||||||
// executes the runner defining all its functions
|
// executes the runner defining all its functions
|
||||||
if (lua_pcall(L, 0, LUA_MULTRET, 0)) {
|
if (lua_pcall(L, 0, LUA_MULTRET, 0)) {
|
||||||
printlogf(L, "Error",
|
printlogf(L, "Error", "preparing runner: %s", lua_tostring(L, -1));
|
||||||
"error preparing '%s': %s",
|
|
||||||
lsyncd_runner_file ? lsyncd_runner_file : "internal runner",
|
|
||||||
lua_tostring(L, -1));
|
|
||||||
exit(-1); // ERRNO
|
exit(-1); // ERRNO
|
||||||
}
|
}
|
||||||
lua_pushlightuserdata(L, (void *)&runner);
|
lua_pushlightuserdata(L, (void *)&runner);
|
||||||
|
@ -1798,6 +1801,21 @@ main1(int argc, char *argv[])
|
||||||
lua_pop(L, 1);
|
lua_pop(L, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
{
|
||||||
|
// 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
|
||||||
|
}
|
||||||
|
|
||||||
|
// prepares the defaults
|
||||||
|
if (lua_pcall(L, 0, 0, 0)) {
|
||||||
|
printlogf(L, "Error", "preparing defaults: %s", lua_tostring(L, -1));
|
||||||
|
exit(-1); // ERRNO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
// checks if there is a "-help" or "--help"
|
// checks if there is a "-help" or "--help"
|
||||||
int i;
|
int i;
|
||||||
|
@ -1857,7 +1875,7 @@ main1(int argc, char *argv[])
|
||||||
}
|
}
|
||||||
free(lsyncd_config_file);
|
free(lsyncd_config_file);
|
||||||
lsyncd_config_file = apath;
|
lsyncd_config_file = apath;
|
||||||
|
|
||||||
if (stat(lsyncd_config_file, &st)) {
|
if (stat(lsyncd_config_file, &st)) {
|
||||||
printlogf(L, "Error", "Cannot find config file at '%s'.", lsyncd_config_file);
|
printlogf(L, "Error", "Cannot find config file at '%s'.", lsyncd_config_file);
|
||||||
exit(-1); // ERRNO
|
exit(-1); // ERRNO
|
||||||
|
@ -1865,7 +1883,7 @@ main1(int argc, char *argv[])
|
||||||
|
|
||||||
// loads and executes the config file
|
// loads and executes the config file
|
||||||
if (luaL_loadfile(L, lsyncd_config_file)) {
|
if (luaL_loadfile(L, lsyncd_config_file)) {
|
||||||
printlogf(L, "Error",
|
printlogf(L, "Error",
|
||||||
"error loading %s: %s", lsyncd_config_file, lua_tostring(L, -1));
|
"error loading %s: %s", lsyncd_config_file, lua_tostring(L, -1));
|
||||||
exit(-1); // ERRNO
|
exit(-1); // ERRNO
|
||||||
}
|
}
|
||||||
|
|
693
lsyncd.lua
693
lsyncd.lua
|
@ -3053,699 +3053,6 @@ function string.ends(String,End)
|
||||||
return End=='' or string.sub(String,-#End)==End
|
return End=='' or string.sub(String,-#End)==End
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
||||||
--============================================================================
|
|
||||||
-- Lsyncd default settings
|
|
||||||
--============================================================================
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Exitcodes to retry on network failures of rsync.
|
|
||||||
--
|
|
||||||
local rsync_exitcodes = {
|
|
||||||
[ 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',
|
|
||||||
}
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Exitcodes to retry on network failures of rsync.
|
|
||||||
--
|
|
||||||
local ssh_exitcodes = {
|
|
||||||
[0] = 'ok',
|
|
||||||
[255] = 'again',
|
|
||||||
}
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Lsyncd classic - sync with rsync
|
|
||||||
--
|
|
||||||
local default_rsync = {
|
|
||||||
-----
|
|
||||||
-- Spawns rsync for a list of events
|
|
||||||
--
|
|
||||||
action = function(inlet)
|
|
||||||
-- gets all events ready for syncing
|
|
||||||
local elist = inlet.getEvents(
|
|
||||||
function(event)
|
|
||||||
return event.etype ~= 'Init' and event.etype ~= 'Blanket'
|
|
||||||
end
|
|
||||||
)
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- 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 etype == 'Delete' and string.byte(path1, -1) == 47 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()
|
|
||||||
spawn(elist, config.rsyncBinary,
|
|
||||||
'<', filter0,
|
|
||||||
config.rsyncOpts,
|
|
||||||
'-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();
|
|
||||||
if #excludes == 0 then
|
|
||||||
log('Normal', 'recursive startup rsync: ', config.source, ' -> ', config.target)
|
|
||||||
spawn(event, config.rsyncBinary,
|
|
||||||
'--delete',
|
|
||||||
config.rsyncOpts,
|
|
||||||
'-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.rsyncBinary,
|
|
||||||
'<', exS,
|
|
||||||
'--exclude-from=-',
|
|
||||||
'--delete',
|
|
||||||
config.rsyncOpts, '-r',
|
|
||||||
config.source,
|
|
||||||
config.target)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Checks the configuration.
|
|
||||||
--
|
|
||||||
prepare = function(config)
|
|
||||||
if not config.target then
|
|
||||||
error('default.rsync needs "target" configured', 4)
|
|
||||||
end
|
|
||||||
|
|
||||||
-- appends a / to target if not present
|
|
||||||
if string.sub(config.target, -1) ~= '/' then
|
|
||||||
config.target = config.target..'/'
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- The rsync binary called.
|
|
||||||
--
|
|
||||||
rsyncBinary = '/usr/bin/rsync',
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Calls rsync with this default short opts.
|
|
||||||
--
|
|
||||||
rsyncOpts = '-lts',
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- exit codes for rsync.
|
|
||||||
--
|
|
||||||
exitcodes = rsync_exitcodes,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Default delay
|
|
||||||
--
|
|
||||||
delay = 15,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Lsyncd 2 improved rsync - sync with rsync but move over ssh.
|
|
||||||
--
|
|
||||||
local default_rsyncssh = {
|
|
||||||
-----
|
|
||||||
-- Spawns rsync for a list of events
|
|
||||||
--
|
|
||||||
action = function(inlet)
|
|
||||||
local event, event2 = inlet.getEvent()
|
|
||||||
local config = inlet.getConfig()
|
|
||||||
|
|
||||||
-- 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 .. '\"')
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- uses ssh to delete files on remote host
|
|
||||||
-- instead of constructing rsync filters
|
|
||||||
if event.etype == 'Delete' then
|
|
||||||
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
|
|
||||||
|
|
||||||
local sPaths = table.concat(paths, '\n')
|
|
||||||
local zPaths = table.concat(paths, config.xargs.delimiter)
|
|
||||||
log('Normal', 'Deleting list\n', sPaths)
|
|
||||||
spawn(elist, '/usr/bin/ssh',
|
|
||||||
'<', zPaths,
|
|
||||||
config.host,
|
|
||||||
config.xargs.binary, config.xargs.xparams)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
-- for everything else spawn a rsync
|
|
||||||
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 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
|
|
||||||
end
|
|
||||||
local sPaths = table.concat(paths, '\n')
|
|
||||||
local zPaths = table.concat(paths, '\000')
|
|
||||||
log('Normal', 'Rsyncing list\n', sPaths)
|
|
||||||
spawn(
|
|
||||||
elist, config.rsyncBinary,
|
|
||||||
'<', zPaths,
|
|
||||||
config.rsyncOpts,
|
|
||||||
'--from0',
|
|
||||||
'--files-from=-',
|
|
||||||
config.source,
|
|
||||||
config.host .. ':' .. config.targetdir
|
|
||||||
)
|
|
||||||
end,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Called when collecting a finished child process
|
|
||||||
--
|
|
||||||
collect = function(agent, exitcode)
|
|
||||||
if not agent.isList and agent.etype == 'Init' then
|
|
||||||
local rc = rsync_exitcodes[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
|
|
||||||
end
|
|
||||||
|
|
||||||
if agent.isList then
|
|
||||||
local rc = rsync_exitcodes[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
|
|
||||||
log('Error', 'Unknown exitcode (list): ',exitcode)
|
|
||||||
rc = 'die'
|
|
||||||
end
|
|
||||||
return rc
|
|
||||||
else
|
|
||||||
local rc = ssh_exitcodes[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
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Spawns the recursive startup sync
|
|
||||||
--
|
|
||||||
init = function(event)
|
|
||||||
local config = event.config
|
|
||||||
local inlet = event.inlet
|
|
||||||
local excludes = inlet.getExcludes()
|
|
||||||
local target = config.host .. ':' .. config.targetdir
|
|
||||||
|
|
||||||
if #excludes == 0 then
|
|
||||||
log('Normal', 'Recursive startup rsync: ',config.source,' -> ',target)
|
|
||||||
spawn(
|
|
||||||
event, config.rsyncBinary,
|
|
||||||
'--delete',
|
|
||||||
'-r',
|
|
||||||
config.rsyncOpts,
|
|
||||||
config.source,
|
|
||||||
target
|
|
||||||
)
|
|
||||||
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
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Checks the configuration.
|
|
||||||
--
|
|
||||||
prepare = function(config)
|
|
||||||
if not config.host then error('default.rsyncssh needs "host" configured', 4) end
|
|
||||||
if not config.targetdir then error('default.rsyncssh needs "targetdir" configured', 4) end
|
|
||||||
|
|
||||||
-- appends a slash to the targetdir if missing
|
|
||||||
if string.sub(config.targetdir, -1) ~= '/' then
|
|
||||||
config.targetdir = config.targetdir .. '/'
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- The rsync binary called.
|
|
||||||
--
|
|
||||||
rsyncBinary = '/usr/bin/rsync',
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Calls rsync with this default short opts.
|
|
||||||
--
|
|
||||||
rsyncOpts = '-lts',
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- allow processes
|
|
||||||
--
|
|
||||||
maxProcesses = 1,
|
|
||||||
|
|
||||||
------
|
|
||||||
-- Let the core not split move events.
|
|
||||||
--
|
|
||||||
onMove = true,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Default delay.
|
|
||||||
--
|
|
||||||
delay = 15,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Delimiter, the binary and the paramters passed to xargs
|
|
||||||
-- xargs is used to delete multiple remote files, when ssh access is
|
|
||||||
-- available this is simpler than to build filters for rsync for this.
|
|
||||||
-- Default uses '0' as limiter, you might override this for old systems.
|
|
||||||
--
|
|
||||||
xargs = {
|
|
||||||
binary = '/usr/bin/xargs',
|
|
||||||
delimiter = '\000',
|
|
||||||
xparams = {'-0', 'rm -rf'}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Keeps two directories with /bin/cp, /bin/rm and /bin/mv in sync.
|
|
||||||
-- Startup still uses rsync tough.
|
|
||||||
--
|
|
||||||
local default_direct = {
|
|
||||||
-----
|
|
||||||
-- Spawns rsync for a list of events
|
|
||||||
--
|
|
||||||
action = function(inlet)
|
|
||||||
-- gets all events ready for syncing
|
|
||||||
local event, event2 = inlet.getEvent()
|
|
||||||
|
|
||||||
if event.etype == 'Create' then
|
|
||||||
if event.isdir then
|
|
||||||
spawn(
|
|
||||||
event,
|
|
||||||
'/bin/mkdir',
|
|
||||||
'-p',
|
|
||||||
event.targetPath
|
|
||||||
)
|
|
||||||
else
|
|
||||||
spawn(
|
|
||||||
event,
|
|
||||||
'/bin/cp',
|
|
||||||
'-t',
|
|
||||||
event.targetPathdir,
|
|
||||||
event.sourcePath
|
|
||||||
)
|
|
||||||
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',
|
|
||||||
'-t',
|
|
||||||
event.targetPathdir,
|
|
||||||
event.sourcePath
|
|
||||||
)
|
|
||||||
elseif event.etype == 'Delete' then
|
|
||||||
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
|
|
||||||
spawnShell(
|
|
||||||
event,
|
|
||||||
'/bin/mv $1 $2 || /bin/rm -rf $1',
|
|
||||||
event.targetPath,
|
|
||||||
event2.targetPath)
|
|
||||||
else
|
|
||||||
log('Warn', 'ignored an event of type "',event.etype, '"')
|
|
||||||
inlet.discardEvent(event)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Called when collecting a finished child process
|
|
||||||
--
|
|
||||||
collect = function(agent, exitcode)
|
|
||||||
local config = agent.config
|
|
||||||
|
|
||||||
if not agent.isList and agent.etype == 'Init' then
|
|
||||||
local rc = rsync_exitcodes[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
|
|
||||||
end
|
|
||||||
|
|
||||||
-- everything else is just as it is,
|
|
||||||
-- there is no network to retry something.
|
|
||||||
return
|
|
||||||
end,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Spawns the recursive startup sync
|
|
||||||
-- identical to default rsync.
|
|
||||||
--
|
|
||||||
init = default_rsync.init,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Checks the configuration.
|
|
||||||
--
|
|
||||||
prepare = function(config)
|
|
||||||
if not config.target then
|
|
||||||
error('default.direct needs "target".', 4)
|
|
||||||
end
|
|
||||||
end,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Default delay is very short.
|
|
||||||
--
|
|
||||||
delay = 1,
|
|
||||||
|
|
||||||
------
|
|
||||||
-- Let the core not split move events.
|
|
||||||
--
|
|
||||||
onMove = true,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- The rsync binary called.
|
|
||||||
--
|
|
||||||
rsyncBinary = '/usr/bin/rsync',
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- For startup sync
|
|
||||||
--
|
|
||||||
rsyncOpts = '-lts',
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- On many system multiple disk operations just rather slow down
|
|
||||||
-- than speed up.
|
|
||||||
|
|
||||||
maxProcesses = 1,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- The default table for the user to access.
|
|
||||||
-- Provides all the default layer 1 functions.
|
|
||||||
--
|
|
||||||
-- TODO make readonly
|
|
||||||
--
|
|
||||||
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 collector.
|
|
||||||
-- 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
|
|
||||||
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
|
|
||||||
log('Normal', 'Retrying startup of "',agent.source,'".')
|
|
||||||
return "again"
|
|
||||||
elseif rc == 'die' then
|
|
||||||
log('Error', 'Failure on startup of "',agent.source,'".')
|
|
||||||
terminate(-1) -- ERRNO
|
|
||||||
else
|
|
||||||
log('Error', 'Unknown exitcode "',exitcode,'" on startup of "',agent.source,'".')
|
|
||||||
return 'die'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if agent.isList then
|
|
||||||
if rc == 'ok' then log('Normal', 'Finished a list = ',exitcode)
|
|
||||||
elseif rc == 'again' then log('Normal', 'Retrying a list on exitcode = ',exitcode)
|
|
||||||
elseif rc == 'die' then log('Error', 'Failure with a list on exitcode = ',exitcode)
|
|
||||||
else
|
|
||||||
log('Error', 'Unknown exitcode "',exitcode,'" with a list')
|
|
||||||
rc = 'die'
|
|
||||||
end
|
|
||||||
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
|
|
||||||
return rc
|
|
||||||
end,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- 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".
|
|
||||||
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 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..
|
|
||||||
--
|
|
||||||
maxDelays = 1000,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- a default rsync configuration for easy usage.
|
|
||||||
--
|
|
||||||
rsync = default_rsync,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- a default rsync configuration with ssh'd move and rm actions
|
|
||||||
--
|
|
||||||
rsyncssh = default_rsyncssh,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- a default configuration using /bin/cp|rm|mv.
|
|
||||||
--
|
|
||||||
direct = default_direct,
|
|
||||||
|
|
||||||
-----
|
|
||||||
-- Minimum seconds between two writes of a status file.
|
|
||||||
--
|
|
||||||
statusInterval = 10,
|
|
||||||
}
|
|
||||||
|
|
||||||
-----
|
-----
|
||||||
-- provides a default empty settings table.
|
-- provides a default empty settings table.
|
||||||
--
|
--
|
||||||
|
|
Loading…
Reference in New Issue