--============================================================================ -- 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 --============================================================================ if default then error( 'default already loaded' ) end 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 = { action = true, checkgauge = true, collect = true, delay = true, exitcodes = true, init = true, maxDelays = true, maxProcesses = true, onAttrib = true, onCreate = true, onModify = true, onDelete = true, onStartup = true, onMove = true, prepare = true, source = true, target = true, } -- -- On default action the user's on*** scripts are called. -- default.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 type( func ) == 'function' 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 -- 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', 'Temporary or permanent failure on startup of "', agent.source, '". Terminating since "insist" is not set.' ) terminate( -1 ) end elseif rc == 'die' then log( 'Error', 'Failure on startup of "', agent.source, '".' ) terminate( -1 ) 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 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 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 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 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 -- -- 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. -- 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, subtable, level ) for k, v in pairs( config ) do if not gauge[k] then error( 'Parameter "' .. subtable .. 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 "' .. subtable .. k .. '" must be a table.', level ) end check( config[ k ], gauge[ k ], subtable .. 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 + 1 ) end