mirror of
https://github.com/octoleo/lsyncd.git
synced 2024-12-12 14:17:47 +00:00
introducing checkgauge, code beautifications
This commit is contained in:
parent
1bf1d13eaa
commit
6a862d6b8f
@ -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
|
||||
--
|
||||
|
@ -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 = { }
|
||||
}
|
||||
|
||||
|
499
default.lua
499
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
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user