lsyncd/default.lua

446 lines
7.4 KiB
Lua
Raw Normal View History

--============================================================================
-- 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>
--============================================================================
2016-12-01 11:40:30 +00:00
if default
then
error( 'default already loaded' )
2012-10-03 16:34:09 +00:00
end
--- @diagnostic disable-next-line: lowercase-global
default = { }
--
-- Only this items are inherited from the default
-- table
--
default._merge = {
action = true,
checkgauge = true,
collect = true,
delay = true,
init = true,
maxDelays = true,
maxProcesses = true,
prepare = true,
}
--
-- used to ensure there aren't typos in the keys
--
default.checkgauge = {
2013-05-14 21:13:09 +00:00
action = true,
checkgauge = true,
collect = true,
2022-03-25 18:01:19 +00:00
crontab = true,
2013-05-14 21:13:09 +00:00
delay = true,
exitcodes = true,
init = true,
2022-03-31 01:54:21 +00:00
full = true,
2013-05-14 21:13:09 +00:00
maxDelays = true,
maxProcesses = true,
onAttrib = true,
onCreate = true,
onModify = true,
onDelete = true,
onStartup = true,
onMove = true,
2022-03-31 01:54:21 +00:00
onFull = true,
2013-05-14 21:13:09 +00:00
prepare = true,
source = true,
target = true,
2022-03-11 06:11:39 +00:00
tunnel = true,
}
--
-- On default action the user's on*** scripts are called.
--
2016-12-13 13:41:35 +00:00
default.action = function
(
inlet -- the inlet of the active sync.
)
-- in case of moves getEvent returns the origin and dest of the move
local event, event2 = inlet.getEvent( )
2016-12-13 13:41:35 +00:00
local config = inlet.getConfig( )
local func = config[ 'on'.. event.etype ]
2016-12-13 13:41:35 +00:00
if type( func ) == 'function'
then
func( event, event2 )
end
-- if function didnt change the wait status its not interested
-- in this event -> drop it.
2016-12-13 13:41:35 +00:00
if event.status == 'wait'
then
inlet.discardEvent( event )
end
end
--
-- Default collector.
--
-- Called when collecting a finished child process
--
2016-12-13 13:41:35 +00:00
default.collect = function
(
agent, -- event or event list being collected
exitcode -- the exitcode of the spawned process
)
local config = agent.config
2016-12-13 13:41:35 +00:00
local rc
2016-12-13 13:41:35 +00:00
if config.exitcodes
then
rc = config.exitcodes[ exitcode ]
elseif exitcode == 0
then
rc = 'ok'
else
rc = 'die'
end
-- TODO synchronize with similar code before
2016-12-01 11:40:30 +00:00
if not agent.isList and agent.etype == 'Init'
then
if rc == 'ok'
then
log(
'Normal',
'Startup of ',
agent.source,
' -> ',
agent.target,
' finished.'
)
2018-12-05 21:41:53 +00:00
if settings('onepass')
then
log(
'Normal',
'onepass config set, exiting'
)
terminate( 0 )
end
return 'ok'
2016-12-01 11:40:30 +00:00
elseif rc == 'again'
then
if settings( 'insist' )
2016-12-01 11:40:30 +00:00
then
log(
'Normal',
'Retrying startup of ',
agent.source,
' -> ',
agent.target,
': ',
exitcode
)
return 'again'
else
log(
'Error',
'Temporary or permanent failure on startup of ',
agent.source,
' -> ',
agent.target,
'. Terminating since "insist" is not set.'
)
terminate( -1 )
end
2016-12-13 13:41:35 +00:00
elseif rc == 'die'
then
log(
'Error',
'Failure on startup of ',
agent.source,
' -> ',
agent.target,
'.'
)
terminate( -1 )
else
log(
'Error',
'Unknown exitcode "',
exitcode,
'" on startup of ',
agent.source,
' -> ',
agent.target,
'.'
)
return 'die'
end
end
2016-12-01 11:40:30 +00:00
if agent.isList
then
if rc == 'ok'
then
log(
'Normal',
'Finished a list after exitcode: ',
exitcode
)
2016-12-01 11:40:30 +00:00
elseif rc == 'again'
then
log(
'Normal',
'Retrying a list after exitcode = ',
exitcode
)
2016-12-01 11:40:30 +00:00
elseif rc == 'die'
then
log(
'Error',
2016-05-24 05:54:07 +00:00
'Failure with a list with exitcode = ',
exitcode
)
else
log(
'Error',
'Unknown exitcode "',exitcode,'" with a list'
)
rc = 'die'
end
else
2016-12-01 11:40:30 +00:00
if rc == 'ok'
then
log(
'Normal',
'Finished ',
agent.etype,
' on ',
agent.sourcePath,
' = ',
exitcode
)
elseif rc == 'again'
then
log(
'Normal',
'Retrying ',
agent.etype,
' on ',
agent.sourcePath,
' = ',
exitcode
)
elseif rc == 'die'
then
log(
'Error',
'Failure with ',
agent.etype,
' on ',
agent.sourcePath,
' = ',
exitcode
)
else
2016-12-01 11:40:30 +00:00
log(
'Normal',
'Unknown exitcode "',
exitcode,
'" with ',
agent.etype,
' on ',
agent.sourcePath,
' = ',
exitcode
)
rc = 'die'
end
end
return rc
end
--
-- Called on the Init event sent
-- on (re)initialization of Lsyncd for every sync
--
2016-12-13 13:41:35 +00:00
default.init = function
(
event -- the precreated init event.
)
local config = event.config
2016-12-01 11:40:30 +00:00
local inlet = event.inlet
-- user functions
-- calls a startup if given by user script.
2016-12-13 13:41:35 +00:00
if type( config.onStartup ) == 'function'
2016-12-01 11:40:30 +00:00
then
2016-12-13 13:41:35 +00:00
config.onStartup( event )
-- TODO honor some return codes of startup like "warmstart".
end
2016-12-01 11:40:30 +00:00
if event.status == 'wait'
then
-- user script did not spawn anything
-- thus the blanket event is deleted again.
2016-12-13 13:41:35 +00:00
inlet.discardEvent( event )
end
end
--
-- The collapsor tries not to have more than these delays.
2016-12-13 13:41:35 +00:00
-- So the delay queue does not grow too large
-- since calculation for stacking events is n*log( n ) (or so)
--
default.maxDelays = 1000
--
-- The maximum number of processes Lsyncd will
-- simultanously spawn for this sync.
--
default.maxProcesses = 1
--
-- Exitcodes of rsync and what to do.
-- TODO move to rsync
--
default.rsyncExitCodes = {
--
-- if another config provides the same table
-- this will not be inherited (merged) into that one
--
-- if it does not, integer keys are to be copied
-- verbatim
--
_merge = false,
_verbatim = true,
[ 0 ] = 'ok',
[ 1 ] = 'die',
[ 2 ] = 'die',
[ 3 ] = 'again',
[ 4 ] = 'die',
[ 5 ] = 'again',
[ 6 ] = 'again',
[ 10 ] = 'again',
[ 11 ] = 'again',
[ 12 ] = 'again',
[ 14 ] = 'again',
[ 20 ] = 'again',
[ 21 ] = 'again',
[ 22 ] = 'again',
-- partial transfers are ok, since Lsyncd has registered the event that
-- caused the transfer to be partial and will recall rsync.
[ 23 ] = 'ok',
[ 24 ] = 'ok',
[ 25 ] = 'die',
[ 30 ] = 'again',
[ 35 ] = 'again',
[ 255 ] = 'again',
}
--
-- Exitcodes of ssh and what to do.
--
2016-12-13 13:41:35 +00:00
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',
}
--
2016-12-13 13:41:35 +00:00
-- Minimum seconds between two writes of the status file.
--
default.statusInterval = 10
--
2016-12-13 13:41:35 +00:00
-- Checks all keys to be in the checkgauge.
--
2016-12-13 13:41:35 +00:00
local function check
(
config,
gauge,
subtable,
level
)
2016-12-13 13:41:35 +00:00
for k, v in pairs( config )
do
2018-03-01 10:26:12 +00:00
if not gauge[ k ]
2016-12-13 13:41:35 +00:00
then
error(
2018-03-01 10:26:12 +00:00
'Parameter "' .. subtable .. k .. '" unknown.'
2016-12-13 13:41:35 +00:00
.. ' ( if this is not a typo add it to checkgauge )',
level
);
end
2016-12-13 13:41:35 +00:00
if type( gauge [ k ] ) == 'table'
then
if type( v ) ~= 'table'
then
error(
2018-03-01 10:26:12 +00:00
'Parameter "' .. subtable .. k .. '" must be a table.',
level
)
end
check(
config[ k ],
gauge[ k ],
subtable .. k .. '.',
level + 1
)
end
end
end
2016-12-13 13:41:35 +00:00
default.prepare = function
(
config, -- the config to prepare for
level -- current callback level for error reporting
)
local gauge = config.checkgauge
2018-03-01 10:26:12 +00:00
if not gauge then return end
2012-10-06 12:22:08 +00:00
check( config, gauge, '', level + 1 )
end