lsyncd/default-rsyncssh.lua

484 lines
7.9 KiB
Lua
Raw Normal View History

--
-- default-rsyncssh.lua
--
-- Improved rsync - sync with rsync, but moves and deletes executed over ssh.
-- A (Layer 1) configuration.
--
-- Note:
2012-02-15 19:10:50 +00:00
-- 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
2012-02-15 19:10:50 +00:00
-- config file of yours and name it differently.
--
-- License: GPLv2 (see COPYING) or any later version
-- Authors: Axel Kittenberger <axkibe@gmail.com>
--
--
2016-12-01 13:12:56 +00:00
if not default
then
error( 'default not loaded' );
end
2016-12-01 13:12:56 +00:00
if not default.rsync
then
error( 'default.rsync not loaded' );
end
2016-12-01 13:12:56 +00:00
if default.rsyncssh
then
error( 'default-rsyncssh already loaded' );
end
--
-- rsyncssh extends default.rsync
--
local rsyncssh = { default.rsync }
2016-12-01 13:12:56 +00:00
default.rsyncssh = rsyncssh
--
-- used to ensure there aren't typos in the keys
--
rsyncssh.checkgauge = {
-- unsets the inherited value of from default.rsync
2012-10-06 12:22:08 +00:00
target = false,
onMove = true,
-- rsyncssh users host and targetdir
2012-10-06 12:22:08 +00:00
host = true,
targetdir = true,
sshExitCodes = true,
rsyncExitCodes = true,
-- ssh settings
ssh = {
binary = true,
identityFile = true,
options = true,
port = true,
_extra = true
},
}
--
-- Returns true for non Init, Blanket and Move events.
--
local eventNotInitBlankMove =
function
(
event
)
-- TODO use a table
if event.etype == 'Move'
or event.etype == 'Init'
or event.etype == 'Blanket'
then
return 'break'
else
return true
end
end
--
-- Replaces what rsync would consider filter rules by literals.
--
local replaceRsyncFilter =
function
(
path
)
if not path
then
return
end
return(
path
:gsub( '%?', '\\?' )
:gsub( '%*', '\\*' )
:gsub( '%[', '\\[' )
)
end
--
-- Spawns rsync for a list of events
--
2016-12-14 07:45:33 +00:00
rsyncssh.action = function
(
inlet
)
local config = inlet.getConfig( )
local event, event2 = inlet.getEvent( )
-- makes move local on target host
-- if the move fails, it deletes the source
2016-12-14 07:45:33 +00:00
if event.etype == 'Move'
then
local path1 = config.targetdir .. event.path
2016-12-14 07:45:33 +00:00
local path2 = config.targetdir .. event2.path
2016-12-14 07:45:33 +00:00
path1 = "'" .. path1:gsub ('\'', '\'"\'"\'') .. "'"
path2 = "'" .. path2:gsub ('\'', '\'"\'"\'') .. "'"
log(
'Normal',
'Moving ',
event.path,
' -> ',
event2.path
)
spawn(
event,
config.ssh.binary,
config.ssh._computed,
config.host,
'mv',
path1,
path2,
'||', 'rm', '-rf',
path1
)
return
end
-- otherwise a rsync is spawned
local elist = inlet.getEvents( eventNotInitBlankMove )
-- gets the list of paths for the event list
-- deletes create multi match patterns
2017-01-05 09:22:00 +00:00
local paths = elist.getPaths( )
log(
'Normal',
'Rsyncing list\n',
2017-01-05 09:22:00 +00:00
table.concat( paths, '\n' )
)
local delete = nil
if config.delete == true
or config.delete == 'running'
then
2017-01-05 09:22:00 +00:00
delete = { '--delete-missing-args', '--ignore-errors' }
end
spawn(
elist,
config.rsync.binary,
2017-01-05 09:23:12 +00:00
'<', table.concat( paths, '\000' ),
config.rsync._computed,
'-r',
delete,
'--force',
'--from0',
2017-01-05 09:22:00 +00:00
'--files-from=-',
config.source,
config.host .. ':' .. config.targetdir
)
end
-----
-- Called when collecting a finished child process
--
2016-12-14 07:45:33 +00:00
rsyncssh.collect = function
(
agent,
exitcode
)
local config = agent.config
2016-12-14 07:45:33 +00:00
if not agent.isList and agent.etype == 'Init'
then
local rc = config.rsyncExitCodes[exitcode]
2016-12-14 07:45:33 +00:00
if rc == 'ok'
then
2017-01-03 12:08:48 +00:00
log('Normal', 'Startup of "', agent.source, '" finished: ', exitcode)
2016-12-14 07:45:33 +00:00
elseif rc == 'again'
then
if settings('insist')
then
2017-01-03 12:08:48 +00:00
log( 'Normal', 'Retrying startup of "', agent.source, '": ', exitcode )
else
2017-01-03 12:08:48 +00:00
log(
'Error',
'Temporary or permanent failure on startup of "',
agent.source, '". Terminating since "insist" is not set.'
)
2016-12-14 07:45:33 +00:00
terminate( -1 ) -- ERRNO
end
2016-12-14 07:45:33 +00:00
elseif rc == 'die'
then
log( 'Error', 'Failure on startup of "',agent.source,'": ', exitcode )
else
2016-12-14 07:45:33 +00:00
log( 'Error', 'Unknown exitcode on startup of "', agent.source,': "',exitcode )
rc = 'die'
end
return rc
end
2016-12-14 07:45:33 +00:00
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
2016-12-14 07:45:33 +00:00
log( 'Error', 'Unknown exitcode (list): ', exitcode )
rc = 'die'
end
return rc
else
local rc = config.sshExitCodes[exitcode]
2016-12-14 07:45:33 +00:00
if rc == 'ok'
then
2017-01-03 12:08:48 +00:00
log( 'Normal', 'Finished ', agent.etype,' ', agent.sourcePath, ': ', exitcode )
2016-12-14 07:45:33 +00:00
elseif rc == 'again'
then
2017-01-03 12:08:48 +00:00
log( 'Normal', 'Retrying ', agent.etype, ' ', agent.sourcePath, ': ', exitcode )
2016-12-14 07:45:33 +00:00
elseif rc == 'die'
then
2017-01-03 12:08:48 +00:00
log( 'Normal', 'Failure ', agent.etype, ' ', agent.sourcePath, ': ', exitcode )
else
2016-12-14 07:45:33 +00:00
log( 'Error', 'Unknown exitcode ',agent.etype,' ',agent.sourcePath,': ',exitcode )
rc = 'die'
end
return rc
end
end
--
-- checks the configuration.
--
2016-12-14 07:45:33 +00:00
rsyncssh.prepare = function
(
config,
level
)
2012-10-06 12:22:08 +00:00
default.rsync.prepare( config, level + 1, true )
2016-11-25 13:55:59 +00:00
if not config.host
then
error(
'default.rsyncssh needs "host" configured',
level
)
end
2016-11-25 13:55:59 +00:00
if not config.targetdir
then
error(
'default.rsyncssh needs "targetdir" configured',
level
)
end
--
-- computes the ssh options
--
2016-11-25 13:55:59 +00:00
if config.ssh._computed
then
error(
'please do not use the internal rsync._computed parameter',
level
)
end
if config.maxProcesses ~= 1
then
error(
'default.rsyncssh must have maxProcesses set to 1.',
level
)
end
2016-11-25 13:55:59 +00:00
local cssh = config.ssh;
2016-11-25 13:55:59 +00:00
cssh._computed = { }
2016-11-25 13:55:59 +00:00
local computed = cssh._computed
2016-11-25 13:55:59 +00:00
local computedN = 1
2016-11-25 13:55:59 +00:00
local rsyncc = config.rsync._computed
2016-11-25 13:55:59 +00:00
if cssh.identityFile
then
computed[ computedN ] = '-i'
2016-11-25 13:55:59 +00:00
computed[ computedN + 1 ] = cssh.identityFile
2016-11-25 13:55:59 +00:00
computedN = computedN + 2
2016-11-25 13:55:59 +00:00
if not config.rsync._rshIndex
then
config.rsync._rshIndex = #rsyncc + 1
2016-11-25 13:55:59 +00:00
rsyncc[ config.rsync._rshIndex ] = '--rsh=ssh'
end
rsyncc[ config.rsync._rshIndex ] =
rsyncc[ config.rsync._rshIndex ] ..
' -i ' ..
cssh.identityFile
end
2016-11-25 13:55:59 +00:00
if cssh.options
then
for k, v in pairs( cssh.options )
do
computed[ computedN ] = '-o'
2016-11-25 13:55:59 +00:00
computed[ computedN + 1 ] = k .. '=' .. v
2016-11-25 13:55:59 +00:00
computedN = computedN + 2
2016-11-25 13:55:59 +00:00
if not config.rsync._rshIndex
then
config.rsync._rshIndex = #rsyncc + 1
2016-11-25 13:55:59 +00:00
rsyncc[ config.rsync._rshIndex ] = '--rsh=ssh'
end
rsyncc[ config.rsync._rshIndex ] =
table.concat(
{
rsyncc[ config.rsync._rshIndex ],
' -o ',
k,
'=',
v
},
''
)
end
end
2016-11-25 13:55:59 +00:00
if cssh.port
then
computed[ computedN ] = '-p'
2016-11-25 13:55:59 +00:00
computed[ computedN + 1 ] = cssh.port
2016-11-25 13:55:59 +00:00
computedN = computedN + 2
2016-11-25 13:55:59 +00:00
if not config.rsync._rshIndex
then
config.rsync._rshIndex = #rsyncc + 1
2016-11-25 13:55:59 +00:00
rsyncc[ config.rsync._rshIndex ] = '--rsh=ssh'
end
rsyncc[ config.rsync._rshIndex ] =
rsyncc[ config.rsync._rshIndex ] .. ' -p ' .. cssh.port
end
2016-11-25 13:55:59 +00:00
if cssh._extra
then
for k, v in ipairs( cssh._extra )
do
computed[ computedN ] = v
2016-11-25 13:55:59 +00:00
computedN = computedN + 1
end
end
-- appends a slash to the targetdir if missing
2016-11-25 13:55:59 +00:00
if string.sub( config.targetdir, -1 ) ~= '/'
then
config.targetdir =
config.targetdir .. '/'
end
end
--
-- allow processes
--
2016-08-29 11:12:09 +00:00
rsyncssh.maxProcesses = 1
--
-- The core should not split move events
--
2016-08-29 11:12:09 +00:00
rsyncssh.onMove = true
--
-- default delay
--
2016-08-29 11:12:09 +00:00
rsyncssh.delay = 15
--
-- no default exit codes
--
2016-08-29 11:12:09 +00:00
rsyncssh.exitcodes = false
--
-- rsync exit codes
--
2016-08-29 11:12:09 +00:00
rsyncssh.rsyncExitCodes = default.rsyncExitCodes
--
-- ssh exit codes
--
2016-08-29 11:12:09 +00:00
rsyncssh.sshExitCodes = default.sshExitCodes
--
-- ssh calls configuration
--
-- ssh is used to move and delete files on the target host
--
rsyncssh.ssh = {
--
-- the binary called
--
2016-12-14 07:45:33 +00:00
binary = '/usr/bin/ssh',
--
-- if set adds this key to ssh
--
2016-12-14 07:45:33 +00:00
identityFile = nil,
--
-- if set adds this special options to ssh
--
2016-12-14 07:45:33 +00:00
options = nil,
--
-- if set connect to this port
--
2016-12-14 07:45:33 +00:00
port = nil,
--
-- extra parameters
--
2016-12-14 07:45:33 +00:00
_extra = { }
}