mirror of
https://github.com/octoleo/lsyncd.git
synced 2024-12-12 14:17:47 +00:00
388 lines
7.5 KiB
Lua
388 lines
7.5 KiB
Lua
--
|
|
-- default-rsyncssh.lua
|
|
--
|
|
-- Improved rsync - sync with rsync, but moves and deletes executed over ssh.
|
|
-- A (Layer 1) configuration.
|
|
--
|
|
-- Note:
|
|
-- this is infact just a configuration using Layer 1 configuration
|
|
-- like any other. It only gets compiled into the binary by default.
|
|
-- You can simply use a modified one, by copying everything into a
|
|
-- config file of yours and name it differently.
|
|
--
|
|
-- 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.rsync not loaded' );
|
|
end
|
|
|
|
if default.rsyncssh then
|
|
error( 'default-rsyncssh already loaded' );
|
|
end
|
|
|
|
--
|
|
-- rsyncssh extends default.rsync
|
|
--
|
|
local rsyncssh = { default.rsync }
|
|
default.rsyncssh = rsyncssh
|
|
|
|
--
|
|
-- used to ensure there aren't typos in the keys
|
|
--
|
|
rsyncssh.checkgauge = {
|
|
|
|
-- unsets the inherited value of from default.rsync
|
|
target = false,
|
|
onMove = true,
|
|
|
|
-- rsyncssh users host and targetdir
|
|
host = true,
|
|
targetdir = true,
|
|
sshExitCodes = true,
|
|
rsyncExitCodes = 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,
|
|
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
|
|
|
|
-- gets all other deletes ready to be
|
|
-- executed
|
|
local elist = inlet.getEvents(
|
|
function( e )
|
|
return e.etype == 'Delete'
|
|
end
|
|
)
|
|
|
|
-- 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
|
|
)
|
|
|
|
-- 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
|
|
|
|
log(
|
|
'Normal',
|
|
'Deleting list\n',
|
|
table.concat( paths, '\n' )
|
|
)
|
|
|
|
local params = { }
|
|
|
|
spawn(
|
|
elist,
|
|
config.ssh.binary,
|
|
'<', table.concat(paths, config.xargs.delimiter),
|
|
params,
|
|
config.ssh._computed,
|
|
config.host,
|
|
config.xargs.binary,
|
|
config.xargs._extra
|
|
)
|
|
|
|
return
|
|
end
|
|
|
|
--
|
|
-- 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 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.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', '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
|
|
|
|
--
|
|
-- checks the configuration.
|
|
--
|
|
rsyncssh.prepare = function( config, level )
|
|
|
|
default.rsync.prepare( config, level + 1, true )
|
|
|
|
if not config.host then
|
|
error(
|
|
'default.rsyncssh needs "host" configured',
|
|
level
|
|
)
|
|
end
|
|
|
|
if not config.targetdir then
|
|
error(
|
|
'default.rsyncssh needs "targetdir" configured',
|
|
level
|
|
)
|
|
end
|
|
|
|
--
|
|
-- computes the ssh options
|
|
--
|
|
if config.ssh._computed then
|
|
error(
|
|
'please do not use the internal rsync._computed parameter',
|
|
level
|
|
)
|
|
end
|
|
|
|
local cssh = config.rsync;
|
|
cssh._computed = { }
|
|
local computed = cssh._computed
|
|
local computedN = 1
|
|
|
|
if cssh._extra then
|
|
for k, v in ipairs( cssh._extra ) do
|
|
computed[ computedN ] = v
|
|
computedN = computedN + 1
|
|
end
|
|
end
|
|
|
|
if cssh.port then
|
|
computed[ computedN ] = '-p'
|
|
computed[ computedN + 1 ] = cssh.port
|
|
computedN = computedN + 2
|
|
end
|
|
|
|
-- appends a slash to the targetdir if missing
|
|
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
|
|
|
|
|
|
--
|
|
-- 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 = {
|
|
|
|
--
|
|
-- 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
|
|
--
|
|
rsyncssh.ssh = {
|
|
|
|
--
|
|
-- the binary called
|
|
--
|
|
binary = '/usr/bin/ssh',
|
|
|
|
--
|
|
-- if set connect to this port
|
|
--
|
|
port = nil,
|
|
|
|
--
|
|
-- extra parameters
|
|
--
|
|
_extra = { }
|
|
}
|
|
|