diff -Nru lsyncd-2.1.6/ChangeLog lsyncd-2.2.3/ChangeLog
--- lsyncd-2.1.6/ChangeLog 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/ChangeLog 2018-03-09 12:39:11.000000000 +0000
@@ -1,4 +1,73 @@
-15-10-2015: 2.1.6
+2018-03-09: 2.2.3
+ enhaencement: supporting includes with new filter and filterFrom options
+ change: needing now at least Lua 5.2 (Lua 5.1 no longer supported, Lua5.3 supported)
+ change: if the target/targetdir ends with a ':' do not append
+ a trailing '/' to it, since that would change it from homedir to rootdir!
+ add: example for Amazon S3 Bucket (Daniel Miranda)
+ fix: setting stdout/stderr to linebuffer mode.
+ fix: Lua5.3 compatiblity, using load() instead of loadstring()
+ fix: cmake lua detection, will resort to "lua" and "luac" binaries only if
+ more specific suffixes (e.g. luac5.3) are not available
+ fix: test suit, Lua5.3 compatibility (table.unpack)
+
+2017-02-16: 2.2.2
+ fix: checkgauge 'insist'
+ fix: no partial path exlusion tests
+ fix: write pid of forked process in pidfile
+ fix: crash on not reachable target
+ workaround:
+ changed back to filter style rsync calling
+ until https://bugzilla.samba.org/show_bug.cgi?id=12569
+ is fixed and released.
+
+2017-01-05: 2.2.1
+ enhancement: now always using filter lists with rysnc
+ instead of include/exclude lists taking advantage of the new --delete-missing-args
+ parameter to delete files on target.
+ >>> Thus Lsyncd 2.2.1 needs rsync >= 3.1.0
+ change: added "tests" make target to run all the tests.
+ fix: crash due to typo in changed ^path, ^pathdir, ^pathname
+
+2017-01-04: 2.2.0
+ enhancement: add rsync options:
+ "append",
+ "append_verify",
+ "backup",
+ "backup_dir",
+ "chmod",
+ "chown",
+ "copy_dirlinks",
+ "existing",
+ "groupmap",
+ "omit_dir_times",
+ "omit_link_times",
+ "suffix,"
+ "usermap",
+ enhancement: settings{ } now checks for unknown entries and errors if so.
+ change: Level3 scripts ^path,^pathdir and ^pathname now don't start with a slash.
+ change: Lsyncd now writes a startup log message before daemonizing
+ does in case logging fails, it is recognized before it cannot
+ message anything about it, since it deamonized
+ change: compatible with Lua5.3 (along with 5.1 and 5.2)
+ change: _verbatim forced for 'exitcodes' entry.
+ change: manpage is not rebuild by default.
+ it is provided precompiled.
+ change: faulty/deprecated config files that use settings = { ... }, with equal sign
+ are no longer worked around.
+ change: default.direct now calls copy with -p
+ fix: potential race conditions:
+ default.rsyncssh will now channel deletes also through rsync and treats moves
+ as blocking events.
+ fix: ']' is not escaped for rsync rules, since rsync only applies
+ doesn't applie pattern matching if no other pattern chars
+ are found.
+ fix: Shell injection hole close for default.direct on mv commands. (Marcin Szewczyk)
+ fix: Crash of default-direct when source doesn't exit (Michael Ploujnikov)
+ fix: fixed faulty event replacement,
+ a race condition noticed by extensive default.rsyncssh testing
+ changed Delays were not reflected in Events
+
+2015-10-15: 2.1.6
enhancement: Lsyncd now locks its pidfile
enhancement: added ssh.identifyFile and ssh.options options
enhancement: added rsync inplace option
@@ -12,7 +81,7 @@
change: now removes its pidfile on INT and TERM signals
change: changed build system from autotools to cmake
-07-06-2013: 2.1.5
+2013-06-07: 2.1.5
enhancement: Added rsync options: bwlimit, timeout
fix: Specifying ssh port no longer overwrites the last rsync option
fix: rsync option password_file is now accepted
@@ -25,22 +94,22 @@
change: a2x is no longer checked by configure script.
should not be needed when building from tarball
-24-11-2012: 2.1.4
+2012-11-24: 2.1.4
fix: making ssh custom port changes work with ssh and rsync
-23-11-2012: 2.1.3
+2012-11-23: 2.1.3
fix: fixed 2 crash conditions due to failure to read 'uSettings'
-03-11-2012: 2.1.2
+2012-11-03: 2.1.2
fix: added excludeFrom to checkgauge (thx to DavidWittman)
fix: fixed rsync option computation
enhancement: added password_file file option to rsync
-27-10-2012: 2.1.1
+2012-10-27: 2.1.1
fix: fix rsync.rsh, rsync.rsync_path, rsync.tmp_dir, rsync._extra parameters
thanks go to Birger Schmidt for this fix.
-23-10-2012: 2.1.0
+2012-10-23: 2.1.0
fix: fail startup if settings.inist is false and one of the target hosts fails
fix: in case of waiting for processes during restart only logs this state now once a minute
rather than filling the log crazy about it
@@ -55,7 +124,7 @@
change: Lsyncd now exits with exitcode 143 on TERM signal
change: settings is now be used as call like settings{...} instead of settings = {...}
-04-04-2012: 2.0.7
+2012-04-04: 2.0.7
fix: closed a memory leak due to not correct configured weak tables
fix: default.direct, do not use on OSX unrecognized option -t on modify
fix: default.direct, typo leading to compile error
@@ -65,7 +134,7 @@
change: removed --with-default-runner since it was broken, and will be replaced by something
more generic in future
-16-02-2012: 2.0.6
+2012-02-16: 2.0.6
fix: no longer stops syslogging on HUP signals
fix: OSX event watcher no longer misses moves into and out of the watch tree
fix: not refinding a relative path to the config file in case of HUP.
@@ -96,7 +165,7 @@
default.rsyncssh: does not add --delete to rsync, and does not use rm via ssh tunnel
default.direct: does not add --delete to startup rsync and does not use rm
-25-08-2011: 2.0.5
+2011-08-25: 2.0.5
fix: Lsyncd will now terminate if it inotify watching exceeds
its preset limit.
fix: rsync error exit code 12 now results in retries.
@@ -125,14 +194,14 @@
enhancement: readdir(path) is available to userscripts, reads the contents
of a directory.
-27-03-2011: 2.0.4
+2011-03-27: 2.0.4
enhancement: new setting options logident, logfacility
fix: moving filenames with spaces through ssh
fix: excludes containing chars % $ ( ) . [ ] + -
fix: various typos
change: api, settings.statusInterval instead of settings.statusIntervall
-25-02-2011: 2.0.3
+2011-02-25: 2.0.3
enhancement: new default target --direct using /bin/ binaries
to keep to local dirs in sync (and by default
not preserving ownership)
@@ -147,7 +216,7 @@
change: leave lua apichecking enabled by default.
-20-01-2011: 2.0.2
+2011-01-20: 2.0.2
fix: exclude rules not terminated with '/' now match a file
or dir named exactly the same not starting with.
fix: pass exclude rules to the startup sync
@@ -155,7 +224,7 @@
partial path than on syncs
fix: properly close pipes that needed more than one write.
-11-01-2011: 2.0.1
+2011-01-11: 2.0.1
fix: write pidfile after daemonize()
fix: fixed weak tables that allowed garbage collector to collect
event lists too eraly.
@@ -163,7 +232,7 @@
change: added OSX fsevents interface, disabled in autoconf by default
since still very experimental and limited to OSX 10.5 only.
-02-12-2010: 2.0.0
+2010-12-02: 2.0.0
a complete recoding!
change: format of command line arguments changed completly.
@@ -185,7 +254,7 @@
change: manpage is now written in asciidoc
change: most more complex logic of Lsyncd is now written in Lua.
-04-10-2010: 1.39
+2010-10-04: 1.39
enhancement: call action for multiple targets simultanously
fix: correctly accept from config xml
fix: correctly close and free the inotify file descriptor in case of restart
@@ -193,7 +262,7 @@
fix: when delay=0 a bug always called rsync file filter even when in
directory mode
-01-09-2010: 1.38
+2010-09-01: 1.38
enhancement: implemented file filters for singular operations
enhancement: added --singular parameter for single file calls
fix: fixed --dryrun messages
@@ -201,7 +270,7 @@
being kill -HUPed
internal: printout the actual binary called when --debug specified
-05-08-2010: 1.37
+2010-08-05: 1.37
enhancement: react on HUP signals (interpreted as complete restart)
enhancement: inotifies are configureable
enhancement: --no-startup skips the startup calls
@@ -212,22 +281,22 @@
internal: removed the need of the "tosync" stack
internal: use more pointers instead of indexes
-11-07-2010: 1.34
+2010-07-11: 1.34
fix: logging segfault on 64bit systems
changed: man page location, spellings
-05-06-2010: 1.33
+2010-06-05: 1.33
fix: exlude file argument passing to rsync
fix: allow exlude files specified for individual sources
fix/enhancement: exlusions will be compared with extended
path files allowing sub dirs to be excluded.
enhancement: allow delays and call aggregation
-05-01-2009: Release of lsyncd 1.26
+2009-01-05: Release of lsyncd 1.26
fix: segfault on multitargets
changed meaning of "version" tag in lsyncd.conf.xml
-14-12-2008: Release of lsyncd 1.25
+2008-12-14: Release of lsyncd 1.25
fix: mv dir and cp -r working
fix: working with reiserfs
enhancement: config files
@@ -238,5 +307,5 @@
lots of smaller stuff here and there ...
Thanks to all contributers!
-05-12-2007: Release of lsyncd 1.0
+2007-12-05: Release of lsyncd 1.0
diff -Nru lsyncd-2.1.6/cmake/FindLua.cmake lsyncd-2.2.3/cmake/FindLua.cmake
--- lsyncd-2.1.6/cmake/FindLua.cmake 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/cmake/FindLua.cmake 2018-03-09 12:39:11.000000000 +0000
@@ -1,7 +1,7 @@
# Locate Lua library
# This module defines
# LUA_EXECUTABLE, if found
-# LUA_FOUND, if false, do not try to link to Lua
+# LUA_FOUND, if false, do not try to link to Lua
# LUA_LIBRARIES
# LUA_INCLUDE_DIR, where to find lua.h
# LUA_VERSION_STRING, the version of Lua found (since CMake 2.8.8)
@@ -27,26 +27,22 @@
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
#
-# The required version of Lua can be specified using the
-# standard syntax, e.g. FIND_PACKAGE(Lua 5.1)
-# Otherwise the module will search for any available Lua implementation
+# This module will try to find the newest Lua version down to 5.2
# Always search for non-versioned lua first (recommended)
SET(_POSSIBLE_LUA_INCLUDE include include/lua)
-SET(_POSSIBLE_LUA_EXECUTABLE lua)
-SET(_POSSIBLE_LUA_LIBRARY lua)
+#SET(_POSSIBLE_LUA_EXECUTABLE lua)
+#SET(_POSSIBLE_LUA_COMPILER luac)
+#SET(_POSSIBLE_LUA_LIBRARY lua)
# Determine possible naming suffixes (there is no standard for this)
-IF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR)
- SET(_POSSIBLE_SUFFIXES "${Lua_FIND_VERSION_MAJOR}${Lua_FIND_VERSION_MINOR}" "${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}" "-${Lua_FIND_VERSION_MAJOR}.${Lua_FIND_VERSION_MINOR}")
-ELSE(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR)
- SET(_POSSIBLE_SUFFIXES "52" "5.2" "-5.2" "51" "5.1" "-5.1")
-ENDIF(Lua_FIND_VERSION_MAJOR AND Lua_FIND_VERSION_MINOR)
+SET(_POSSIBLE_SUFFIXES "52" "5.2" "-5.2" "53" "5.3" "-5.3" "")
# Set up possible search names and locations
FOREACH(_SUFFIX ${_POSSIBLE_SUFFIXES})
LIST(APPEND _POSSIBLE_LUA_INCLUDE "include/lua${_SUFFIX}")
LIST(APPEND _POSSIBLE_LUA_EXECUTABLE "lua${_SUFFIX}")
+ LIST(APPEND _POSSIBLE_LUA_COMPILER "luac${_SUFFIX}")
LIST(APPEND _POSSIBLE_LUA_LIBRARY "lua${_SUFFIX}")
ENDFOREACH(_SUFFIX)
@@ -55,6 +51,11 @@
NAMES ${_POSSIBLE_LUA_EXECUTABLE}
)
+# Find the lua executable
+FIND_PROGRAM(LUA_COMPILER
+ NAMES luac5.3 ${_POSSIBLE_LUA_COMPILER}
+)
+
# Find the lua header
FIND_PATH(LUA_INCLUDE_DIR lua.h
HINTS
@@ -72,7 +73,7 @@
)
# Find the lua library
-FIND_LIBRARY(LUA_LIBRARY
+FIND_LIBRARY(LUA_LIBRARY
NAMES ${_POSSIBLE_LUA_LIBRARY}
HINTS
$ENV{LUA_DIR}
@@ -108,11 +109,11 @@
ENDIF()
INCLUDE(FindPackageHandleStandardArgs)
-# handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if
+# handle the QUIETLY and REQUIRED arguments and set LUA_FOUND to TRUE if
# all listed variables are TRUE
FIND_PACKAGE_HANDLE_STANDARD_ARGS(Lua
REQUIRED_VARS LUA_LIBRARIES LUA_INCLUDE_DIR
VERSION_VAR LUA_VERSION_STRING)
-MARK_AS_ADVANCED(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY LUA_EXECUTABLE)
+MARK_AS_ADVANCED(LUA_INCLUDE_DIR LUA_LIBRARIES LUA_LIBRARY LUA_MATH_LIBRARY LUA_EXECUTABLE LUA_COMPILER)
diff -Nru lsyncd-2.1.6/CMakeLists.txt lsyncd-2.2.3/CMakeLists.txt
--- lsyncd-2.1.6/CMakeLists.txt 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/CMakeLists.txt 2018-03-09 12:39:11.000000000 +0000
@@ -1,12 +1,12 @@
# preamble
project( Lsyncd )
cmake_minimum_required( VERSION 2.8 )
-set( LSYNCD_VERSION 2.1.6 )
+set( LSYNCD_VERSION 2.2.3 )
set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/" )
# finding Lua
-find_package(Lua REQUIRED)
+find_package( Lua REQUIRED )
include_directories ( ${LUA_INCLUDE_DIR} )
@@ -45,26 +45,26 @@
# building and compiling the part of lsyncd written in Lua
# also called "runner"
-add_custom_command( OUTPUT runner.c
- COMMAND ${CMAKE_COMMAND} -E echo "Generating built-in runner linkable"
+add_custom_command( OUTPUT runner.c
+ COMMAND ${CMAKE_COMMAND} -E echo "Generating built-in runner linkable"
COMMAND ${LUA_EXECUTABLE} ${PROJECT_SOURCE_DIR}/bin2carray.lua runner.out runner runner.c
- DEPENDS runner.out
-)
+ DEPENDS runner.out
+)
# this supposes the Lua compiler 'luac' is sitting right next to the Lua interpreter 'lua'
add_custom_command( OUTPUT runner.out
- COMMAND ${CMAKE_COMMAND} -E echo "Compiling built-in runner"
- COMMAND ${LUA_EXECUTABLE}c -o runner.out ${PROJECT_SOURCE_DIR}/lsyncd.lua
- DEPENDS ${PROJECT_SOURCE_DIR}/lsyncd.lua
-)
+ COMMAND ${CMAKE_COMMAND} -E echo "Compiling built-in runner"
+ COMMAND ${LUA_COMPILER} -o runner.out ${PROJECT_SOURCE_DIR}/lsyncd.lua
+ DEPENDS ${PROJECT_SOURCE_DIR}/lsyncd.lua
+)
# building and compiling the built-in default configs:
# rsync rysnc-ssh and direct
-add_custom_command( OUTPUT defaults.c
- COMMAND ${CMAKE_COMMAND} -E echo "Generating built-in default configs"
+add_custom_command( OUTPUT defaults.c
+ COMMAND ${CMAKE_COMMAND} -E echo "Generating built-in default configs"
COMMAND ${LUA_EXECUTABLE} ${PROJECT_SOURCE_DIR}/bin2carray.lua defaults.out defaults defaults.c
- DEPENDS defaults.out
-)
+ DEPENDS defaults.out
+)
set( DEFAULT_CONFIGS
${PROJECT_SOURCE_DIR}/default.lua
@@ -74,22 +74,38 @@
)
add_custom_command( OUTPUT defaults.out
- COMMAND ${CMAKE_COMMAND} -E echo "Compiling built-in default configs"
- COMMAND ${LUA_EXECUTABLE}c -o defaults.out ${DEFAULT_CONFIGS}
- DEPENDS ${DEFAULT_CONFIGS}
-)
+ COMMAND ${CMAKE_COMMAND} -E echo "Compiling built-in default configs"
+ COMMAND ${LUA_COMPILER} -o defaults.out ${DEFAULT_CONFIGS}
+ DEPENDS ${DEFAULT_CONFIGS}
+)
# the manpage
-add_custom_command( OUTPUT doc/lsyncd.1
- COMMAND ${CMAKE_COMMAND} -E echo "Updating the manpage"
- COMMAND a2x --format=manpage doc/lsyncd.1.txt
- DEPENDS doc/lsyncd.1.txt
+add_custom_target( manpage
+ COMMAND ${CMAKE_COMMAND} -E echo "Updating the manpage"
+ COMMAND a2x --format=manpage doc/manpage/lsyncd.1.txt
+ DEPENDS doc/manpage/lsyncd.1.txt
+)
+
+add_custom_target( tests
+ COMMAND echo "Running the tests"
+ COMMAND echo "Note you are expected to:"
+ COMMAND echo " * have lua-posix installed"
+ COMMAND echo " * have a passwordless ssh access to localhost"
+ COMMAND ${LUA_EXECUTABLE} tests/schedule.lua
+ COMMAND ${LUA_EXECUTABLE} tests/l4rsyncdata.lua
+ COMMAND ${LUA_EXECUTABLE} tests/filter-rsync.lua
+ COMMAND ${LUA_EXECUTABLE} tests/exclude-rsync.lua
+ COMMAND ${LUA_EXECUTABLE} tests/exclude-rsyncssh.lua
+ COMMAND ${LUA_EXECUTABLE} tests/churn-rsync.lua
+ COMMAND ${LUA_EXECUTABLE} tests/churn-rsyncssh.lua
+ COMMAND ${LUA_EXECUTABLE} tests/churn-direct.lua
+ COMMAND echo "Finished all successfull!"
)
-add_custom_target( manpage ALL DEPENDS doc/lsyncd.1 )
# compiling and linking it all together
add_executable( lsyncd ${LSYNCD_SRC} )
target_link_libraries( lsyncd ${LUA_LIBRARIES} )
install( TARGETS lsyncd RUNTIME DESTINATION bin )
-install( FILES doc/lsyncd.1 DESTINATION man)
+install( FILES doc/manpage/lsyncd.1 DESTINATION man )
+
diff -Nru lsyncd-2.1.6/debian/changelog lsyncd-2.2.3/debian/changelog
--- lsyncd-2.1.6/debian/changelog 2016-12-18 19:21:28.000000000 +0000
+++ lsyncd-2.2.3/debian/changelog 2018-12-31 12:25:54.000000000 +0000
@@ -1,3 +1,23 @@
+lsyncd (2.2.3-1) unstable; urgency=medium
+
+ [ Ondřej Nový ]
+ * d/copyright: Use https protocol in Format field
+ * d/control: Set Vcs-* to salsa.debian.org
+
+ [ Jan Dittberner ]
+ * New upstream version (Closes: #862747)
+ * Refresh debian/patches/out-of-tree-manpage-build.patch
+ * Update lua dependencies to lua 5.3
+ * Fix paths in debian/lsyncd.docs
+ * Add DEB_BUILD_MAINT_OPTIONS = hardening=+all to debian/rules to
+ improve hardening
+ * debian/control: bump Standards-Version to 4.3.0 (no changes)
+ * Update debian/copyright
+ * debian/control: Update to debhelper >= 10, use compat level 10 in
+ debian/compat
+
+ -- Jan Dittberner Mon, 31 Dec 2018 13:25:54 +0100
+
lsyncd (2.1.6-1) unstable; urgency=medium
* New upstream release
diff -Nru lsyncd-2.1.6/debian/compat lsyncd-2.2.3/debian/compat
--- lsyncd-2.1.6/debian/compat 2016-12-18 19:21:28.000000000 +0000
+++ lsyncd-2.2.3/debian/compat 2018-12-31 12:25:54.000000000 +0000
@@ -1 +1 @@
-9
+10
diff -Nru lsyncd-2.1.6/debian/control lsyncd-2.2.3/debian/control
--- lsyncd-2.1.6/debian/control 2016-12-18 19:21:28.000000000 +0000
+++ lsyncd-2.2.3/debian/control 2018-12-31 12:25:54.000000000 +0000
@@ -2,26 +2,26 @@
Section: admin
Priority: optional
Maintainer: Jan Dittberner
-Standards-Version: 3.9.8
+Standards-Version: 4.3.0
Build-Depends: asciidoc,
cmake,
- debhelper (>= 9),
+ debhelper (>= 10),
docbook-xml,
docbook-xsl,
dpkg-dev (>= 1.16.1~),
- liblua5.1-0-dev,
+ liblua5.3-dev,
libxml2-dev,
libxml2-utils,
- lua5.1,
+ lua5.3,
pkg-config,
xsltproc
Homepage: https://github.com/axkibe/lsyncd
-Vcs-Git: git://anonscm.debian.org/collab-maint/lsyncd.git
-Vcs-Browser: http://anonscm.debian.org/gitweb/?p=collab-maint/lsyncd.git
+Vcs-Git: https://salsa.debian.org/debian/lsyncd.git
+Vcs-Browser: https://salsa.debian.org/debian/lsyncd
Package: lsyncd
Architecture: any
-Depends: lsb-base (>= 3.0-6), lua5.1, rsync, ${misc:Depends}, ${shlibs:Depends}
+Depends: lsb-base (>= 3.0-6), lua5.3, rsync, ${misc:Depends}, ${shlibs:Depends}
Description: daemon to synchronize local directories using rsync
Lsyncd (Live syncing mirror daemon) uses rsync to synchronize local
directories with a remote machine running rsyncd. Lsyncd watches
diff -Nru lsyncd-2.1.6/debian/copyright lsyncd-2.2.3/debian/copyright
--- lsyncd-2.1.6/debian/copyright 2016-12-18 19:21:28.000000000 +0000
+++ lsyncd-2.2.3/debian/copyright 2018-12-31 12:25:54.000000000 +0000
@@ -1,10 +1,10 @@
-Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
+Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: lsyncd
Upstream-Contact: Axel Kittenberger
Source: https://github.com/axkibe/lsyncd
Files: *
-Copyright: 2007-2015, Axel Kittenberger
+Copyright: 2007-2018, Axel Kittenberger
Semi Malinen
Jürgen "README" Mangler
Eugene Sanivsky
@@ -12,7 +12,7 @@
License: GPL-2+
Files: debian/*
-Copyright: 2009-2016, Jan Dittberner
+Copyright: 2009-2018, Jan Dittberner
License: GPL-2+
License: GPL-2+
diff -Nru lsyncd-2.1.6/debian/lsyncd.docs lsyncd-2.2.3/debian/lsyncd.docs
--- lsyncd-2.1.6/debian/lsyncd.docs 2016-12-18 19:21:28.000000000 +0000
+++ lsyncd-2.2.3/debian/lsyncd.docs 2018-12-31 12:25:54.000000000 +0000
@@ -1 +1,2 @@
-doc/lsyncd.1.txt
+doc/manpage/lsyncd.1.txt
+README.md
diff -Nru lsyncd-2.1.6/debian/patches/out-of-tree-manpage-build.patch lsyncd-2.2.3/debian/patches/out-of-tree-manpage-build.patch
--- lsyncd-2.1.6/debian/patches/out-of-tree-manpage-build.patch 2016-12-18 19:21:28.000000000 +0000
+++ lsyncd-2.2.3/debian/patches/out-of-tree-manpage-build.patch 2018-12-31 12:25:54.000000000 +0000
@@ -5,19 +5,20 @@
+++ b/CMakeLists.txt
@@ -82,8 +82,9 @@
# the manpage
- add_custom_command( OUTPUT doc/lsyncd.1
- COMMAND ${CMAKE_COMMAND} -E echo "Updating the manpage"
-- COMMAND a2x --format=manpage doc/lsyncd.1.txt
-- DEPENDS doc/lsyncd.1.txt
-+ COMMAND ${CMAKE_COMMAND} -E make_directory doc
-+ COMMAND a2x --format=manpage --destination-dir=doc "${PROJECT_SOURCE_DIR}/doc/lsyncd.1.txt"
-+ DEPENDS "${PROJECT_SOURCE_DIR}/doc/lsyncd.1.txt"
+ add_custom_target( manpage
+ COMMAND ${CMAKE_COMMAND} -E echo "Updating the manpage"
+- COMMAND a2x --format=manpage doc/manpage/lsyncd.1.txt
+- DEPENDS doc/manpage/lsyncd.1.txt
++ COMMAND ${CMAKE_COMMAND} -E make_directory doc/manpage
++ COMMAND a2x --format=manpage --destination-dir=doc/manpage ${PROJECT_SOURCE_DIR}/doc/manpage/lsyncd.1.txt
++ DEPENDS ${PROJECT_SOURCE_DIR}/doc/manpage/lsyncd.1.txt
)
- add_custom_target( manpage ALL DEPENDS doc/lsyncd.1 )
-@@ -92,4 +93,4 @@
+ add_custom_target( tests
+@@ -107,5 +108,5 @@
target_link_libraries( lsyncd ${LUA_LIBRARIES} )
install( TARGETS lsyncd RUNTIME DESTINATION bin )
--install( FILES doc/lsyncd.1 DESTINATION man)
-+install( FILES doc/lsyncd.1 DESTINATION share/man/man1)
+-install( FILES doc/manpage/lsyncd.1 DESTINATION man )
++install( FILES doc/manpage/lsyncd.1 DESTINATION share/man/man1 )
+
diff -Nru lsyncd-2.1.6/debian/rules lsyncd-2.2.3/debian/rules
--- lsyncd-2.1.6/debian/rules 2016-12-18 19:21:28.000000000 +0000
+++ lsyncd-2.2.3/debian/rules 2018-12-31 12:25:54.000000000 +0000
@@ -1,7 +1,10 @@
#!/usr/bin/make -f
+DEB_BUILD_MAINT_OPTIONS = hardening=+all
DPKG_EXPORT_BUILDFLAGS = 1
include /usr/share/dpkg/buildflags.mk
+# Uncomment this to turn on verbose mode.
+#export DH_VERBOSE=1
override_dh_auto_configure:
dh_auto_configure $@
diff -Nru lsyncd-2.1.6/default-direct.lua lsyncd-2.2.3/default-direct.lua
--- lsyncd-2.1.6/default-direct.lua 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/default-direct.lua 2018-03-09 12:39:11.000000000 +0000
@@ -62,6 +62,7 @@
spawn(
event,
'/bin/mkdir',
+ '--',
event.targetPath
)
else
@@ -69,6 +70,8 @@
spawn(
event,
'/bin/cp',
+ '-p',
+ '--',
event.sourcePath,
event.targetPathdir
)
@@ -79,6 +82,8 @@
end
spawn(event,
'/bin/cp',
+ '-p',
+ '--',
event.sourcePath,
event.targetPathdir
)
@@ -99,7 +104,7 @@
error('Refusing to erase your harddisk!')
end
- spawn(event, '/bin/rm', '-rf', tp)
+ spawn(event, '/bin/rm', '-rf', '--', tp)
elseif event.etype == 'Move' then
local tp = event.targetPath
@@ -109,13 +114,13 @@
error('Refusing to erase your harddisk!')
end
- local command = '/bin/mv $1 $2 || /bin/rm -rf $1'
+ local command = '/bin/mv -- "$1" "$2" || /bin/rm -rf -- "$1"'
if
config.delete ~= true and
config.delete ~= 'running'
then
- command = '/bin/mv $1 $2'
+ command = '/bin/mv -- "$1" "$2"'
end
spawnShell(
@@ -142,8 +147,10 @@
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
+ 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 "',
diff -Nru lsyncd-2.1.6/default.lua lsyncd-2.2.3/default.lua
--- lsyncd-2.1.6/default.lua 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/default.lua 2018-03-09 12:39:11.000000000 +0000
@@ -8,7 +8,8 @@
-- Authors: Axel Kittenberger
--============================================================================
-if default then
+if default
+then
error( 'default already loaded' )
end
@@ -56,21 +57,26 @@
--
-- On default action the user's on*** scripts are called.
--
-default.action = function( inlet )
-
+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( )
+
local config = inlet.getConfig( )
local func = config[ 'on'.. event.etype ]
- if type( func ) == 'function' then
+ 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
+ if event.status == 'wait'
+ then
inlet.discardEvent( event )
end
@@ -82,31 +88,51 @@
--
-- Called when collecting a finished child process
--
-default.collect = function( agent, exitcode )
-
+default.collect = function
+(
+ agent, -- event or event list being collected
+ exitcode -- the exitcode of the spawned process
+)
local config = agent.config
+
local rc
- if config.exitcodes then
- rc = config.exitcodes[exitcode]
- elseif exitcode == 0 then
+ 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.')
+ if not agent.isList and agent.etype == 'Init'
+ then
+ if rc == 'ok'
+ then
+ log(
+ 'Normal',
+ 'Startup of ',
+ agent.source,
+ ' -> ',
+ agent.target,
+ ' finished.'
+ )
+
return 'ok'
- elseif rc == 'again' then
- if settings('insist') then
+ elseif rc == 'again'
+ then
+ if settings( 'insist' )
+ then
log(
'Normal',
- 'Retrying startup of "',
+ 'Retrying startup of ',
agent.source,
- '": ',
+ ' -> ',
+ agent.target,
+ ': ',
exitcode
)
@@ -114,19 +140,24 @@
else
log(
'Error',
- 'Temporary or permanent failure on startup of "',
+ 'Temporary or permanent failure on startup of ',
agent.source,
- '". Terminating since "insist" is not set.'
+ ' -> ',
+ agent.target,
+ '. Terminating since "insist" is not set.'
)
terminate( -1 )
end
- elseif rc == 'die' then
+ elseif rc == 'die'
+ then
log(
'Error',
- 'Failure on startup of "',
+ 'Failure on startup of ',
agent.source,
- '".'
+ ' -> ',
+ agent.target,
+ '.'
)
terminate( -1 )
@@ -135,31 +166,37 @@
'Error',
'Unknown exitcode "',
exitcode,
- '" on startup of "',
+ '" on startup of ',
agent.source,
- '".'
+ ' -> ',
+ agent.target,
+ '.'
)
return 'die'
end
end
- if agent.isList then
- if rc == 'ok' then
+ if agent.isList
+ then
+ if rc == 'ok'
+ then
log(
'Normal',
'Finished a list after exitcode: ',
exitcode
)
- elseif rc == 'again' then
+ elseif rc == 'again'
+ then
log(
'Normal',
'Retrying a list after exitcode = ',
exitcode
)
- elseif rc == 'die' then
+ elseif rc == 'die'
+ then
log(
'Error',
- 'Failure with a list width exitcode = ',
+ 'Failure with a list with exitcode = ',
exitcode
)
else
@@ -171,15 +208,52 @@
rc = 'die'
end
else
- 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)
+ 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
- log('Normal', 'Unknown exitcode "',exitcode,'" with ', agent.etype,
- ' on ',agent.sourcePath,' = ',exitcode)
+ log(
+ 'Normal',
+ 'Unknown exitcode "',
+ exitcode,
+ '" with ',
+ agent.etype,
+ ' on ',
+ agent.sourcePath,
+ ' = ',
+ exitcode
+ )
+
rc = 'die'
end
end
@@ -192,29 +266,35 @@
-- Called on the Init event sent
-- on (re)initialization of Lsyncd for every sync
--
-default.init = function(event)
+default.init = function
+(
+ event -- the precreated init 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)
+ if type( config.onStartup ) == 'function'
+ then
+ config.onStartup( event )
-- TODO honor some return codes of startup like "warmstart".
end
- if event.status == 'wait' then
+ if event.status == 'wait'
+ then
-- user script did not spawn anything
-- thus the blanket event is deleted again.
- inlet.discardEvent(event)
+ 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)
+-- So the delay queue does not grow too large
+-- since calculation for stacking events is n*log( n ) (or so)
--
default.maxDelays = 1000
@@ -273,8 +353,8 @@
--
-- Exitcodes of ssh and what to do.
--
-default.sshExitCodes = {
-
+default.sshExitCodes =
+{
--
-- if another config provides the same table
-- this will not be inherited (merged) into that one
@@ -291,46 +371,40 @@
--
--- Minimum seconds between two writes of a status file.
+-- Minimum seconds between two writes of the status file.
--
default.statusInterval = 10
--
--- checks all keys to be in the checkgauge
+-- Checks all keys to be in the checkgauge.
--
-
-local function check(
+local function check
+(
config,
gauge,
subtable,
level
)
- for k, v in pairs( config ) do
-
- if not gauge[k] then
+ 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)',
+ '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
-
+ if type( gauge [ k ] ) == 'table'
+ then
+ if type( v ) ~= 'table'
+ then
error(
- 'Parameter "'
- .. subtable
- .. k
- .. '" must be a table.',
+ 'Parameter "' .. subtable .. k .. '" must be a table.',
level
)
-
end
check(
@@ -339,18 +413,20 @@
subtable .. k .. '.',
level + 1
)
-
end
end
end
-default.prepare = function( config, level )
+
+default.prepare = function
+(
+ config, -- the config to prepare for
+ level -- current callback level for error reporting
+)
local gauge = config.checkgauge
- if not gauge then
- return
- end
+ if not gauge then return end
check( config, gauge, '', level + 1 )
end
diff -Nru lsyncd-2.1.6/default-rsync.lua lsyncd-2.2.3/default-rsync.lua
--- lsyncd-2.1.6/default-rsync.lua 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/default-rsync.lua 2018-03-09 12:39:11.000000000 +0000
@@ -16,16 +16,9 @@
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-if not default
-then
- error( 'default not loaded' )
-end
-
+if not default then error( 'default not loaded' ) end
-if default.rsync
-then
- error( 'default-rsync already loaded' )
-end
+if default.rsync then error( 'default-rsync already loaded' ) end
local rsync = { }
@@ -49,20 +42,31 @@
delete = true,
exclude = true,
excludeFrom = true,
+ filter = true,
+ filterFrom = true,
target = true,
rsync = {
acls = true,
+ append = true,
+ append_verify = true,
archive = true,
+ backup = true,
+ backup_dir = true,
binary = true,
bwlimit = true,
checksum = true,
+ chown = true,
+ chmod = true,
compress = true,
+ copy_dirlinks = true,
copy_links = true,
cvs_exclude = true,
dry_run = true,
executability = true,
+ existing = true,
group = true,
+ groupmap = true,
hard_links = true,
ignore_times = true,
inplace = true,
@@ -71,6 +75,8 @@
keep_dirlinks = true,
links = true,
one_file_system = true,
+ omit_dir_times = true,
+ omit_link_times = true,
owner = true,
password_file = true,
perms = true,
@@ -80,10 +86,12 @@
rsh = true,
rsync_path = true,
sparse = true,
+ suffix = true,
temp_dir = true,
timeout = true,
times = true,
update = true,
+ usermap = true,
verbose = true,
whole_file = true,
xattrs = true,
@@ -93,29 +101,44 @@
--
+-- Returns true for non Init and Blanket events.
+--
+local eventNotInitBlank =
+ function
+(
+ event
+)
+ return event.etype ~= 'Init' and event.etype ~= 'Blanket'
+end
+
+
+--
-- Spawns rsync for a list of events
--
--- Exlcusions are already handled by not having
+-- Exclusions are already handled by not having
-- events for them.
--
-rsync.action = function( inlet )
+rsync.action = function
+(
+ inlet
+)
+ local config = inlet.getConfig( )
- --
-- gets all events ready for syncing
- --
- local elist = inlet.getEvents(
- function(event)
- return event.etype ~= 'Init' and event.etype ~= 'Blanket'
- end
- )
+ local elist = inlet.getEvents( eventNotInitBlank )
+
+ -- gets the list of paths for the event list
+ -- deletes create multi match patterns
+ local paths = elist.getPaths( )
--
-- Replaces what rsync would consider filter rules by literals
--
- local function sub( p )
- if not p then
- return
- end
+ local function sub
+ (
+ p -- pattern
+ )
+ if not p then return end
return p:
gsub( '%?', '\\?' ):
@@ -130,8 +153,14 @@
-- Deletes create multi match patterns
--
local paths = elist.getPaths(
- function( etype, path1, path2 )
- if string.byte( path1, -1 ) == 47 and etype == 'Delete' then
+ function
+ (
+ etype, -- event type
+ path1, -- path
+ path2 -- path to for move events
+ )
+ if string.byte( path1, -1 ) == 47 and etype == 'Delete'
+ then
return sub( path1 )..'***', sub( path2 )
else
return sub( path1 ), sub( path2 )
@@ -139,74 +168,65 @@
end
)
- --
-- stores all filters by integer index
- --
local filterI = { }
- --
- -- Stores all filters with path index
- --
+ -- stores all filters with path index
local filterP = { }
- --
- -- Adds one path to the filter
- --
- local function addToFilter( path )
-
- if filterP[ path ] then
- return
- end
+ -- adds one path to the filter
+ local function addToFilter
+ (
+ path
+ )
+ if filterP[ path ] then return end
filterP[ path ] = true
table.insert( filterI, path )
end
+ -- adds a path to the filter.
--
- -- Adds a path to the filter.
- --
- -- Rsync needs to have entries for all steps in the path,
+ -- rsync needs to have entries for all steps in the path,
-- so the file for example d1/d2/d3/f1 needs following filters:
-- 'd1/', 'd1/d2/', 'd1/d2/d3/' and 'd1/d2/d3/f1'
- --
- for _, path in ipairs( paths ) do
-
- if path and path ~= '' then
-
- addToFilter(path)
+ for _, path in ipairs( paths )
+ do
+ if path and path ~= ''
+ then
+ addToFilter( path )
local pp = string.match( path, '^(.*/)[^/]+/?' )
- while pp do
- addToFilter(pp)
+ while pp
+ do
+ addToFilter( pp )
+
pp = string.match( pp, '^(.*/)[^/]+/?' )
end
-
end
-
end
- local filterS = table.concat( filterI, '\n' )
- local filter0 = table.concat( filterI, '\000' )
-
log(
'Normal',
'Calling rsync with filter-list of new/modified files/dirs\n',
- filterS
+ table.concat( filterI, '\n' )
)
local config = inlet.getConfig( )
+
local delete = nil
- if config.delete == true or config.delete == 'running' then
+ if config.delete == true or config.delete == 'running'
+ then
delete = { '--delete', '--ignore-errors' }
end
spawn(
elist,
config.rsync.binary,
- '<', filter0,
+ '<', table.concat( filterI, '\000' ),
config.rsync._computed,
'-r',
delete,
@@ -217,35 +237,108 @@
config.source,
config.target
)
-
end
+----
+---- NOTE: This optimized version can be used once
+---- https://bugzilla.samba.org/show_bug.cgi?id=12569
+---- is fixed.
+----
+---- Spawns rsync for a list of events
+----
+---- Exclusions are already handled by not having
+---- events for them.
+----
+--rsync.action = function
+--(
+-- inlet
+--)
+-- local config = inlet.getConfig( )
+--
+-- -- gets all events ready for syncing
+-- local elist = inlet.getEvents( eventNotInitBlank )
+--
+-- -- gets the list of paths for the event list
+-- -- deletes create multi match patterns
+-- 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
--
--- Spawns the recursive startup sync
+-- log(
+-- 'Normal',
+-- 'Calling rsync with filter-list of new/modified files/dirs\n',
+-- table.concat( paths, '\n' )
+-- )
--
-rsync.init = function(event)
+-- local delete = nil
+--
+-- if config.delete == true
+-- or config.delete == 'running'
+-- then
+-- delete = { '--delete-missing-args', '--ignore-errors' }
+-- end
+--
+-- spawn(
+-- elist,
+-- config.rsync.binary,
+-- '<', table.concat( paths, '\000' ),
+-- config.rsync._computed,
+-- delete,
+-- '--force',
+-- '--from0',
+-- '--files-from=-',
+-- config.source,
+-- config.target
+-- )
+--end
+
+--
+-- Spawns the recursive startup sync.
+--
+rsync.init = function
+(
+ event
+)
local config = event.config
+
local inlet = event.inlet
+
local excludes = inlet.getExcludes( )
+
+ local filters = inlet.hasFilters( ) and inlet.getFilters( )
+
local delete = nil
+
local target = config.target
- if not target then
- if not config.host then
+ 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 == true or config.delete == 'startup' then
+ if config.delete == true
+ or config.delete == 'startup'
+ then
delete = { '--delete', '--ignore-errors' }
end
- if #excludes == 0 then
- -- start rsync without any excludes
+ if not filters and #excludes == 0
+ then
+ -- starts rsync without any filters or excludes
log(
'Normal',
'recursive startup rsync: ',
@@ -264,8 +357,9 @@
target
)
- else
- -- start rsync providing an exclude list
+ elseif not filters
+ then
+ -- starts rsync providing an exclusion list
-- on stdin
local exS = table.concat( excludes, '\n' )
@@ -290,6 +384,32 @@
config.source,
target
)
+ else
+ -- starts rsync providing a filter list
+ -- on stdin
+ local fS = table.concat( filters, '\n' )
+
+ log(
+ 'Normal',
+ 'recursive startup rsync: ',
+ config.source,
+ ' -> ',
+ target,
+ ' filtering\n',
+ fS
+ )
+
+ spawn(
+ event,
+ config.rsync.binary,
+ '<', fS,
+ '--filter=. -',
+ delete,
+ config.rsync._computed,
+ '-r',
+ config.source,
+ target
+ )
end
end
@@ -297,12 +417,12 @@
--
-- Prepares and checks a syncs configuration on startup.
--
-rsync.prepare =
- function(
- config, -- the configuration
- level, -- additional error level for inherited use ( by rsyncssh )
- skipTarget -- used by rsyncssh, do not check for target
- )
+rsync.prepare = function
+(
+ config, -- the configuration
+ level, -- additional error level for inherited use ( by rsyncssh )
+ skipTarget -- used by rsyncssh, do not check for target
+)
-- First let default.prepare test the checkgauge
default.prepare( config, level + 6 )
@@ -315,56 +435,6 @@
)
end
- if config.rsyncOps
- then
- error(
- '"rsyncOps" is outdated please use the new rsync = { ... } syntax.',
- level
- )
- end
-
- if config.rsyncOpts and config.rsync._extra
- then
- error(
- '"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.',
- level
- )
- end
-
- if config.rsyncOpts
- then
- log(
- 'Warn',
- '"rsyncOpts" is outdated. Please use the new rsync = { ... } syntax."'
- )
-
- config.rsync._extra = config.rsyncOpts
- config.rsyncOpts = nil
- end
-
- if config.rsyncBinary and config.rsync.binary
- then
- error(
- '"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.'",
- level
- )
- end
-
- if config.rsyncBinary
- then
- log(
- 'Warn',
- '"rsyncBinary" is outdated. Please use the new rsync = { ... } syntax."'
- )
-
- config.rsync.binary = config.rsyncBinary
- config.rsyncOpts = nil
- end
-
-- checks if the _computed argument exists already
if config.rsync._computed
then
@@ -404,15 +474,18 @@
end
end
-
crsync._computed = { true }
+
local computed = crsync._computed
+
local computedN = 2
local shortFlags = {
acls = 'A',
+ backup = 'b',
checksum = 'c',
compress = 'z',
+ copy_dirlinks = 'k',
copy_links = 'L',
cvs_exclude = 'C',
dry_run = 'n',
@@ -425,6 +498,8 @@
keep_dirlinks = 'K',
links = 'l',
one_file_system = 'x',
+ omit_dir_times = 'O',
+ omit_link_times = 'J',
owner = 'o',
perms = 'p',
protect_args = 's',
@@ -477,12 +552,54 @@
end
end
+ if crsync.append
+ then
+ computed[ computedN ] = '--append'
+ computedN = computedN + 1
+ end
+
+ if crsync.append_verify
+ then
+ computed[ computedN ] = '--append-verify'
+ computedN = computedN + 1
+ end
+
+ if crsync.backup_dir
+ then
+ computed[ computedN ] = '--backup-dir=' .. crsync.backup_dir
+ computedN = computedN + 1
+ end
+
if crsync.bwlimit
then
computed[ computedN ] = '--bwlimit=' .. crsync.bwlimit
computedN = computedN + 1
end
+ if crsync.chmod
+ then
+ computed[ computedN ] = '--chmod=' .. crsync.chmod
+ computedN = computedN + 1
+ end
+
+ if crsync.chown
+ then
+ computed[ computedN ] = '--chown=' .. crsync.chown
+ computedN = computedN + 1
+ end
+
+ if crsync.groupmap
+ then
+ computed[ computedN ] = '--groupmap=' .. crsync.groupmap
+ computedN = computedN + 1
+ end
+
+ if crsync.existing
+ then
+ computed[ computedN ] = '--existing'
+ computedN = computedN + 1
+ end
+
if crsync.inplace
then
computed[ computedN ] = '--inplace'
@@ -507,6 +624,12 @@
computedN = computedN + 1
end
+ if crsync.suffix
+ then
+ computed[ computedN ] = '--suffix=' .. crsync.suffix
+ computedN = computedN + 1
+ end
+
if crsync.temp_dir
then
computed[ computedN ] = '--temp-dir=' .. crsync.temp_dir
@@ -519,6 +642,12 @@
computedN = computedN + 1
end
+ if crsync.usermap
+ then
+ computed[ computedN ] = '--usermap=' .. crsync.usermap
+ computedN = computedN + 1
+ end
+
if shortsN ~= 2
then
computed[ 1 ] = table.concat( shorts, '' )
@@ -527,11 +656,13 @@
end
-- appends a / to target if not present
- if not skipTarget and string.sub(config.target, -1) ~= '/'
+ -- and not a ':' for home dir.
+ if not skipTarget
+ and string.sub( config.target, -1 ) ~= '/'
+ and string.sub( config.target, -1 ) ~= ':'
then
config.target = config.target..'/'
end
-
end
diff -Nru lsyncd-2.1.6/default-rsyncssh.lua lsyncd-2.2.3/default-rsyncssh.lua
--- lsyncd-2.1.6/default-rsyncssh.lua 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/default-rsyncssh.lua 2018-03-09 12:39:11.000000000 +0000
@@ -15,15 +15,18 @@
--
--
-if not default then
+if not default
+then
error( 'default not loaded' );
end
-if not default.rsync then
+if not default.rsync
+then
error( 'default.rsync not loaded' );
end
-if default.rsyncssh then
+if default.rsyncssh
+then
error( 'default-rsyncssh already loaded' );
end
@@ -31,6 +34,7 @@
-- rsyncssh extends default.rsync
--
local rsyncssh = { default.rsync }
+
default.rsyncssh = rsyncssh
--
@@ -56,29 +60,67 @@
port = true,
_extra = true
},
-
- -- xargs settings
- xargs = {
- binary = true,
- delimiter = true,
- _extra = true
- }
}
+
--
--- Spawns rsync for a list of events
+-- Returns true for non Init, Blanket and Move events.
--
-rsyncssh.action = function( inlet )
+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
- local event, event2 = inlet.getEvent( )
+--
+-- 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
+--
+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
- if event.etype == 'Move' then
+ if event.etype == 'Move'
+ then
local path1 = config.targetdir .. event.path
+
local path2 = config.targetdir .. event2.path
+
path1 = "'" .. path1:gsub ('\'', '\'"\'"\'') .. "'"
path2 = "'" .. path2:gsub ('\'', '\'"\'"\'') .. "'"
@@ -105,169 +147,283 @@
return
end
- -- uses ssh to delete files on remote host
- -- instead of constructing rsync filters
+ -- otherwise a rsync is spawned
+ local elist = inlet.getEvents( eventNotInitBlankMove )
- if event.etype == 'Delete' then
-
- if
- config.delete ~= true and
- config.delete ~= 'running'
- then
- inlet.discardEvent(event)
- return
- end
+ -- gets the list of paths for the event list
+ -- deletes create multi match patterns
+ local paths = elist.getPaths( )
- -- gets all other deletes ready to be
- -- executed
- local elist = inlet.getEvents(
- function( e )
- return e.etype == 'Delete'
- end
- )
+ --
+ -- Replaces what rsync would consider filter rules by literals
+ --
+ local function sub( p )
+ if not p then return 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
- )
+ return p:
+ gsub( '%?', '\\?' ):
+ gsub( '%*', '\\*' ):
+ gsub( '%[', '\\[' ):
+ gsub( '%]', '\\]' )
+ 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
+ --
+ -- Gets the list of paths for the event list
+ --
+ -- Deletes create multi match patterns
+ --
+ local paths = elist.getPaths(
+ function( etype, path1, path2 )
+ if string.byte( path1, -1 ) == 47 and etype == 'Delete' then
+ return sub( path1 )..'***', sub( path2 )
+ else
+ return sub( path1 ), sub( path2 )
end
end
+ )
- log(
- 'Normal',
- 'Deleting list\n',
- table.concat( paths, '\n' )
- )
+ -- stores all filters by integer index
+ local filterI = { }
- local params = { }
+ -- stores all filters with path index
+ local filterP = { }
- spawn(
- elist,
- config.ssh.binary,
- '<', table.concat(paths, config.xargs.delimiter),
- params,
- config.ssh._computed,
- config.host,
- config.xargs.binary,
- config.xargs._extra
- )
+ -- adds one path to the filter
+ local function addToFilter( path )
+ if filterP[ path ] then return end
- return
+ filterP[ path ] = true
+
+ table.insert( filterI, path )
end
+ -- adds a path to the filter.
--
- -- 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
- )
+ -- rsync needs to have entries for all steps in the path,
+ -- so the file for example d1/d2/d3/f1 needs following filters:
+ -- 'd1/', 'd1/d2/', 'd1/d2/d3/' and 'd1/d2/d3/f1'
+ for _, path in ipairs( paths )
+ do
+ if path and path ~= ''
+ then
+ addToFilter(path)
- local paths = elist.getPaths( )
+ local pp = string.match( path, '^(.*/)[^/]+/?' )
+
+ while pp
+ do
+ addToFilter(pp)
+ pp = string.match( pp, '^(.*/)[^/]+/?' )
+ 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 )
end
- end
- local sPaths = table.concat( paths, '\n' )
- local zPaths = table.concat( paths, '\000' )
+ end
log(
'Normal',
- 'Rsyncing list\n',
- sPaths
+ 'Calling rsync with filter-list of new/modified files/dirs\n',
+ table.concat( filterI, '\n' )
)
+ local config = inlet.getConfig( )
+
+ local delete = nil
+
+ if config.delete == true or config.delete == 'running'
+ then
+ delete = { '--delete', '--ignore-errors' }
+ end
+
spawn(
elist,
config.rsync.binary,
- '<', zPaths,
+ '<', table.concat( filterI, '\000' ),
config.rsync._computed,
+ '-r',
+ delete,
+ '--force',
'--from0',
- '--files-from=-',
+ '--include-from=-',
+ '--exclude=*',
config.source,
config.host .. ':' .. config.targetdir
)
end
------
--- Called when collecting a finished child process
+
+----
+---- NOTE: This optimized version can be used once
+---- https://bugzilla.samba.org/show_bug.cgi?id=12569
+---- is fixed.
+----
+--
+-- Spawns rsync for a list of events
--
-rsyncssh.collect = function( agent, exitcode )
+--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
+-- if event.etype == 'Move'
+-- then
+-- local path1 = config.targetdir .. event.path
+--
+-- local path2 = config.targetdir .. event2.path
+--
+-- 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
+-- 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
+--
+-- log(
+-- 'Normal',
+-- 'Rsyncing list\n',
+-- table.concat( paths, '\n' )
+-- )
+--
+-- local delete = nil
+--
+-- if config.delete == true
+-- or config.delete == 'running'
+-- then
+-- delete = { '--delete-missing-args', '--ignore-errors' }
+-- end
+--
+-- spawn(
+-- elist,
+-- config.rsync.binary,
+-- '<', table.concat( paths, '\000' ),
+-- config.rsync._computed,
+-- delete,
+-- '--force',
+-- '--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
+ 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)
+ 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
+ log(
+ 'Error',
+ 'Temporary or permanent failure on startup of "',
+ agent.source, '". Terminating since "insist" is not set.'
+ )
- elseif rc == 'die' then
- log('Error', 'Failure on startup of "',agent.source,'": ', exitcode)
+ 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)
+ 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)
+ 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)
+ 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)
+ 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)
+ log( 'Error', 'Unknown exitcode ', agent.etype,' ', agent.sourcePath,': ', exitcode )
+
rc = 'die'
end
@@ -279,65 +435,59 @@
--
-- checks the configuration.
--
-rsyncssh.prepare = function( config, level )
-
+rsyncssh.prepare = function
+(
+ config,
+ level
+)
default.rsync.prepare( config, level + 1, true )
- if not config.host then
- error(
- 'default.rsyncssh needs "host" configured',
- level
- )
+ 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
- )
+ 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
- )
+ if config.ssh._computed
+ then
+ error( 'please do not use the internal rsync._computed parameter', level )
end
- local cssh =
- config.ssh;
+ if config.maxProcesses ~= 1
+ then
+ error( 'default.rsyncssh must have maxProcesses set to 1.', level )
+ end
- cssh._computed =
- { }
+ local cssh = config.ssh;
- local computed =
- cssh._computed
+ cssh._computed = { }
- local computedN =
- 1
+ local computed = cssh._computed
- local rsyncc =
- config.rsync._computed
+ local computedN = 1
- if cssh.identityFile then
- computed[ computedN ] =
- '-i'
+ local rsyncc = config.rsync._computed
- computed[ computedN + 1 ] =
- cssh.identityFile
+ if cssh.identityFile
+ then
+ computed[ computedN ] = '-i'
- computedN =
- computedN + 2
+ computed[ computedN + 1 ] = cssh.identityFile
- if not config.rsync._rshIndex then
- config.rsync._rshIndex =
- #rsyncc + 1
+ computedN = computedN + 2
- rsyncc[ config.rsync._rshIndex ] =
- '--rsh=ssh'
+ if not config.rsync._rshIndex
+ then
+ config.rsync._rshIndex = #rsyncc + 1
+
+ rsyncc[ config.rsync._rshIndex ] = '--rsh=ssh'
end
rsyncc[ config.rsync._rshIndex ] =
@@ -346,26 +496,21 @@
cssh.identityFile
end
- if cssh.options then
-
-
- for k, v in pairs( cssh.options ) do
+ if cssh.options
+ then
+ for k, v in pairs( cssh.options )
+ do
+ computed[ computedN ] = '-o'
- computed[ computedN ] =
- '-o'
+ computed[ computedN + 1 ] = k .. '=' .. v
- computed[ computedN + 1 ] =
- k .. '=' .. v
+ computedN = computedN + 2
- computedN =
- computedN + 2
+ if not config.rsync._rshIndex
+ then
+ config.rsync._rshIndex = #rsyncc + 1
- if not config.rsync._rshIndex then
- config.rsync._rshIndex =
- #rsyncc + 1
-
- rsyncc[ config.rsync._rshIndex ] =
- '--rsh=ssh'
+ rsyncc[ config.rsync._rshIndex ] = '--rsh=ssh'
end
rsyncc[ config.rsync._rshIndex ] =
@@ -382,42 +527,41 @@
end
end
- if cssh.port then
- computed[ computedN ] =
- '-p'
-
- computed[ computedN + 1 ] =
- cssh.port
-
- computedN =
- computedN + 2
-
- if not config.rsync._rshIndex then
- config.rsync._rshIndex =
- #rsyncc + 1
+ if cssh.port
+ then
+ computed[ computedN ] = '-p'
- rsyncc[ config.rsync._rshIndex ] =
- '--rsh=ssh'
+ computed[ computedN + 1 ] = cssh.port
+
+ computedN = computedN + 2
+
+ if not config.rsync._rshIndex
+ then
+ config.rsync._rshIndex = #rsyncc + 1
+
+ rsyncc[ config.rsync._rshIndex ] = '--rsh=ssh'
end
rsyncc[ config.rsync._rshIndex ] =
rsyncc[ config.rsync._rshIndex ] .. ' -p ' .. cssh.port
end
- if cssh._extra then
- for k, v in ipairs( cssh._extra ) do
- computed[ computedN ] =
- v
+ if cssh._extra
+ then
+ for k, v in ipairs( cssh._extra )
+ do
+ computed[ computedN ] = v
- computedN =
- computedN + 1
+ computedN = computedN + 1
end
end
-- appends a slash to the targetdir if missing
- if string.sub( config.targetdir, -1 ) ~= '/' then
- config.targetdir =
- config.targetdir .. '/'
+ -- and is not ':' for home dir
+ if string.sub( config.targetdir, -1 ) ~= '/'
+ and string.sub( config.targetdir, -1 ) ~= ':'
+ then
+ config.targetdir = config.targetdir .. '/'
end
end
@@ -425,64 +569,34 @@
--
-- allow processes
--
-rsyncssh.maxProcesses =
- 1
+rsyncssh.maxProcesses = 1
--
-- The core should not split move events
--
-rsyncssh.onMove =
- true
+rsyncssh.onMove = true
--
-- default delay
--
-rsyncssh.delay =
- 15
+rsyncssh.delay = 15
--
-- no default exit codes
--
-rsyncssh.exitcodes =
- false
+rsyncssh.exitcodes = false
--
-- rsync exit codes
--
-rsyncssh.rsyncExitCodes =
- default.rsyncExitCodes
+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 = {
+rsyncssh.sshExitCodes = default.sshExitCodes
- --
- -- 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
@@ -494,31 +608,26 @@
--
-- the binary called
--
- binary =
- '/usr/bin/ssh',
+ binary = '/usr/bin/ssh',
--
-- if set adds this key to ssh
--
- identityFile =
- nil,
+ identityFile = nil,
--
-- if set adds this special options to ssh
--
- options =
- nil,
+ options = nil,
--
-- if set connect to this port
--
- port =
- nil,
+ port = nil,
--
-- extra parameters
--
- _extra =
- { }
+ _extra = { }
}
diff -Nru lsyncd-2.1.6/distclean.sh lsyncd-2.2.3/distclean.sh
--- lsyncd-2.1.6/distclean.sh 1970-01-01 00:00:00.000000000 +0000
+++ lsyncd-2.2.3/distclean.sh 2018-03-09 12:39:11.000000000 +0000
@@ -0,0 +1,3 @@
+#!/bin/sh
+# removes all stuff generated by cmake / make
+rm -rf AdditionalInfo.txt config.h Makefile build/ CMakeCache.txt CMakeFiles/ cmake_install.cmake install_manifest.txt defaults.c runner.c *.o *.out lsyncd
diff -Nru lsyncd-2.1.6/doc/lsyncd.1 lsyncd-2.2.3/doc/lsyncd.1
--- lsyncd-2.1.6/doc/lsyncd.1 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/doc/lsyncd.1 1970-01-01 00:00:00.000000000 +0000
@@ -1,157 +0,0 @@
-'\" t
-.\" Title: lsyncd
-.\" Author: [see the "AUTHOR" section]
-.\" Generator: DocBook XSL Stylesheets v1.78.1
-.\" Date: April 2012
-.\" Manual: Lsyncd
-.\" Source: Lsyncd 2.0.7
-.\" Language: English
-.\"
-.TH "LSYNCD" "1" "April 2012" "Lsyncd 2\&.0\&.7" "Lsyncd"
-.\" -----------------------------------------------------------------
-.\" * Define some portability stuff
-.\" -----------------------------------------------------------------
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.\" http://bugs.debian.org/507673
-.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
-.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-.ie \n(.g .ds Aq \(aq
-.el .ds Aq '
-.\" -----------------------------------------------------------------
-.\" * set default formatting
-.\" -----------------------------------------------------------------
-.\" disable hyphenation
-.nh
-.\" disable justification (adjust text to left margin only)
-.ad l
-.\" -----------------------------------------------------------------
-.\" * MAIN CONTENT STARTS HERE *
-.\" -----------------------------------------------------------------
-.SH "NAME"
-lsyncd \- a daemon to continuously synchronize directory trees
-.SH "SYNOPSIS"
-.PP
-config file
-.RS 4
-\ \&
-\fBlsyncd\fR
-[\fIOPTIONS\fR]
-\fICONFIG\-FILE\fR
-.RE
-.PP
-default rsync behaviour
-.RS 4
-\ \&
-\fBlsyncd\fR
-[\fIOPTIONS\fR] \-rsync
-\fISOURCEDIR\fR\fITARGET\fR
-\&...
-.RE
-.PP
-default rync+ssh behaviour (moves and deletes through ssh)
-.RS 4
-\ \&
-\fBlsyncd\fR
-[\fIOPTIONS\fR] \-rsyncssh
-\fISOURCEDIR\fR\fITARGETHOST\fR\fITARGETDIR\fR
-\&...
-.RE
-.PP
-default direct behaviour (local file operations/rsync)
-.RS 4
-\ \&
-\fBlsyncd\fR
-[\fIOPTIONS\fR] \-direct
-\fISOURCEDIR\fR\fITARGETDIR\fR
-\&...
-.RE
-.SH "DESCRIPTION"
-.sp
-Lsyncd(1) watches local directory trees through an event monitor interface (inotify, fsevents)\&. It aggregates and combines events for a few seconds and then spawns one or more processes to synchronize the changes\&. By default this is rsync(1)\&. Lsyncd is thus a light\-weight asynchronous live mirror solution that is comparatively easy to install not requiring new filesystems or block devices and does not hamper local filesystem performance\&.
-.sp
-Rsync+ssh is an advanced action configuration that uses a SSH(1) to act file and directory moves directly on the target instead of re\-transmitting the move destination over the wire\&.
-.sp
-Fine\-grained customization can be achieved through the CONFIG\-FILE\&. Custom action configs can even be written from scratch in cascading layers ranging from shell scripts to code written in the LUA(1) language\&. This way simplicity can be balanced with powerfulness\&. See the online manual for details on the CONFIG\-FILE https://github\&.com/axkibe/lsyncd/wiki/Manual\-to\-Lsyncd\-2\&.0\&.x \&.
-.sp
-Note that under normal configuration Lsyncd will delete pre\-existing files in the target directories that are not present in the respective source directory\&.
-.SH "OPTIONS"
-.PP
-\fB\-delay\fR \fISECS\fR
-.RS 4
-Overrides the default delay times\&.
-.RE
-.PP
-\fB\-help\fR
-.RS 4
-Show a help message\&.
-.RE
-.PP
-\fB\-insist\fR
-.RS 4
-Continues start up even if rsync cannot connect\&.
-.RE
-.PP
-\fB\-log\fR \fILEVEL\fR
-.RS 4
-Controls which kind of events are logged\&. By default Lsyncd logs
-\fINormal\fR
-and
-\fIError\fR
-Messages\&.
-\fB\-log scarce\fR
-will make Lsyncd log Error messages only\&.
-\fB\-log all\fR
-will log all debug messages\&.
-.RE
-.PP
-\fB\-log\fR \fICategory\fR
-.RS 4
-Turns on a specific debug message\&. E\&.g\&.
-\fB\-log Exec\fR
-will log all processes as they are spawned\&.
-.RE
-.PP
-\fB\-nodaemon\fR
-.RS 4
-Lsyncd will not detach from the invoker and log as well to stdout/err\&.
-.RE
-.PP
-\fB\-pidfile\fR \fIFILE\fR
-.RS 4
-Lsyncd will write its process ID in
-\fIFILE\fR\&.
-.RE
-.PP
-\fB\-runner\fR \fIFILE\fR
-.RS 4
-Makes the Lsyncd core load the part of Lsyncd written in Lua from
-\fIFILE\fR\&.
-.RE
-.PP
-\fB\-version\fR
-.RS 4
-Writes version information and exits\&.
-.RE
-.SH "EXIT STATUS"
-.PP
-\fB0\fR
-.RS 4
-Terminated on a TERM signal(7)
-.RE
-.PP
-\fB\-1\fR
-.RS 4
-Failure (syntax, unrecoverable error condition, internal failure)
-.RE
-.SH "SEE ALSO"
-.sp
-Online Manual: https://github\&.com/axkibe/lsyncd/wiki/Lsyncd\-2\&.1\&.x\-%E2%80%96\-What\(cqs\-New%3F
-.SH "VERSION"
-.sp
-This man page is for lsyncd(1) version 2\&.0\&.7
-.SH "AUTHOR"
-.sp
-Axel Kittenberger, 2010\-2012
-.SH "COPYING"
-.sp
-Copyright (C) 2010\-2012 Axel Kittenberger\&. Free use of this software is granted under the terms of the GNU General Public License (GPL) version 2, or any later version\&. Free redistrubition of this Documentation (/doc directory) is granted under the terms of the Creative Commons 3\&.0 Attribution License (CC\-3\&.0\-BY)\&.
diff -Nru lsyncd-2.1.6/doc/lsyncd.1.txt lsyncd-2.2.3/doc/lsyncd.1.txt
--- lsyncd-2.1.6/doc/lsyncd.1.txt 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/doc/lsyncd.1.txt 1970-01-01 00:00:00.000000000 +0000
@@ -1,107 +0,0 @@
-lsyncd(1)
-=========
-:doctype: manpage
-:man source: Lsyncd
-:man manual: Lsyncd
-:man version: 2.0.7
-:date: April 2012
-
-NAME
-----
-lsyncd - a daemon to continuously synchronize directory trees
-
-SYNOPSIS
---------
-config file:::
-{nbsp} *lsyncd* ['OPTIONS'] 'CONFIG-FILE'
-
-default rsync behaviour:::
-{nbsp} *lsyncd* ['OPTIONS'] -rsync 'SOURCEDIR' 'TARGET' ...
-
-default rync+ssh behaviour (moves and deletes through ssh):::
-{nbsp} *lsyncd* ['OPTIONS'] -rsyncssh 'SOURCEDIR' 'TARGETHOST' 'TARGETDIR' ...
-
-default direct behaviour (local file operations/rsync):::
-{nbsp} *lsyncd* ['OPTIONS'] -direct 'SOURCEDIR' 'TARGETDIR' ...
-
-DESCRIPTION
-------------
-Lsyncd(1) watches local directory trees through an event monitor interface
-(inotify, fsevents). It aggregates and combines events for a few seconds and
-then spawns one or more processes to synchronize the changes. By default this
-is rsync(1). Lsyncd is thus a light-weight asynchronous live mirror solution
-that is comparatively easy to install not requiring new filesystems or
-block devices and does not hamper local filesystem performance.
-
-Rsync+ssh is an advanced action configuration that uses a SSH(1) to act file
-and directory moves directly on the target instead of re-transmitting the move
-destination over the wire.
-
-Fine-grained customization can be achieved through the CONFIG-FILE. Custom
-action configs can even be written from scratch in cascading layers ranging
-from shell scripts to code written in the LUA(1) language. This way simplicity
-can be balanced with powerfulness. See the online manual for details on the
-CONFIG-FILE https://github.com/axkibe/lsyncd/wiki/Manual-to-Lsyncd-2.0.x .
-
-Note that under normal configuration Lsyncd will delete pre-existing files in
-the target directories that are not present in the respective source directory.
-
-OPTIONS
--------
-*-delay* 'SECS'::
- Overrides the default delay times.
-
-*-help*::
- Show a help message.
-
-*-insist*::
- Continues start up even if rsync cannot connect.
-
-*-log* 'LEVEL'::
- Controls which kind of events are logged. By default Lsyncd logs 'Normal'
- and 'Error' Messages. *-log scarce* will make Lsyncd log Error messages
- only. *-log all* will log all debug messages.
-
-*-log* 'Category'::
- Turns on a specific debug message. E.g. *-log Exec* will log
- all processes as they are spawned.
-
-*-nodaemon*::
- Lsyncd will not detach from the invoker and log as well to stdout/err.
-
-*-pidfile* 'FILE'::
- Lsyncd will write its process ID in 'FILE'.
-
-*-runner* 'FILE'::
- Makes the Lsyncd core load the part of Lsyncd written in Lua from 'FILE'.
-
-*-version*::
- Writes version information and exits.
-
-EXIT STATUS
------------
-*0*::
- Terminated on a TERM signal(7)
-
-*-1*::
- Failure (syntax, unrecoverable error condition, internal failure)
-
-SEE ALSO
---------
-Online Manual: https://github.com/axkibe/lsyncd/wiki/Lsyncd-2.1.x-%E2%80%96-What's-New%3F
-
-VERSION
-------
-This man page is for lsyncd(1) version 2.0.7
-
-AUTHOR
-------
-Axel Kittenberger, 2010-2012
-
-COPYING
--------
-Copyright \(C) 2010-2012 Axel Kittenberger. Free use of this software is granted
-under the terms of the GNU General Public License (GPL) version 2, or any later
-version. Free redistrubition of this Documentation (/doc directory) is granted
-under the terms of the Creative Commons 3.0 Attribution License (CC-3.0-BY).
-
diff -Nru lsyncd-2.1.6/doc/manpage/lsyncd.1 lsyncd-2.2.3/doc/manpage/lsyncd.1
--- lsyncd-2.1.6/doc/manpage/lsyncd.1 1970-01-01 00:00:00.000000000 +0000
+++ lsyncd-2.2.3/doc/manpage/lsyncd.1 2018-03-09 12:39:11.000000000 +0000
@@ -0,0 +1,161 @@
+'\" t
+.\" Title: lsyncd
+.\" Author: [see the "AUTHOR" section]
+.\" Generator: DocBook XSL Stylesheets v1.79.1
+.\" Date: January 2017
+.\" Manual: Lsyncd
+.\" Source: Lsyncd 2.2.1
+.\" Language: English
+.\"
+.TH "LSYNCD" "1" "January 2017" "Lsyncd 2\&.2\&.1" "Lsyncd"
+.\" -----------------------------------------------------------------
+.\" * Define some portability stuff
+.\" -----------------------------------------------------------------
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.\" http://bugs.debian.org/507673
+.\" http://lists.gnu.org/archive/html/groff/2009-02/msg00013.html
+.\" ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+.ie \n(.g .ds Aq \(aq
+.el .ds Aq '
+.\" -----------------------------------------------------------------
+.\" * set default formatting
+.\" -----------------------------------------------------------------
+.\" disable hyphenation
+.nh
+.\" disable justification (adjust text to left margin only)
+.ad l
+.\" -----------------------------------------------------------------
+.\" * MAIN CONTENT STARTS HERE *
+.\" -----------------------------------------------------------------
+.SH "NAME"
+lsyncd \- a daemon to continuously synchronize directory trees
+.SH "SYNOPSIS"
+.PP
+config file
+.RS 4
+\ \&
+\fBlsyncd\fR
+[\fIOPTIONS\fR]
+\fICONFIG\-FILE\fR
+.RE
+.PP
+default rsync behaviour
+.RS 4
+\ \&
+\fBlsyncd\fR
+[\fIOPTIONS\fR] \-rsync
+\fISOURCEDIR\fR
+\fITARGET\fR
+\&...
+.RE
+.PP
+default rync+ssh behaviour (moves and deletes through ssh)
+.RS 4
+\ \&
+\fBlsyncd\fR
+[\fIOPTIONS\fR] \-rsyncssh
+\fISOURCEDIR\fR
+\fITARGETHOST\fR
+\fITARGETDIR\fR
+\&...
+.RE
+.PP
+default direct behaviour (local file operations/rsync)
+.RS 4
+\ \&
+\fBlsyncd\fR
+[\fIOPTIONS\fR] \-direct
+\fISOURCEDIR\fR
+\fITARGETDIR\fR
+\&...
+.RE
+.SH "DESCRIPTION"
+.sp
+Lsyncd(1) watches local directory trees through an event monitor interface (inotify, fsevents)\&. It aggregates and combines events for a few seconds and then spawns one or more processes to synchronize the changes\&. By default this is rsync(1)\&. Lsyncd is thus a light\-weight asynchronous live mirror solution that is comparatively easy to install not requiring new filesystems or block devices and does not hamper local filesystem performance\&.
+.sp
+Rsync+ssh is an advanced action configuration that uses a SSH(1) to act file and directory moves directly on the target instead of re\-transmitting the move destination over the wire\&.
+.sp
+Fine\-grained customization can be achieved through the CONFIG\-FILE\&. Custom action configs can even be written from scratch in cascading layers ranging from shell scripts to code written in the LUA(1) language\&. This way simplicity can be balanced with powerfulness\&. See the online manual for details on the CONFIG\-FILE https://axkibe\&.github\&.io/lsyncd/manual/config/file/ \&.
+.sp
+Note that under normal configuration Lsyncd will delete pre\-existing files in the target directories that are not present in the respective source directory\&.
+.SH "OPTIONS"
+.PP
+\fB\-delay\fR \fISECS\fR
+.RS 4
+Overrides the default delay times\&.
+.RE
+.PP
+\fB\-help\fR
+.RS 4
+Show a help message\&.
+.RE
+.PP
+\fB\-insist\fR
+.RS 4
+Continues start up even if rsync cannot connect\&.
+.RE
+.PP
+\fB\-log\fR \fILEVEL\fR
+.RS 4
+Controls which kind of events are logged\&. By default Lsyncd logs
+\fINormal\fR
+and
+\fIError\fR
+Messages\&.
+\fB\-log scarce\fR
+will make Lsyncd log Error messages only\&.
+\fB\-log all\fR
+will log all debug messages\&.
+.RE
+.PP
+\fB\-log\fR \fICategory\fR
+.RS 4
+Turns on a specific debug message\&. E\&.g\&.
+\fB\-log Exec\fR
+will log all processes as they are spawned\&.
+.RE
+.PP
+\fB\-nodaemon\fR
+.RS 4
+Lsyncd will not detach from the invoker and log as well to stdout/err\&.
+.RE
+.PP
+\fB\-pidfile\fR \fIFILE\fR
+.RS 4
+Lsyncd will write its process ID in
+\fIFILE\fR\&.
+.RE
+.PP
+\fB\-runner\fR \fIFILE\fR
+.RS 4
+Makes the Lsyncd core load the part of Lsyncd written in Lua from
+\fIFILE\fR\&.
+.RE
+.PP
+\fB\-version\fR
+.RS 4
+Writes version information and exits\&.
+.RE
+.SH "EXIT STATUS"
+.PP
+\fB(128+SIGNUM)\fR
+.RS 4
+Terminated by Signal (143 by TERM)
+.RE
+.PP
+\fB\-1\fR
+.RS 4
+Failure (syntax, unrecoverable error condition, internal failure)
+.RE
+.SH "SEE ALSO"
+.sp
+Online Manual: https://axkibe\&.github\&.io/lsyncd/
+.SH "VERSION"
+.sp
+This man page is for lsyncd(1) version 2\&.2\&.0
+.SH "AUTHOR"
+.sp
+Axel Kittenberger, 2010\-2017
+.SH "COPYING"
+.sp
+Copyright (C) 2010\-2017 Axel Kittenberger\&. Free use of this software is granted under the terms of the GNU General Public License (GPL) version 2, or any later version\&. Free redistrubition of this Documentation (/doc directory) is granted under the terms of the Creative Commons 3\&.0 Attribution License (CC\-3\&.0\-BY)\&.
diff -Nru lsyncd-2.1.6/doc/manpage/lsyncd.1.txt lsyncd-2.2.3/doc/manpage/lsyncd.1.txt
--- lsyncd-2.1.6/doc/manpage/lsyncd.1.txt 1970-01-01 00:00:00.000000000 +0000
+++ lsyncd-2.2.3/doc/manpage/lsyncd.1.txt 2018-03-09 12:39:11.000000000 +0000
@@ -0,0 +1,108 @@
+lsyncd(1)
+=========
+:doctype: manpage
+:man source: Lsyncd
+:man manual: Lsyncd
+:man version: 2.2.1
+:date: January 2017
+
+NAME
+----
+lsyncd - a daemon to continuously synchronize directory trees
+
+SYNOPSIS
+--------
+config file:::
+{nbsp} *lsyncd* ['OPTIONS'] 'CONFIG-FILE'
+
+default rsync behaviour:::
+{nbsp} *lsyncd* ['OPTIONS'] -rsync 'SOURCEDIR' 'TARGET' ...
+
+default rync+ssh behaviour (moves and deletes through ssh):::
+{nbsp} *lsyncd* ['OPTIONS'] -rsyncssh 'SOURCEDIR' 'TARGETHOST' 'TARGETDIR' ...
+
+default direct behaviour (local file operations/rsync):::
+{nbsp} *lsyncd* ['OPTIONS'] -direct 'SOURCEDIR' 'TARGETDIR' ...
+
+DESCRIPTION
+------------
+Lsyncd(1) watches local directory trees through an event monitor interface
+(inotify, fsevents). It aggregates and combines events for a few seconds and
+then spawns one or more processes to synchronize the changes. By default this
+is rsync(1). Lsyncd is thus a light-weight asynchronous live mirror solution
+that is comparatively easy to install not requiring new filesystems or
+block devices and does not hamper local filesystem performance.
+
+Rsync+ssh is an advanced action configuration that uses a SSH(1) to act file
+and directory moves directly on the target instead of re-transmitting the move
+destination over the wire.
+
+Fine-grained customization can be achieved through the CONFIG-FILE. Custom
+action configs can even be written from scratch in cascading layers ranging
+from shell scripts to code written in the LUA(1) language. This way simplicity
+can be balanced with powerfulness. See the online manual for details on the
+CONFIG-FILE https://axkibe.github.io/lsyncd/manual/config/file/ .
+
+Note that under normal configuration Lsyncd will delete pre-existing files in
+the target directories that are not present in the respective source directory.
+
+OPTIONS
+-------
+*-delay* 'SECS'::
+ Overrides the default delay times.
+
+*-help*::
+ Show a help message.
+
+*-insist*::
+ Continues start up even if rsync cannot connect.
+
+*-log* 'LEVEL'::
+ Controls which kind of events are logged. By default Lsyncd logs 'Normal'
+ and 'Error' Messages. *-log scarce* will make Lsyncd log Error messages
+ only. *-log all* will log all debug messages.
+
+*-log* 'Category'::
+ Turns on a specific debug message. E.g. *-log Exec* will log
+ all processes as they are spawned.
+
+*-nodaemon*::
+ Lsyncd will not detach from the invoker and log as well to stdout/err.
+
+*-pidfile* 'FILE'::
+ Lsyncd will write its process ID in 'FILE'.
+
+*-runner* 'FILE'::
+ Makes the Lsyncd core load the part of Lsyncd written in Lua from 'FILE'.
+
+*-version*::
+ Writes version information and exits.
+
+EXIT STATUS
+-----------
+*(128+SIGNUM)*::
+ Terminated by Signal (143 by TERM)
+
+*-1*::
+ Failure (syntax, unrecoverable error condition, internal failure)
+
+
+SEE ALSO
+--------
+Online Manual: https://axkibe.github.io/lsyncd/
+
+VERSION
+------
+This man page is for lsyncd(1) version 2.2.0
+
+AUTHOR
+------
+Axel Kittenberger, 2010-2017
+
+COPYING
+-------
+Copyright \(C) 2010-2017 Axel Kittenberger. Free use of this software is granted
+under the terms of the GNU General Public License (GPL) version 2, or any later
+version. Free redistrubition of this Documentation (/doc directory) is granted
+under the terms of the Creative Commons 3.0 Attribution License (CC-3.0-BY).
+
diff -Nru lsyncd-2.1.6/examples/lbash.lua lsyncd-2.2.3/examples/lbash.lua
--- lsyncd-2.1.6/examples/lbash.lua 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/examples/lbash.lua 2018-03-09 12:39:11.000000000 +0000
@@ -4,7 +4,7 @@
-- This example uses local bash commands to keep two local
-- directory trees in sync.
--
-settings = {
+settings {
logfile = "/tmp/lsyncd.log",
statusFile = "/tmp/lsyncd.stat",
statusIntervall = 1,
diff -Nru lsyncd-2.1.6/examples/lpostcmd.lua lsyncd-2.2.3/examples/lpostcmd.lua
--- lsyncd-2.1.6/examples/lpostcmd.lua 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/examples/lpostcmd.lua 2018-03-09 12:39:11.000000000 +0000
@@ -24,24 +24,30 @@
maxProcesses = 1,
-- called whenever something is to be done
- action = function(inlet)
- local event = inlet.getEvent()
- local config = inlet.getConfig()
+ action = function
+ (
+ inlet
+ )
+ local event = inlet.getEvent( )
+ local config = inlet.getConfig( )
-- if the event is a blanket event and not the startup,
-- its there to spawn the webservice restart at the target.
- if event.etype == "Blanket" then
+ if event.etype == 'Blanket'
+ then
-- uses rawget to test if "isPostcmd" has been set without
-- triggering an error if not.
- local isPostcmd = rawget(event, "isPostcmd")
- if isPostcmd then
+ local isPostcmd = rawget( event, 'isPostcmd' )
+
+ if isPostcmd
+ then
spawn(event, "/usr/bin/ssh",
config.host, config.postcmd)
- return
+ return
else
- -- this is the startup, forwards it to default routine.
- return default.rsync.action(inlet)
- end
- error("this should never be reached")
+ -- this is the startup, forwards it to default routine.
+ return default.rsync.action(inlet)
+ end
+ error( 'this should never be reached' )
end
-- for any other event, a blanket event is created that
-- will stack on the queue and do the postcmd when its finished
@@ -53,33 +59,49 @@
-- called when a process exited.
-- this can be a rsync command, the startup rsync or the postcmd
- collect = function(agent, exitcode)
+ collect = function
+ (
+ agent,
+ exitcode
+ )
-- for the ssh commands 255 is network error -> try again
- local isPostcmd = rawget(agent, "isPostcmd")
- if not agent.isList and agent.etype == "Blanket" and isPostcmd then
- if exitcode == 255 then
- return "again"
- end
+ local isPostcmd = rawget( agent, 'isPostcmd' )
+
+ if not agent.isList and agent.etype == "Blanket" and isPostcmd
+ then
+ if exitcode == 255 then return 'again' end
+
return
else
--- everything else, forward to default collection handler
- return default.collect(agent,exitcode)
+ return default.collect( agent,exitcode )
end
- error("this should never be reached")
+ error( 'this should never be reached' )
end,
-- called before anything else
-- builds the target from host and targetdir
- prepare = function(config, level, skipTarget)
- if not config.host then
- error("rsyncpostcmd neets 'host' configured", 4)
+ prepare = function
+ (
+ config,
+ level,
+ skipTarget
+ )
+ if not config.host
+ then
+ error( 'rsyncpostcmd needs "host" configured', 4 )
end
- if not config.targetdir then
- error("rsyncpostcmd needs 'targetdir' configured", 4)
+
+ if not config.targetdir
+ then
+ error( 'rsyncpostcmd needs "targetdir" configured', 4)
end
- if not config.target then
+
+ if not config.target
+ then
config.target = config.host .. ":" .. config.targetdir
end
+
return default.rsync.prepare(config, level, skipTarget)
end
}
@@ -87,9 +109,10 @@
sync {
rsyncpostcmd,
- source = "src",
- host = "beetle",
- targetdir = "/path/to/trg",
- postcmd = "/usr/local/bin/restart-servelt.sh",
+ delay = 3,
+ source = '/path/to/src',
+ host = 'localhost',
+ targetdir = '/path/to/trg',
+ postcmd = '/usr/local/bin/dopostcmd',
}
diff -Nru lsyncd-2.1.6/examples/lrsync.lua lsyncd-2.2.3/examples/lrsync.lua
--- lsyncd-2.1.6/examples/lrsync.lua 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/examples/lrsync.lua 2018-03-09 12:39:11.000000000 +0000
@@ -3,7 +3,7 @@
--
-- Simple example for default rsync.
--
-settings = {
+settings {
statusFile = "/tmp/lsyncd.stat",
statusInterval = 1,
}
diff -Nru lsyncd-2.1.6/examples/ls3.lua lsyncd-2.2.3/examples/ls3.lua
--- lsyncd-2.1.6/examples/ls3.lua 1970-01-01 00:00:00.000000000 +0000
+++ lsyncd-2.2.3/examples/ls3.lua 2018-03-09 12:39:11.000000000 +0000
@@ -0,0 +1,222 @@
+----
+-- Example lsyncd configuration for syncing with an Amazon S3 bucket
+--
+-- This requires the official AWS CLI to be available, and that credentials
+-- bet set up through some external method, such as environment variables,
+-- IAM profiles or the AWS SDK configuration.
+--
+-- The AWS CLI sync exclude rules are not as powerful as the ones supported by
+-- lsyncd. Hence, some of the do not translate perfectly. For example, '*'
+-- (asterisk) matches slashes, while it does not in lsyncd. Hence it is a good
+-- idea to only use exclude patterns for full directories, either by using a
+-- trailing / (slash) or ** (double asterisk), as those will be correctly
+-- translated.
+--
+-- An initialSync options is provided as a convenience, since it's not easy to
+-- make sure exclusion rules match when doing it manually. It will *pull* from
+-- the target bucket to the local dir (the opposite of the regular behavior)
+-- then exit immediately.
+--
+-- Author: Daniel Miranda
+--
+s3 = {}
+
+s3.checkgauge = {
+ onCreate = false,
+ onModify = false,
+ onDelete = false,
+ onStartup = false,
+ onMove = false,
+
+ delete = true,
+ exclude = true,
+ excludeFrom = true,
+ target = true,
+
+ s3 = {
+ -- Path to the AWS CLI binary
+ awscliBinary = true,
+ -- Extra options to pass to the AWS CLI (as a list)
+ awscliOptions = true,
+ -- Whether to do a dry-run, and not make any real changes
+ dryrun = true,
+ -- Do an initial pull from the bucket and exit immediately.
+ initialSync = true
+ }
+}
+
+-- Generate a list of exclude flags for the AWS CLI based on the lsyncd
+-- patterns provided. Cache it to avoid re-generating it every time.
+
+local s3Excludes = function(config, excludes)
+ if config.s3._excludes == nil then
+ config.s3._excludes = {}
+ for _, pat in ipairs(excludes) do
+ pat = pat:gsub('%*%*', '[[ANY]]')
+ pat = pat:gsub('%?', '[[ANY_BUT_SLASH_ONCE]]')
+ pat = pat:gsub('/$', '/*')
+ pat = pat:gsub('%[%[ANY%]%]', '*')
+ pat = pat:gsub('%[%[ANY_BUT_SLASH_ONCE%]%]', '[^/]')
+
+ if pat:match('^/') then
+ pat = pat:sub(2, -1)
+ else
+ pat = '*/' .. pat
+ end
+
+ table.insert(config.s3._excludes, '--exclude')
+ table.insert(config.s3._excludes, pat)
+ end
+
+ log('s3Excludes', table.concat(config.s3._excludes, '\n'))
+ end
+
+ return config.s3._excludes
+end
+
+-- Generates a command line to call the AWS CLI as configured, with the provided
+-- S3 action (such as cp, mv, rm or sync).
+-- Returns a tuple of (binaryPath, arguments)
+local awscliCommand = function(verb, config)
+ local bin = config.s3.awscliBinary
+ local args = {'s3', verb, '--only-show-errors'}
+ if config.s3.dryrun then
+ table.insert(args, '--dryrun')
+ end
+
+ if verb == 'sync'
+ and (config.delete == true or config.delete == 'startup')
+ then
+ table.insert(args, '--delete')
+ end
+
+ for _, opt in ipairs(config.s3.awscliOptions) do
+ table.insert(args, opt)
+ end
+
+ return bin, args
+end
+
+s3.action = function(inlet)
+ local event, event2 = inlet.getEvent()
+ -- S3 never actually deals with directories - they are just an illusion
+ -- created based on the common prefixes of objects. Hence discard any events
+ -- that do not concern files.
+ if event.isdir then
+ inlet.discardEvent(event)
+ return
+ end
+
+ local config = inlet.getConfig()
+ if event.etype == 'Create' or event.etype == 'Modify' then
+ local bin, args = awscliCommand('cp', config)
+ spawn(
+ event,
+ bin,
+ args,
+ event.sourcePath,
+ event.targetPath
+ )
+ elseif event.etype == 'Delete' then
+ if config.delete ~= true and config.delete ~= 'running' then
+ inlet.discardEvent(event)
+ return
+ end
+
+ local bin, args = awscliCommand('rm', config)
+ spawn(
+ event,
+ bin,
+ args,
+ event.targetPath
+ )
+ elseif event.etype == 'Move' then
+ local bin, args = awscliCommand('mv', config)
+ spawn(
+ event,
+ bin,
+ args,
+ event.targetPath,
+ event2.targetPath
+ )
+ else
+ log('Warn', 'ignored an event of type "', event.etype, '"')
+ inlet.discardEvent(event)
+ end
+end
+
+s3.init = function(event)
+ local config = event.config
+ local inlet = event.inlet
+ local excludes = s3Excludes(config, inlet.getExcludes())
+ local bin, args = awscliCommand('sync', config)
+
+ -- Do a pull when initialSync is enabled.
+ if config.s3.initialSync then
+ spawn(
+ event,
+ bin,
+ args,
+ excludes,
+ config.target,
+ event.sourcePath
+ )
+ -- And a push, as usual, otherwise
+ else
+ spawn(
+ event,
+ bin,
+ args,
+ excludes,
+ event.sourcePath,
+ config.target
+ )
+ end
+end
+
+-- Define a collect callback so we can terminate immediately when initialSync
+-- is enabled
+s3.collect = function(agent, exitcode)
+ local config = agent.config
+ if not agent.isList and agent.etype == 'Init' and config.s3.initialSync then
+ terminate(exitcode == 0 and 0 or -1)
+ end
+
+ return
+end
+
+s3.prepare = function(config, level)
+ default.prepare(config, level + 1)
+
+ config.target = config.target:gsub('/+$', '')
+ if not config.target:match('^s3://') then
+ config.target = 's3://' .. config.target
+ end
+end
+
+s3.s3 = {
+ awscliBinary = '/usr/bin/aws',
+ awscliOptions = {},
+ dryrun = false,
+ initialSync = false
+}
+s3.delete = false
+s3.delay = 10
+s3.maxProcesses = 1
+
+sync {
+ s3,
+ source = '/my/dir',
+ target = 's3://my-bucket/my-path',
+ delay = 30,
+ delete = true,
+ maxProcesses = 2,
+ exclude = {
+ '/sub/folder/',
+ },
+ s3 = {
+ awscliBinary = '/usr/local/bin/aws',
+ awscliOptions = {'--acl', 'public-read'},
+ dryrun = false
+ }
+}
diff -Nru lsyncd-2.1.6/inotify.c lsyncd-2.2.3/inotify.c
--- lsyncd-2.1.6/inotify.c 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/inotify.c 2018-03-09 12:39:11.000000000 +0000
@@ -124,9 +124,9 @@
// kernel call to create the inotify watch
int wd = inotify_add_watch( inotify_fd, path, mask );
- if (wd < 0)
+ if( wd < 0 )
{
- if (errno == ENOSPC)
+ if( errno == ENOSPC )
{
printlogf(
L, "Error",
@@ -154,18 +154,18 @@
/*
- * Removes an inotify watch.
- *
- * param dir (Lua stack) numeric watch descriptor
- *
- * return nil
- */
+* Removes an inotify watch.
+*
+* param dir (Lua stack) numeric watch descriptor
+*
+* return nil
+*/
static int
-l_rmwatch(lua_State *L)
+l_rmwatch( lua_State *L )
{
- int wd = luaL_checkinteger(L, 1);
- inotify_rm_watch(inotify_fd, wd);
- printlogf(L, "Inotify", "rmwatch()<-%d", wd);
+ int wd = luaL_checkinteger( L, 1 );
+ inotify_rm_watch( inotify_fd, wd );
+ printlogf( L, "Inotify", "rmwatch()<-%d", wd );
return 0;
}
@@ -173,7 +173,8 @@
/*
| Lsyncd's core's inotify functions.
*/
-static const luaL_Reg linotfylib[] = {
+static const luaL_Reg linotfylib[ ] =
+{
{ "addwatch", l_addwatch },
{ "rmwatch", l_rmwatch },
{ NULL, NULL}
@@ -215,6 +216,7 @@
// used to execute two events in case of unmatched MOVE_FROM buffer
struct inotify_event *after_buf = NULL;
+
if( event && ( IN_Q_OVERFLOW & event->mask ) )
{
// and overflow happened, tells the runner
@@ -224,8 +226,11 @@
{
exit( -1 );
}
+
lua_pop( L, 1 );
+
hup = 1;
+
return;
}
@@ -251,10 +256,10 @@
move_event = false;
}
else if(
- move_event &&
- (
- !( IN_MOVED_TO & event->mask ) ||
- event->cookie != move_event_buf->cookie
+ move_event
+ && (
+ !( IN_MOVED_TO & event->mask )
+ || event->cookie != move_event_buf->cookie
)
)
{
@@ -263,18 +268,21 @@
logstring(
"Inotify",
"icore, changing unary MOVE_FROM into DELETE"
- )
+ );
+
after_buf = event;
+
event = move_event_buf;
+
event_type = "Delete";
+
move_event = false;
}
else if(
- move_event &&
- (
- IN_MOVED_TO & event->mask ) &&
- event->cookie == move_event_buf->cookie
- )
+ move_event
+ && ( IN_MOVED_TO & event->mask )
+ && event->cookie == move_event_buf->cookie
+ )
{
// this is indeed a matched move */
event_type = "Move";
@@ -290,10 +298,14 @@
if( move_event_buf_size < el )
{
move_event_buf_size = el;
+
move_event_buf = s_realloc( move_event_buf, el );
}
+
memcpy( move_event_buf, event, el );
+
move_event = true;
+
return;
}
@@ -346,6 +358,7 @@
}
lua_pushstring( L, event_type );
+
if( event_type != MOVE )
{
lua_pushnumber( L, event->wd );
@@ -390,6 +403,7 @@
| buffer to read inotify events into
*/
static size_t readbuf_size = 2048;
+
static char * readbuf = NULL;
@@ -495,7 +509,7 @@
extern void
register_inotify( lua_State *L )
{
- luaL_register( L, LSYNCD_INOTIFYLIBNAME, linotfylib );
+ lua_compat_register( L, LSYNCD_INOTIFYLIBNAME, linotfylib );
}
@@ -511,11 +525,14 @@
"Error",
"internal failure: inotify_fd != ob->fd"
);
+
exit( -1 );
}
close( inotify_fd );
+
free( readbuf );
+
readbuf = NULL;
}
diff -Nru lsyncd-2.1.6/INSTALL lsyncd-2.2.3/INSTALL
--- lsyncd-2.1.6/INSTALL 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/INSTALL 2018-03-09 12:39:11.000000000 +0000
@@ -15,22 +15,22 @@
Lua
For building Lsyncd the Lua interpreter 'lua'
- and the Lua compiler 'luac' are needed.
- They aren't needed in the deployed binary tough.
-
- Use Lua 5.1 or Lua 5.2 at your choice.
+ and the Lua compiler 'luac' are needed.
+ They aren't needed in the deployed binary though.
+
+ Use Lua 5.2 or later.
Liblua
The lua library.
- Note that you likely need the package "liblua-dev"
- or something like that.
+ Note that you likely need the package "liblua-dev"
+ or something like that.
- Use Lua 5.1 or Lua 5.2 at your choice.
+ Use Lua 5.2 or later.
- Note, this has to be exactly the same Version as the
- lua compiler used above!
+ Note, this has to be exactly the same Version as the
+ lua compiler used above!
Building
@@ -41,16 +41,16 @@
mkdir build
cd build
cmake ..
- make
+ make
sudo make install
Building intree:
cmake .
- make
+ make
On OSX you yet need to get the xnu sources.
For example:
cmake -DWITH_INOTIFY=OFF -DWITH_FSEVENTS=ON -DXNU_DIR=/path/to/xnu-VERSION
- make
+ make
FIXME make install not yet done
diff -Nru lsyncd-2.1.6/lsyncd.c lsyncd-2.2.3/lsyncd.c
--- lsyncd-2.1.6/lsyncd.c 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/lsyncd.c 2018-03-09 12:39:11.000000000 +0000
@@ -80,6 +80,7 @@
NULL,
};
+
/**
| Configuration parameters that matter to the core
*/
@@ -92,16 +93,19 @@
.nodaemon = false,
};
+
/*
| True when Lsyncd daemonized itself.
*/
static bool is_daemon = false;
+
/*
| The config file loaded by Lsyncd.
*/
char * lsyncd_config_file = NULL;
+
/*
| False after first time Lsyncd started up.
|
@@ -114,6 +118,7 @@
*/
static bool first_time = true;
+
/*
| Set by TERM or HUP signal handler
| telling Lsyncd should end or reset ASAP.
@@ -123,11 +128,13 @@
volatile sig_atomic_t sigcode = 0;
int pidfile_fd = 0;
+
/*
| The kernel's clock ticks per second.
*/
static long clocks_per_sec;
+
/**
* signal handler
*/
@@ -136,6 +143,7 @@
// nothing
}
+
/**
* signal handler
*/
@@ -156,6 +164,7 @@
}
}
+
/*
| Non glibc builds need a real tms structure for the times( ) call
*/
@@ -166,6 +175,7 @@
static struct tms * dummy_tms = &_dummy_tms;
#endif
+
/*
| Returns the absolute path of a path.
|
@@ -191,7 +201,6 @@
}
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*
( Logging )
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
@@ -256,18 +265,22 @@
if( !strcmp( "all", name ) )
{
settings.log_level = 99;
+
return true;
}
if( !strcmp( "scarce", name ) )
{
settings.log_level = LOG_WARNING;
+
return true;
}
// categories must start with a capital letter.
if( name[ 0 ] < 'A' || name[ 0 ] > 'Z' )
- { return false; }
+ {
+ return false;
+ }
if( !logcats[ name[ 0 ]- 'A' ] )
{
@@ -344,6 +357,7 @@
char ct[ 255 ];
// gets current timestamp hour:minute:second
time_t mtime;
+
time( &mtime );
strftime( ct, sizeof( ct ), "%T", localtime( &mtime ) );
@@ -358,18 +372,21 @@
}
// writes to file if configured so
- if (settings.log_file)
+ if( settings.log_file )
{
FILE * flog = fopen( settings.log_file, "a" );
+
char * ct;
+
time_t mtime;
// gets current timestamp day-time-year
time( &mtime );
+
ct = ctime( &mtime );
// cuts trailing linefeed
- ct[ strlen( ct ) - 1] = 0;
+ ct[ strlen( ct ) - 1] = 0;
if( flog == NULL )
{
@@ -378,6 +395,7 @@
"Cannot open logfile [%s]!\n",
settings.log_file
);
+
exit( -1 );
}
@@ -420,7 +438,6 @@
}
-
/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*
( Simple memory management )
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
@@ -596,6 +613,7 @@
*/
static int runner;
+
/*
| Dummy variable of which it's address is used as
| the cores index n the lua registry to
@@ -612,9 +630,9 @@
{
int flags;
- flags = fcntl( fd, F_GETFD );
+ flags = fcntl( fd, F_GETFD );
- if( flags == -1 )
+ if( flags == -1 )
{
logstring( "Error", "cannot get descriptor flags!" );
exit( -1 );
@@ -638,9 +656,9 @@
{
int flags;
- flags = fcntl( fd, F_GETFL );
+ flags = fcntl( fd, F_GETFL );
- if( flags == -1 )
+ if( flags == -1 )
{
logstring( "Error", "cannot get status flags!" );
exit( -1 );
@@ -659,7 +677,8 @@
| Writes a pid file.
*/
static void
-write_pidfile(
+write_pidfile
+(
lua_State *L,
const char *pidfile
)
@@ -952,12 +971,13 @@
}
-/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*
-( Library calls for the runner )
- *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
+/******************************.
+* Library calls for the runner *
+'******************************/
-static void daemonize( lua_State *L );
+static void daemonize( lua_State *L, const char *pidfile );
+
int l_stackdump( lua_State* L );
@@ -981,7 +1001,9 @@
// skips filtered messages
if( priority > settings.log_level )
- { return 0; }
+ {
+ return 0;
+ }
// replaces non string values
{
@@ -1212,9 +1234,14 @@
// prepares the arguments
argv = s_calloc( argc + 2, sizeof( char * ) );
+
argv[ 0 ] = binary;
+
for( i = 1; i <= argc; i++ )
- { argv[i] = luaL_checkstring( L, i + li ); }
+ {
+ argv[i] = luaL_checkstring( L, i + li );
+ }
+
argv[ i ] = NULL;
// the fork!
@@ -1224,7 +1251,9 @@
{
// replaces stdin for pipes
if( pipe_text )
- { dup2( pipefd[ 0 ], STDIN_FILENO ); }
+ {
+ dup2( pipefd[ 0 ], STDIN_FILENO );
+ }
// if lsyncd runs as a daemon and has a logfile it will redirect
// stdout/stderr of child processes to the logfile.
@@ -1325,36 +1354,56 @@
const char *rdir = luaL_checkstring(L, 1);
char *adir = get_realpath(rdir);
- if (!adir) {
- printlogf(L, "Error", "failure getting absolute path of [%s]", rdir);
+ if( !adir )
+ {
+ printlogf(
+ L, "Error",
+ "failure getting absolute path of [%s]",
+ rdir
+ );
+
return 0;
}
{
// makes sure its a directory
struct stat st;
- if (stat(adir, &st)) {
- printlogf(L, "Error",
- "cannot get absolute path of dir '%s': %s", rdir, strerror(errno));
- free(adir);
+ if( stat( adir, &st ) )
+ {
+ printlogf(
+ L, "Error",
+ "cannot get absolute path of dir '%s': %s",
+ rdir,
+ strerror( errno )
+ );
+
+ free( adir );
+
return 0;
}
- if (!S_ISDIR(st.st_mode)) {
- printlogf(L, "Error",
- "cannot get absolute path of dir '%s': is not a directory", rdir);
- free(adir);
+ if( !S_ISDIR( st.st_mode ) )
+ {
+ printlogf(
+ L, "Error",
+ "cannot get absolute path of dir '%s': is not a directory",
+ rdir
+ );
+
+ free( adir );
+
return 0;
}
}
// returns absolute path with a concated '/'
- luaL_buffinit(L, &b);
- luaL_addstring(&b, adir);
- luaL_addchar(&b, '/');
- luaL_pushresult(&b);
+ luaL_buffinit( L, &b );
+ luaL_addstring( &b, adir );
+ luaL_addchar( &b, '/' );
+ luaL_pushresult( &b );
+
+ free( adir );
- free(adir);
return 1;
}
@@ -1380,35 +1429,43 @@
switch( t )
{
case LUA_TSTRING:
+
printlogf(
L, "Debug",
"%d string: '%s'",
i, lua_tostring( L, i )
);
+
break;
case LUA_TBOOLEAN:
+
printlogf(
L, "Debug",
"%d boolean %s",
i, lua_toboolean( L, i ) ? "true" : "false"
);
+
break;
case LUA_TNUMBER:
+
printlogf(
L, "Debug",
"%d number: %g",
i, lua_tonumber( L, i )
);
+
break;
default:
+
printlogf(
L, "Debug",
"%d %s",
i, lua_typename( L, t )
);
+
break;
}
}
@@ -1428,12 +1485,14 @@
| values are boolean true on dirs.
*/
static int
-l_readdir ( lua_State *L )
+l_readdir( lua_State *L )
{
const char * dirname = luaL_checkstring( L, 1 );
+
DIR *d;
d = opendir( dirname );
+
if( d == NULL )
{
printlogf(
@@ -1451,16 +1510,15 @@
struct dirent *de = readdir( d );
bool isdir;
- if( de == NULL )
+ if( de == NULL ) // finished
{
- // finished
break;
}
// ignores . and ..
if(
- !strcmp( de->d_name, "." ) ||
- !strcmp( de->d_name, ".." )
+ !strcmp( de->d_name, "." )
+ || !strcmp( de->d_name, ".." )
)
{
continue;
@@ -1475,6 +1533,7 @@
strlen( de->d_name ) +
2
);
+
struct stat st;
strcpy( entry, dirname );
@@ -1500,6 +1559,7 @@
}
closedir( d );
+
return 1;
}
@@ -1514,7 +1574,7 @@
|
*/
int
-l_terminate(lua_State *L)
+l_terminate( lua_State *L )
{
int exitcode = luaL_checkinteger( L, 1 );
@@ -1534,6 +1594,7 @@
l_configure( lua_State *L )
{
const char * command = luaL_checkstring( L, 1 );
+
if( !strcmp( command, "running" ) )
{
// set by runner after first initialize
@@ -1546,22 +1607,22 @@
settings.log_syslog = true;
const char * log_ident =
- settings.log_ident ?
- settings.log_ident :
- "lsyncd";
+ settings.log_ident
+ ? settings.log_ident
+ : "lsyncd";
openlog( log_ident, 0, settings.log_facility );
}
if( !settings.nodaemon && !is_daemon )
{
- logstring( "Debug", "daemonizing now." );
- daemonize( L );
- }
+ logstring( "Normal", "--- Startup, daemonizing ---" );
- if( settings.pidfile )
+ daemonize( L, settings.pidfile );
+ }
+ else
{
- write_pidfile( L, settings.pidfile );
+ logstring( "Normal", "--- Startup ---" );
}
}
@@ -1590,8 +1651,7 @@
free( settings.pidfile );
}
- settings.pidfile =
- s_strdup( file );
+ settings.pidfile = s_strdup( file );
}
else if( !strcmp( command, "logfacility" ) )
{
@@ -1635,8 +1695,10 @@
{
const char * ident = luaL_checkstring( L, 2 );
- if (settings.log_ident)
- { free(settings.log_ident); }
+ if( settings.log_ident )
+ {
+ free( settings.log_ident );
+ }
settings.log_ident = s_strdup( ident );
}
@@ -1767,6 +1829,7 @@
return 0;
}
+
/*
| The Lsnycd's core library
*/
@@ -1785,6 +1848,7 @@
{ NULL, NULL }
};
+
/*
| Adds a number in seconds to a jiffy timestamp.
*/
@@ -1817,6 +1881,7 @@
}
}
+
/*
| Subracts two jiffy timestamps resulting in a number in seconds
| or substracts a jiffy by a number in seconds resulting a jiffy timestamp.
@@ -1849,6 +1914,7 @@
return 1;
}
+
/*
| Compares two jiffy timestamps
*/
@@ -1863,6 +1929,7 @@
return 1;
}
+
/*
* True if jiffy1 timestamp is eariler than jiffy2 timestamp
*/
@@ -1877,6 +1944,7 @@
return 1;
}
+
/*
| True if jiffy1 before or equals jiffy2
*/
@@ -1897,7 +1965,7 @@
void
register_lsyncd( lua_State *L )
{
- luaL_register( L, LSYNCD_LIBNAME, lsyncdlib );
+ lua_compat_register( L, LSYNCD_LIBNAME, lsyncdlib );
lua_setglobal( L, LSYNCD_LIBNAME );
// creates the metatable for the jiffies ( timestamps ) userdata
@@ -1971,6 +2039,7 @@
lua_remove( L, -2 );
}
+
/*
| Daemonizes.
|
@@ -1981,7 +2050,10 @@
| might actually close the monitors fd!
*/
static void
-daemonize( lua_State *L )
+daemonize(
+ lua_State *L, // the lua state
+ const char *pidfile // if not NULL write pidfile
+)
{
pid_t pid, sid;
@@ -1998,14 +2070,20 @@
exit( -1 );
}
- if (pid > 0)
+ if( pid > 0 )
{
// parent process returns to shell
exit( 0 );
}
+ if( pidfile )
+ {
+ write_pidfile( L, pidfile );
+ }
+
// detaches the new process from the parent process
sid = setsid( );
+
if( sid < 0 )
{
printlogf(
@@ -2338,6 +2416,7 @@
}
}
+
/*
| The effective main for one run.
|
@@ -2356,6 +2435,7 @@
// load Lua
L = luaL_newstate( );
+
luaL_openlibs( L );
{
// checks the lua version
@@ -2433,7 +2513,6 @@
// registers Lsycnd's core library
register_lsyncd( L );
-
if( check_logcat( "Debug" ) <= settings.log_level )
{
// printlogf doesnt support %ld :-(
@@ -2591,7 +2670,6 @@
}
}
-
// checks if there is a "-help" or "--help"
{
int i;
@@ -2719,15 +2797,11 @@
}
#ifdef WITH_INOTIFY
-
open_inotify( L );
-
#endif
#ifdef WITH_FSEVENTS
-
open_fsevents( L );
-
#endif
// adds signal handlers
@@ -2805,6 +2879,7 @@
return 0;
}
+
/*
| Main
*/
@@ -2814,6 +2889,9 @@
// gets a kernel parameter
clocks_per_sec = sysconf( _SC_CLK_TCK );
+ setlinebuf( stdout );
+ setlinebuf( stderr );
+
while( !term ) {
main1( argc, argv );
}
diff -Nru lsyncd-2.1.6/lsyncd.h lsyncd-2.2.3/lsyncd.h
--- lsyncd-2.1.6/lsyncd.h 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/lsyncd.h 2018-03-09 12:39:11.000000000 +0000
@@ -12,11 +12,12 @@
#define LSYNCD_H
// some older machines need this to see pselect
-#define _BSD_SOURCE 1
+#define _DEFAULT_SOURCE 1
#define _XOPEN_SOURCE 700
#define _DARWIN_C_SOURCE 1
#define LUA_COMPAT_ALL
+#define LUA_COMPAT_5_1
// includes needed for headerfile
#include "config.h"
@@ -31,6 +32,20 @@
#define LSYNCD_LIBNAME "lsyncd"
#define LSYNCD_INOTIFYLIBNAME "inotify"
+/*
+| Workaround to register a library for different lua versions.
+*/
+#if LUA_VERSION_NUM > 502
+ #define lua_compat_register( L, name, lib ) \
+ { \
+ lua_newtable((L)); \
+ luaL_setfuncs((L), (lib), 0); \
+ }
+#else
+ #define lua_compat_register( L, name, lib ) \
+ {luaL_register( (L), (name), (lib) );}
+#endif
+
/**
* Lsyncd runtime configuration
*/
diff -Nru lsyncd-2.1.6/lsyncd.lua lsyncd-2.2.3/lsyncd.lua
--- lsyncd-2.1.6/lsyncd.lua 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/lsyncd.lua 2018-03-09 12:39:11.000000000 +0000
@@ -12,34 +12,32 @@
--
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--- require('profiler')
--- profiler.start()
--
-- A security measurement.
-- The core will exit if version ids mismatch.
--
-if lsyncd_version then
-
+if lsyncd_version
+then
-- ensures the runner is not being loaded twice
- lsyncd.log(
- 'Error',
- 'You cannot use the lsyncd runner as configuration file!'
- )
+ lsyncd.log( 'Error', 'You cannot use the lsyncd runner as configuration file!' )
lsyncd.terminate( -1 )
end
-lsyncd_version = '2.1.6'
+lsyncd_version = '2.2.3'
+
--
-- Hides the core interface from user scripts.
--
local _l = lsyncd
lsyncd = nil
+
local lsyncd = _l
_l = nil
+
--
-- Shortcuts (which user is supposed to be able to use them as well)
--
@@ -48,33 +46,58 @@
now = lsyncd.now
readdir = lsyncd.readdir
+
--
--- Coping globals to ensure userscripts don't change this.
+-- Coping globals to ensure userscripts cannot change this.
--
local log = log
local terminate = terminate
local now = now
+local readdir = readdir
--
--- Predeclarations
+-- Predeclarations.
--
local Monitors
+
--
--- Global: total number of processess running
+-- Global: total number of processess running.
--
local processCount = 0
+
+--
+-- All valid entries in a settings{} call.
+--
+local settingsCheckgauge =
+{
+ logfile = true,
+ pidfile = true,
+ nodaemon = true,
+ statusFile = true,
+ statusInterval = true,
+ logfacility = true,
+ logident = true,
+ insist = true,
+ inotifyMode = true,
+ maxProcesses = true,
+ maxDelays = true,
+}
+
+
--
-- Settings specified by command line.
--
local clSettings = { }
+
--
-- Settings specified by config scripts.
--
local uSettings = { }
+
--
-- A copy of the settings function to see if the
-- user script replaced the settings() by a table
@@ -82,38 +105,64 @@
--
local settingsSafe
+
--============================================================================
-- Lsyncd Prototypes
--============================================================================
+
--
-- Array tables error if accessed with a non-number.
--
-local Array = ( function( )
-
- -- Metatable
+local Array = ( function
+( )
+ --
+ -- Metatable.
+ --
local mt = { }
- -- on accessing a nil index.
- mt.__index = function( t, k )
- if type(k) ~= 'number' then
+ --
+ -- On accessing a nil index.
+ --
+ mt.__index = function
+ (
+ t, -- table accessed
+ k -- key value accessed
+ )
+ if type(k) ~= 'number'
+ then
error( 'Key "'..k..'" invalid for Array', 2 )
end
+
return rawget( t, k )
end
- -- on assigning a new index.
- mt.__newindex = function( t, k, v )
- if type( k ) ~= 'number' then
+ --
+ -- On assigning a new index.
+ --
+ mt.__newindex = function
+ (
+ t, -- table getting a new index assigned
+ k, -- key value to assign to
+ v -- value to assign
+ )
+ if type( k ) ~= 'number'
+ then
error( 'Key "'..k..'" invalid for Array', 2 )
end
+
rawset( t, k, v )
end
- -- creates a new object
- local function new( )
+ --
+ -- Creates a new object
+ --
+ local function new
+ ( )
local o = { }
+
setmetatable( o, mt )
+
return o
end
@@ -121,7 +170,6 @@
-- Public interface
--
return { new = new }
-
end )( )
@@ -132,8 +180,8 @@
-- since Lua's # operator does not work on tables whose key values are not
-- strictly linear.
--
-local CountArray = ( function( )
-
+local CountArray = ( function
+( )
--
-- Metatable
--
@@ -147,228 +195,425 @@
--
-- On accessing a nil index.
--
- mt.__index = function( t, k )
- if type( k ) ~= 'number' then
- error( 'Key "'..k..'" invalid for CountArray', 2 )
+ mt.__index = function
+ (
+ t, -- table being accessed
+ k -- key used to access
+ )
+ if type( k ) ~= 'number'
+ then
+ error( 'Key "' .. k .. '" invalid for CountArray', 2 )
end
+
return t[ k_nt ][ k ]
end
--
-- On assigning a new index.
--
- mt.__newindex = function( t, k, v )
-
- if type(k) ~= 'number' then
+ mt.__newindex = function
+ (
+ t, -- table getting a new index assigned
+ k, -- key value to assign to
+ v -- value to assign
+ )
+ if type( k ) ~= 'number'
+ then
error( 'Key "'..k..'" invalid for CountArray', 2 )
end
-- value before
local vb = t[ k_nt ][ k ]
- if v and not vb then
+
+ if v and not vb
+ then
t._size = t._size + 1
- elseif not v and vb then
+ elseif not v and vb
+ then
t._size = t._size - 1
end
+
t[ k_nt ][ k ] = v
end
--
-- Walks through all entries in any order.
--
- local function walk( self )
+ local function walk
+ (
+ self -- the count array
+ )
return pairs( self[ k_nt ] )
end
--
- -- Returns the count
+ -- Returns the count.
--
- local function size( self )
+ local function size
+ (
+ self -- the count array
+ )
return self._size
end
--
-- Creates a new count array
--
- local function new( )
-
- -- k_nt is native table, private for this object.
- local o = {
+ local function new
+ ( )
+ -- k_nt is a native table, private to this object.
+ local o =
+ {
_size = 0,
walk = walk,
size = size,
- [k_nt] = { }
+ [ k_nt ] = { }
}
- setmetatable(o, mt)
+ setmetatable( o, mt )
+
return o
end
-
--
-- Public interface
--
return { new = new }
end )( )
+
--
--- A queue is optimized for pushing on the right and poping on the left.
+-- A queue is optimized for pushing and poping.
+-- TODO: make this an object
--
-Queue = ( function( )
+Queue = ( function
+( )
+ --
+ -- Metatable
+ --
+ local mt = { }
+
--
- -- Creates a new queue.
+ -- Key to native table
--
- local function new( )
- return {
- first = 1,
- last = 0,
- size = 0
- };
+ local k_nt = { }
+
+
+ --
+ -- On accessing a nil index.
+ --
+ mt.__index = function
+ (
+ t, -- table being accessed
+ k -- key used to access
+ )
+ if type( k ) ~= 'number'
+ then
+ error( 'Key "' .. k .. '" invalid for Queue', 2 )
+ end
+
+ return t[ k_nt ][ k ]
end
+
+ --
+ -- On assigning a new index.
+ --
+ mt.__newindex = function
+ (
+ t, -- table getting a new index assigned
+ k, -- key value to assign to
+ v -- value to assign
+ )
+ error( 'Queues are not directly assignable.', 2 )
+ end
+
+ --
+ -- Returns the first item of the Queue.
+ --
+ local function first
+ (
+ self
+ )
+ local nt = self[ k_nt ]
+
+ return nt[ nt.first ]
+ end
+
+ --
+ -- Returns the last item of the Queue.
+ --
+ local function last
+ (
+ self
+ )
+ local nt = self[ k_nt ]
+
+ return nt[ nt.last ]
+ end
+
+ --
+ -- Returns the size of the queue.
+ --
+ local function size
+ (
+ self
+ )
+ return self[ k_nt ].size
+ end
+
+
--
-- Pushes a value on the queue.
-- Returns the last value
--
- local function push( list, value )
-
- if not value then
- error('Queue pushing nil value', 2)
+ local function push
+ (
+ self, -- queue to push to
+ value -- value to push
+ )
+ if not value
+ then
+ error( 'Queue pushing nil value', 2 )
end
- local last = list.last + 1
- list.last = last
- list[ last ] = value
- list.size = list.size + 1
+ local nt = self[ k_nt ]
+
+ local last = nt.last + 1
+
+ nt.last = last
+
+ nt[ last ] = value
+
+ nt.size = nt.size + 1
+
return last
end
+
--
-- Removes an item at pos from the Queue.
--
- local function remove( list, pos )
+ local function remove
+ (
+ self, -- the queue
+ pos -- position to remove
+ )
+ local nt = self[ k_nt ]
- if list[ pos ] == nil then
- error('Removing nonexisting item in Queue', 2)
+ if nt[ pos ] == nil
+ then
+ error( 'Removing nonexisting item in Queue', 2 )
end
- list[ pos ] = nil
+ nt[ pos ] = nil
-- if removing first or last element,
-- the queue limits are adjusted.
- if pos == list.first then
-
- local last = list.last
+ if pos == nt.first
+ then
+ local last = nt.last
- while list[ pos ] == nil and pos <= list.last do
+ while nt[ pos ] == nil and pos <= last
+ do
pos = pos + 1
end
- list.first = pos
+ nt.first = pos
- elseif pos == list.last then
+ elseif pos == nt.last
+ then
+ local first = nt.first
- while list[ pos ] == nil and pos >= list.first do
+ while nt[ pos ] == nil and pos >= first
+ do
pos = pos - 1
end
- list.last = pos
-
+ nt.last = pos
end
-- reset the indizies if the queue is empty
- if list.last < list.first then
- list.first = 1
- list.last = 0
+ if nt.last < nt.first
+ then
+ nt.first = 1
+
+ nt.last = 0
end
- list.size = list.size - 1
+ nt.size = nt.size - 1
end
--
- -- Queue iterator (stateless)
+ -- Replaces a value.
--
- local function iter( list, pos )
+ local function replace
+ (
+ self, -- the queue
+ pos, -- position to replace
+ value -- the new entry
+ )
+ local nt = self[ k_nt ]
+
+ if nt[ pos ] == nil
+ then
+ error( 'Trying to replace an unset Queue entry.' )
+ end
+
+ nt[ pos ] = value
+ end
+
+ --
+ -- Queue iterator ( stateless )
+ -- TODO rename next
+ --
+ local function iter
+ (
+ self, -- queue to iterate
+ pos -- current position
+ )
+ local nt = self[ k_nt ]
pos = pos + 1
- while list[ pos ] == nil and pos <= list.last do
+ while nt[ pos ] == nil and pos <= nt.last
+ do
pos = pos + 1
end
- if pos > list.last then
+ if pos > nt.last
+ then
return nil
end
- return pos, list[ pos ]
+ return pos, nt[ pos ]
end
+
--
-- Reverse queue iterator (stateless)
+ -- TODO rename prev
--
- local function iterReverse( list, pos )
+ local function iterReverse
+ (
+ self, -- queue to iterate
+ pos -- current position
+ )
+ local nt = self[ k_nt ]
pos = pos - 1
- while list[pos] == nil and pos >= list.first do
+ while nt[ pos ] == nil and pos >= nt.first
+ do
pos = pos - 1
end
- if pos < list.first then
+ if pos < nt.first
+ then
return nil
end
- return pos, list[ pos ]
+ return pos, nt[ pos ]
end
+
--
-- Iteraters through the queue
-- returning all non-nil pos-value entries.
--
- local function qpairs( list )
- return iter, list, list.first - 1
+ local function qpairs
+ (
+ self
+ )
+ return iter, self, self[ k_nt ].first - 1
end
+
--
-- Iteraters backwards through the queue
-- returning all non-nil pos-value entries.
--
- local function qpairsReverse( list )
- return iterReverse, list, list.last + 1
+ local function qpairsReverse
+ (
+ self
+ )
+ return iterReverse, self, self[ k_nt ].last + 1
end
- return {
- new = new,
- push = push,
- remove = remove,
- qpairs = qpairs,
- qpairsReverse = qpairsReverse
- }
+ --
+ -- Creates a new queue.
+ --
+ local function new
+ ( )
+ local q = {
+ first = first,
+ last = last,
+ push = push,
+ qpairs = qpairs,
+ qpairsReverse = qpairsReverse,
+ remove = remove,
+ replace = replace,
+ size = size,
+
+ [ k_nt ] =
+ {
+ first = 1,
+ last = 0,
+ size = 0
+ }
+ }
+
+ setmetatable( q, mt )
+
+ return q
+ end
+
+ --
+ -- Public interface
+ --
+ return { new = new }
end )( )
+
--
--- Locks globals,
--- No more globals can be created after this
+-- Locks globals.
--
-local function lockGlobals( )
-
+-- No more globals can be created after this!
+--
+local function lockGlobals
+( )
local t = _G
+
local mt = getmetatable( t ) or { }
-- TODO try to remove the underscore exceptions
- mt.__index = function( t, k )
- if k ~= '_' and string.sub(k, 1, 2) ~= '__' then
- error( 'Access of non-existing global "'..k..'"', 2 )
+ mt.__index = function
+ (
+ t, -- table being accessed
+ k -- key used to access
+ )
+ if k ~= '_' and string.sub( k, 1, 2 ) ~= '__'
+ then
+ error( 'Access of non-existing global "' .. k ..'"', 2 )
else
rawget( t, k )
end
end
- mt.__newindex = function( t, k, v )
- if k ~= '_' and string.sub( k, 1, 2 ) ~= '__' then
- error('Lsyncd does not allow GLOBALS to be created on the fly. '..
- 'Declare "'..k..'" local or declare global on load.', 2)
+ mt.__newindex = function
+ (
+ t, -- table getting a new index assigned
+ k, -- key value to assign to
+ v -- value to assign
+ )
+ if k ~= '_' and string.sub( k, 1, 2 ) ~= '__'
+ then
+ error(
+ 'Lsyncd does not allow GLOBALS to be created on the fly. '
+ .. 'Declare "' .. k.. '" local or declare global on load.',
+ 2
+ )
else
rawset( t, k, v )
end
@@ -377,163 +622,186 @@
setmetatable( t, mt )
end
+
--
-- Holds the information about a delayed event for one Sync.
--
-local Delay = ( function( )
-
- --
- -- Creates a new delay.
+-- Valid stati of an delay are:
+-- 'wait' ... the event is ready to be handled.
+-- 'active' ... there is process running catering for this event.
+-- 'blocked' ... this event waits for another to be handled first.
+--
+local Delay = ( function
+( )
--
- -- Params see below.
+ -- Metatable.
--
- local function new( etype, sync, alarm, path, path2 )
-
- local o = {
- --
- -- Type of event.
- -- Can be 'Create', 'Modify', 'Attrib', 'Delete' and 'Move'
- --
- etype = etype,
-
- --
- -- the Sync this delay belongs to
- --
- sync = sync,
-
- --
- -- Latest point in time this should be catered for.
- -- This value is in kernel ticks, return of the C's
- -- times(NULL) call.
- alarm = alarm,
-
- --
- -- Path and filename or dirname of the delay relative
- -- to the syncs root.
- --
- -- for the directories it contains a trailing slash
- --
- path = path,
-
- --
- -- Used only for Moves.
- -- Path and file/dirname of a move destination.
- --
- path2 = path2,
-
- --
- -- Status of the event. Valid stati are:
- --
- -- 'wait' ... the event is ready to be handled.
- --
- -- 'active' ... there is process running catering for this event.
- --
- -- 'blocked' ... this event waits for another to be handled first.
- --
- -- 'done' ... event has been collected. This should never be
- -- visible as all references should be droped on
- -- collection, nevertheless the seperate status is
- -- used as insurrance everything is running correctly.
- status = 'wait',
-
- --
- -- Position in the queue
- --
- dpos = -1,
- }
-
- return o
- end
-
+ local mt = { }
--
- -- Public interface
+ -- Secret key to native table
--
- return { new = new }
-
-end )( )
+ local k_nt = { }
---
--- Combines delays
---
-local Combiner = ( function( )
+ local assignAble =
+ {
+ dpos = true,
+ etype = true,
+ path = true,
+ path2 = true,
+ status = true,
+ }
--
- -- The new delay is absorbed by an older one.
+ -- On accessing a nil index.
--
- local function abso( d1, d2 )
-
- log(
- 'Delay',
- d2.etype, ':',d2.path,
- ' absorbed by ',
- d1.etype,':',d1.path
- )
+ mt.__index = function
+ (
+ t, -- table accessed
+ k -- key value accessed
+ )
+ return t[ k_nt ][ k ]
+ end
- return 'absorb'
+ --
+ -- On assigning a new index.
+ --
+ mt.__newindex = function
+ (
+ t, -- table getting a new index assigned
+ k, -- key value to assign to
+ v -- value to assign
+ )
+ if not assignAble[ k ]
+ then
+ error( 'Cannot assign new key "' .. k .. '" to Delay' )
+ end
+ t[ k_nt ][ k ] = v
end
--
- -- The new delay replaces the old one if it's a file
+ -- This delay is being blocked by another delay
--
- local function refi( d1, d2 )
-
- -- but a directory blocks
- if d2.path:byte( -1 ) == 47 then
+ local function blockedBy
+ (
+ self, -- this delay
+ delay -- the blocking delay
+ )
+ self[ k_nt ].status = 'block'
- log(
- 'Delay',
- d2.etype,':',d2.path,
- ' blocked by ',
- d1.etype,':',d1.path
- )
+ local blocks = delay[ k_nt ].blocks
- return 'stack'
+ if not blocks
+ then
+ blocks = { }
+ delay[ k_nt ].blocks = blocks
end
- log(
- 'Delay',
- d2.etype, ':', d2.path,
- ' replaces ',
- d1.etype, ':', d1.path
- )
+ table.insert( blocks, self )
+ end
- return 'replace'
+ --
+ -- Sets the delay status to 'active'.
+ --
+ local function setActive
+ (
+ self
+ )
+ self[ k_nt ].status = 'active'
end
--
- -- The new delay replaces an older one.
+ -- Sets the delay status to 'wait'
--
- local function repl( d1, d2 )
+ local function wait
+ (
+ self, -- this delay
+ alarm -- alarm for the delay
+ )
+ self[ k_nt ].status = 'wait'
- log(
- 'Delay',
- d2.etype, ':', d2.path,
- ' replaces ',
- d1.etype, ':', d1.path
- )
+ self[ k_nt ].alarm = alarm
+ end
- return 'replace'
+ --
+ -- Creates a new delay.
+ --
+ local function new
+ (
+ etype, -- type of event.
+ -- 'Create', 'Modify', 'Attrib', 'Delete' or 'Move'
+ sync, -- the Sync this delay belongs to
+ alarm, -- latest point in time this should be catered for
+ path, -- path and file-/dirname of the delay relative
+ -- -- to the syncs root.
+ path2 -- used only in moves, path and file-/dirname of
+ -- move destination
+ )
+ local delay =
+ {
+ blockedBy = blockedBy,
+ setActive = setActive,
+ wait = wait,
+ [ k_nt ] =
+ {
+ etype = etype,
+ sync = sync,
+ alarm = alarm,
+ path = path,
+ path2 = path2,
+ status = 'wait'
+ },
+ }
+ setmetatable( delay, mt )
+
+ return delay
end
--
- -- Two delays nullificate each other.
+ -- Public interface
+ --
+ return { new = new }
+end )( )
+
+
+--
+-- Combines delays.
+--
+local Combiner = ( function
+( )
+ --
+ -- The new delay replaces the old one if it's a file
--
- local function null( d1, d2 )
+ local function refi
+ (
+ d1, -- old delay
+ d2 -- new delay
+ )
+ -- but a directory blocks
+ if d2.path:byte( -1 ) == 47
+ then
+ log(
+ 'Delay',
+ d2.etype,': ',d2.path,
+ ' blocked by ',
+ d1.etype,': ',d1.path
+ )
+
+ return 'stack'
+ end
log(
'Delay',
- d2.etype,':',d2.path,
- ' nullifies ',
- d1.etype,':',d1.path
+ d2.etype, ': ', d2.path,
+ ' replaces ',
+ d1.etype, ': ', d1.path
)
- return 'remove'
-
+ return 'replace'
end
--
@@ -542,139 +810,115 @@
local combineNoMove = {
Attrib = {
- Attrib = abso,
- Modify = repl,
- Create = repl,
- Delete = repl
+ Attrib = 'absorb',
+ Modify = 'replace',
+ Create = 'replace',
+ Delete = 'replace'
},
Modify = {
- Attrib = abso,
- Modify = abso,
- Create = repl,
- Delete = repl
+ Attrib = 'absorb',
+ Modify = 'absorb',
+ Create = 'replace',
+ Delete = 'replace'
},
Create = {
- Attrib = abso,
- Modify = abso,
- Create = abso,
- Delete = repl
+ Attrib = 'absorb',
+ Modify = 'absorb',
+ Create = 'absorb',
+ Delete = 'replace'
},
Delete = {
- Attrib = abso,
- Modify = abso,
- Create = refi,
- Delete = abso
+ Attrib = 'absorb',
+ Modify = 'absorb',
+ Create = 'replace file,block dir',
+ Delete = 'absorb'
},
}
--
- -- Combines two delays
+ -- Returns the way two Delay should be combined.
--
- local function combine( d1, d2 )
-
- if d1.etype == 'Init' or d1.etype == 'Blanket' then
-
- -- everything is blocked by init or blanket delays.
- if d2.path2 then
- log(
- 'Delay',
- d2.etype,':',d2.path,'->',d2.path2,
- ' blocked by ',
- d1.etype,' event'
- )
- else
- log(
- 'Delay',
- d2.etype,':',d2.path,
- ' blocked by ',
- d1.etype,' event'
- )
- end
-
+ -- Result:
+ -- nil -- They don't affect each other.
+ -- 'stack' -- Old Delay blocks new Delay.
+ -- 'replace' -- Old Delay is replaced by new Delay.
+ -- 'absorb' -- Old Delay absorbs new Delay.
+ -- 'toDelete,stack' -- Old Delay is turned into a Delete
+ -- and blocks the new Delay.
+ -- 'split' -- New Delay a Move is to be split
+ -- into a Create and Delete.
+ --
+ local function combine
+ (
+ d1, -- old delay
+ d2 -- new delay
+ )
+ if d1.etype == 'Init' or d1.etype == 'Blanket'
+ then
return 'stack'
end
-- two normal events
- if d1.etype ~= 'Move' and d2.etype ~= 'Move' then
-
- if d1.path == d2.path then
- if d1.status == 'active' then
- return 'stack'
+ if d1.etype ~= 'Move' and d2.etype ~= 'Move'
+ then
+ if d1.path == d2.path
+ then
+ -- lookups up the function in the combination matrix
+ -- and calls it
+ local result = combineNoMove[ d1.etype ][ d2.etype ]
+
+ if result == 'replace file,block dir'
+ then
+ if d2.path:byte( -1 ) == 47
+ then
+ return 'stack'
+ else
+ return 'replace'
+ end
end
-
- return combineNoMove[ d1.etype ][ d2.etype ]( d1, d2 )
end
-- if one is a parent directory of another, events are blocking
- if d1.path:byte(-1) == 47 and string.starts(d2.path, d1.path) or
- d2.path:byte(-1) == 47 and string.starts(d1.path, d2.path)
+ if d1.path:byte( -1 ) == 47 and string.starts( d2.path, d1.path )
+ or d2.path:byte( -1 ) == 47 and string.starts( d1.path, d2.path )
then
return 'stack'
end
return nil
-
end
-- non-move event on a move.
- if d1.etype == 'Move' and d2.etype ~= 'Move' then
- -- if the from field could be damaged the events are stacked
- if d1.path == d2.path or
- d2.path:byte(-1) == 47 and string.starts(d1.path, d2.path) or
- d1.path:byte(-1) == 47 and string.starts(d2.path, d1.path)
+ if d1.etype == 'Move' and d2.etype ~= 'Move'
+ then
+ -- if the move source could be damaged the events are stacked
+ if d1.path == d2.path
+ or d2.path:byte( -1 ) == 47 and string.starts( d1.path, d2.path )
+ or d1.path:byte( -1 ) == 47 and string.starts( d2.path, d1.path )
then
- log(
- 'Delay',
- d2.etype, ':', d2.path,
- ' blocked by ',
- 'Move :', d1.path,'->', d1.path2
- )
-
return 'stack'
end
-- the event does something with the move destination
- if d1.path2 == d2.path then
-
- if d2.etype == 'Delete' or d2.etype == 'Create' then
-
- if d1.status == 'active' then
- return 'stack'
- end
-
- log(
- 'Delay',
- d2.etype, ':', d2.path,
- ' turns ',
- 'Move :', d1.path, '->', d1.path2,
- ' into ',
- 'Delete:', d1.path
- )
- d1.etype = 'Delete'
- d1.path2 = nil
-
- return 'stack'
+ if d1.path2 == d2.path
+ then
+ if d2.etype == 'Delete'
+ or d2.etype == 'Create'
+ then
+ return 'toDelete,stack'
end
-- on 'Attrib' or 'Modify' simply stack on moves
-
return 'stack'
end
- if d2.path :byte(-1) == 47 and string.starts(d1.path2, d2.path) or
- d1.path2:byte(-1) == 47 and string.starts(d2.path, d1.path2)
+ if d2.path:byte( -1 ) == 47 and string.starts( d1.path2, d2.path )
+ or d1.path2:byte( -1 ) == 47 and string.starts( d2.path, d1.path2 )
then
- log(
- 'Delay'
- ,d2.etype, ':', d2.path,
- ' blocked by ',
- 'Move:', d1.path, '->', d1.path2
- )
-
return 'stack'
end
@@ -682,19 +926,15 @@
end
-- a move upon a non-move event
- if d1.etype ~= 'Move' and d2.etype == 'Move' then
- if d1.path == d2.path or d1.path == d2.path2 or
- d1.path :byte(-1) == 47 and string.starts(d2.path, d1.path) or
- d1.path :byte(-1) == 47 and string.starts(d2.path2, d1.path) or
- d2.path :byte(-1) == 47 and string.starts(d1.path, d2.path) or
- d2.path2:byte(-1) == 47 and string.starts(d1.path, d2.path2)
+ if d1.etype ~= 'Move' and d2.etype == 'Move'
+ then
+ if d1.path == d2.path
+ or d1.path == d2.path2
+ or d1.path:byte( -1 ) == 47 and string.starts( d2.path, d1.path )
+ or d1.path:byte( -1 ) == 47 and string.starts( d2.path2, d1.path )
+ or d2.path:byte( -1 ) == 47 and string.starts( d1.path, d2.path )
+ or d2.path2:byte( -1 ) == 47 and string.starts( d1.path, d2.path2 )
then
- log(
- 'Delay',
- 'Move:', d2.path, '->', d2.path2,
- ' splits on ',
- d1.etype, ':', d1.path
- )
return 'split'
end
@@ -704,27 +944,22 @@
--
-- a move event upon a move event
--
- if d1.etype == 'Move' and d2.etype == 'Move' then
+ if d1.etype == 'Move' and d2.etype == 'Move'
+ then
-- TODO combine moves,
-
- if d1.path == d2.path or d1.path == d2.path2 or
- d1.path2 == d2.path or d2.path2 == d2.path or
- d1.path :byte(-1) == 47 and string.starts(d2.path, d1.path) or
- d1.path :byte(-1) == 47 and string.starts(d2.path2, d1.path) or
- d1.path2:byte(-1) == 47 and string.starts(d2.path, d1.path2) or
- d1.path2:byte(-1) == 47 and string.starts(d2.path2, d1.path2) or
- d2.path :byte(-1) == 47 and string.starts(d1.path, d2.path) or
- d2.path :byte(-1) == 47 and string.starts(d1.path2, d2.path) or
- d2.path2:byte(-1) == 47 and string.starts(d1.path, d2.path2) or
- d2.path2:byte(-1) == 47 and string.starts(d1.path2, d2.path2)
+ if d1.path == d2.path
+ or d1.path == d2.path2
+ or d1.path2 == d2.path
+ or d2.path2 == d2.path
+ or d1.path:byte( -1 ) == 47 and string.starts( d2.path, d1.path )
+ or d1.path:byte( -1 ) == 47 and string.starts( d2.path2, d1.path )
+ or d1.path2:byte( -1 ) == 47 and string.starts( d2.path, d1.path2 )
+ or d1.path2:byte( -1 ) == 47 and string.starts( d2.path2, d1.path2 )
+ or d2.path:byte( -1 ) == 47 and string.starts( d1.path, d2.path )
+ or d2.path:byte( -1 ) == 47 and string.starts( d1.path2, d2.path )
+ or d2.path2:byte( -1 ) == 47 and string.starts( d1.path, d2.path2 )
+ or d2.path2:byte( -1 ) == 47 and string.starts( d1.path2, d2.path2 )
then
- log(
- 'Delay',
- 'Move:', d2.path, '->', d1.path2,
- ' splits on Move:',
- d1.path, '->', d1.path2
- )
-
return 'split'
end
@@ -736,17 +971,167 @@
--
+ -- The new delay is absorbed by an older one.
+ --
+ local function logAbsorb
+ (
+ d1, -- old delay
+ d2 -- new delay
+ )
+ log(
+ 'Delay',
+ d2.etype, ': ',d2.path,
+ ' absorbed by ',
+ d1.etype,': ',d1.path
+ )
+ end
+
+ --
+ -- The new delay replaces the old one if it's a file.
+ --
+ local function logReplace
+ (
+ d1, -- old delay
+ d2 -- new delay
+ )
+ log(
+ 'Delay',
+ d2.etype, ': ', d2.path,
+ ' replaces ',
+ d1.etype, ': ', d1.path
+ )
+ end
+
+
+ --
+ -- The new delay splits on the old one.
+ --
+ local function logSplit
+ (
+ d1, -- old delay
+ d2 -- new delay
+ )
+ log(
+ 'Delay',
+ d2.etype, ': ',
+ d2.path, ' -> ', d2.path2,
+ ' splits on ',
+ d1.etype, ': ', d1.path
+ )
+ end
+
+ --
+ -- The new delay is blocked by the old delay.
+ --
+ local function logStack
+ (
+ d1, -- old delay
+ d2 -- new delay
+ )
+ local active = ''
+
+ if d1.active
+ then
+ active = 'active '
+ end
+
+ if d2.path2
+ then
+ log(
+ 'Delay',
+ d2.etype, ': ',
+ d2.path, '->', d2.path2,
+ ' blocked by ',
+ active,
+ d1.etype, ': ', d1.path
+ )
+ else
+ log(
+ 'Delay',
+ d2.etype, ': ', d2.path,
+ ' blocked by ',
+ active,
+ d1.etype, ': ', d1.path
+ )
+ end
+ end
+
+
+ --
+ -- The new delay turns the old one (a move) into a delete and is blocked.
+ --
+ local function logToDeleteStack
+ (
+ d1, -- old delay
+ d2 -- new delay
+ )
+ if d1.path2
+ then
+ log(
+ 'Delay',
+ d2.etype, ': ', d2.path,
+ ' turns ',
+ d1.etype, ': ', d1.path, ' -> ', d1.path2,
+ ' into Delete: ', d1.path
+ )
+ else
+ log(
+ 'Delay',
+ d2.etype, ': ', d2.path,
+ ' turns ',
+ d1.etype, ': ', d1.path,
+ ' into Delete: ', d1.path
+ )
+ end
+ end
+
+
+ local logFuncs =
+ {
+ absorb = logAbsorb,
+ replace = logReplace,
+ split = logSplit,
+ stack = logStack,
+ [ 'toDelete,stack' ] = logToDeleteStack
+ }
+
+
+ --
+ -- Prints the log message for a combination result
+ --
+ local function log
+ (
+ result, -- the combination result
+ d1, -- old delay
+ d2 -- new delay
+ )
+ local lf = logFuncs[ result ]
+
+ if not lf
+ then
+ error( 'unknown combination result: ' .. result )
+ end
+
+ lf( d1, d2 )
+ end
+
+ --
-- Public interface
--
- return { combine = combine }
+ return
+ {
+ combine = combine,
+ log = log
+ }
end )( )
+
--
-- Creates inlets for syncs: the user interface for events.
--
-local InletFactory = ( function( )
-
+local InletFactory = ( function
+( )
--
-- Table to receive the delay of an event
-- or the delay list of an event list.
@@ -773,9 +1158,13 @@
--
-- Removes the trailing slash from a path.
--
- local function cutSlash( path )
- if string.byte(path, -1) == 47 then
- return string.sub(path, 1, -2)
+ local function cutSlash
+ (
+ path -- path to cut
+ )
+ if string.byte( path, -1 ) == 47
+ then
+ return string.sub( path, 1, -2 )
else
return path
end
@@ -784,8 +1173,12 @@
--
-- Gets the path of an event.
--
- local function getPath( event )
- if event.move ~= 'To' then
+ local function getPath
+ (
+ event
+ )
+ if event.move ~= 'To'
+ then
return e2d[ event ].path
else
return e2d[ event ].path2
@@ -803,14 +1196,20 @@
--
-- TODO give user a readonly version.
--
- config = function( event )
+ config = function
+ (
+ event
+ )
return e2d[ event ].sync.config
end,
- -----
+ --
-- Returns the inlet belonging to an event.
--
- inlet = function( event )
+ inlet = function
+ (
+ event
+ )
return e2d[ event ].sync.inlet
end,
@@ -819,14 +1218,18 @@
--
-- Can be: 'Attrib', 'Create', 'Delete', 'Modify' or 'Move',
--
- etype = function( event )
+ etype = function
+ (
+ event
+ )
return e2d[ event ].etype
end,
--
-- Events are not lists.
--
- isList = function( )
+ isList = function
+ ( )
return false
end,
@@ -836,14 +1239,20 @@
-- Can be:
-- 'wait', 'active', 'block'.
--
- status = function( event )
+ status = function
+ (
+ event
+ )
return e2d[ event ].status
end,
--
-- Returns true if event relates to a directory
--
- isdir = function( event )
+ isdir = function
+ (
+ event
+ )
return string.byte( getPath( event ), -1 ) == 47
end,
@@ -852,7 +1261,10 @@
--
-- Includes a trailing slash for dirs.
--
- name = function( event )
+ name = function
+ (
+ event
+ )
return string.match( getPath( event ), '[^/]+/?$' )
end,
@@ -860,39 +1272,75 @@
-- Returns the name of the file/dir
-- excluding a trailing slash for dirs.
--
- basename = function( event )
+ basename = function
+ (
+ event
+ )
return string.match( getPath( event ), '([^/]+)/?$')
end,
- ---
+ --
-- Returns the file/dir relative to watch root
-- including a trailing slash for dirs.
--
- path = function( event )
- return getPath( event )
+ path = function
+ (
+ event
+ )
+ local p = getPath( event )
+
+ if string.byte( p, 1 ) == 47
+ then
+ p = string.sub( p, 2, -1 )
+ end
+
+ return p
end,
--
-- Returns the directory of the file/dir relative to watch root
-- Always includes a trailing slash.
--
- pathdir = function( event )
- return string.match( getPath( event ), '^(.*/)[^/]+/?' ) or ''
+ pathdir = function
+ (
+ event
+ )
+ local p = getPath( event )
+
+ if string.byte( p, 1 ) == 47
+ then
+ p = string.sub( p, 2, -1 )
+ end
+
+ return string.match( p, '^(.*/)[^/]+/?' ) or ''
end,
--
-- Returns the file/dir relativ to watch root
-- excluding a trailing slash for dirs.
--
- pathname = function( event )
- return cutSlash( getPath( event ) )
+ pathname = function
+ (
+ event
+ )
+ local p = getPath( event )
+
+ if string.byte( p, 1 ) == 47
+ then
+ p = string.sub( p, 2, -1 )
+ end
+
+ return cutSlash( p )
end,
- ---
+ --
-- Returns the absolute path of the watch root.
-- All symlinks are resolved.
--
- source = function( event )
+ source = function
+ (
+ event
+ )
return e2d[ event ].sync.source
end,
@@ -900,7 +1348,10 @@
-- Returns the absolute path of the file/dir
-- including a trailing slash for dirs.
--
- sourcePath = function( event )
+ sourcePath = function
+ (
+ event
+ )
return e2d[ event ].sync.source .. getPath( event )
end,
@@ -908,23 +1359,37 @@
-- Returns the absolute dir of the file/dir
-- including a trailing slash.
--
- sourcePathdir = function( event )
- return e2d[event].sync.source ..
- ( string.match( getPath( event ), '^(.*/)[^/]+/?' ) or '' )
+ sourcePathdir = function
+ (
+ event
+ )
+ return(
+ e2d[event].sync.source
+ .. (
+ string.match( getPath( event ), '^(.*/)[^/]+/?' )
+ or ''
+ )
+ )
end,
- ------
+ --
-- Returns the absolute path of the file/dir
-- excluding a trailing slash for dirs.
--
- sourcePathname = function( event )
+ sourcePathname = function
+ (
+ event
+ )
return e2d[ event ].sync.source .. cutSlash( getPath( event ) )
end,
--
- -- Returns the configured target
+ -- Returns the configured target.
--
- target = function( event )
+ target = function
+ (
+ event
+ )
return e2d[ event ].sync.config.target
end,
@@ -932,7 +1397,10 @@
-- Returns the relative dir/file appended to the target
-- including a trailing slash for dirs.
--
- targetPath = function( event )
+ targetPath = function
+ (
+ event
+ )
return e2d[ event ].sync.config.target .. getPath( event )
end,
@@ -940,9 +1408,17 @@
-- Returns the dir of the dir/file appended to the target
-- including a trailing slash.
--
- targetPathdir = function( event )
- return e2d[ event ].sync.config.target ..
- ( string.match( getPath( event ), '^(.*/)[^/]+/?' ) or '' )
+ targetPathdir = function
+ (
+ event
+ )
+ return(
+ e2d[ event ].sync.config.target
+ .. (
+ string.match( getPath( event ), '^(.*/)[^/]+/?' )
+ or ''
+ )
+ )
end,
--
@@ -950,68 +1426,84 @@
-- excluding a trailing slash for dirs.
--
targetPathname = function( event )
- return e2d[ event ].sync.config.target ..
- cutSlash( getPath( event ) )
+ return(
+ e2d[ event ].sync.config.target
+ .. cutSlash( getPath( event ) )
+ )
end,
}
--
-- Retrievs event fields for the user script.
--
- local eventMeta = {
-
- __index = function( event, field )
+ local eventMeta =
+ {
+ __index = function
+ (
+ event,
+ field
+ )
local f = eventFields[ field ]
- if not f then
- if field == 'move' then
+
+ if not f
+ then
+ if field == 'move'
+ then
-- possibly undefined
return nil
end
- error( 'event does not have field "'..field..'"', 2 )
+
+ error( 'event does not have field "' .. field .. '"', 2 )
end
+
return f( event )
end
-
}
--
-- Interface for user scripts to get list fields.
--
- local eventListFuncs = {
-
+ local eventListFuncs =
+ {
--
-- Returns a list of paths of all events in list.
--
- -- @param elist -- handle returned by getevents()
- -- @param mutator -- if not nil called with (etype, path, path2)
- -- returns one or two strings to add.
--
- getPaths = function( elist, mutator )
-
- local dlist = e2d[elist]
+ getPaths = function
+ (
+ elist, -- handle returned by getevents( )
+ mutator -- if not nil called with ( etype, path, path2 )
+ -- returns one or two strings to add.
+ )
+ local dlist = e2d[ elist ]
- if not dlist then
+ if not dlist
+ then
error( 'cannot find delay list from event list.' )
end
local result = { }
local resultn = 1
- for k, d in ipairs( dlist ) do
-
+ for k, d in ipairs( dlist )
+ do
local s1, s2
- if mutator then
+ if mutator
+ then
s1, s2 = mutator( d.etype, d.path, d.path2 )
else
s1, s2 = d.path, d.path2
end
result[ resultn ] = s1
+
resultn = resultn + 1
- if s2 then
+ if s2
+ then
result[ resultn ] = s2
+
resultn = resultn + 1
end
end
@@ -1024,33 +1516,38 @@
--
-- Retrievs event list fields for the user script
--
- local eventListMeta = {
-
- __index = function( elist, func )
-
- if func == 'isList' then
+ local eventListMeta =
+ {
+ __index = function
+ (
+ elist,
+ func
+ )
+ if func == 'isList'
+ then
return true
end
- if func == 'config' then
+ if func == 'config'
+ then
return e2d[ elist ].sync.config
end
local f = eventListFuncs[ func ]
- if not f then
+ if not f
+ then
error(
'event list does not have function "' .. func .. '"',
2
)
end
- return function( ... )
+ return function
+ ( ... )
return f( elist, ... )
end
-
end
-
}
--
@@ -1066,34 +1563,34 @@
--
-- Encapsulates a delay into an event for the user script.
--
- local function d2e( delay )
-
+ local function d2e
+ (
+ delay -- delay to encapsulate
+ )
-- already created?
- local eu = e2d2[delay]
+ local eu = e2d2[ delay ]
- if delay.etype ~= 'Move' then
-
- if eu then
- return eu
- end
+ if delay.etype ~= 'Move'
+ then
+ if eu then return eu end
local event = { }
+
setmetatable( event, eventMeta )
+
e2d[ event ] = delay
+
e2d2[ delay ] = event
return event
-
else
-- moves have 2 events - origin and destination
- if eu then
- return eu[1], eu[2]
- end
+ if eu then return eu[1], eu[2] end
local event = { move = 'Fr' }
local event2 = { move = 'To' }
- setmetatable( event, eventMeta )
+ setmetatable( event, eventMeta )
setmetatable( event2, eventMeta )
e2d[ event ] = delay
@@ -1103,61 +1600,85 @@
-- move events have a field 'move'
return event, event2
-
end
end
--
-- Encapsulates a delay list into an event list for the user script.
--
- local function dl2el( dlist )
-
+ local function dl2el
+ (
+ dlist
+ )
local eu = e2d2[ dlist ]
- if eu then
- return eu
- end
+ if eu then return eu end
local elist = { }
setmetatable( elist, eventListMeta )
e2d [ elist ] = dlist
+
e2d2[ dlist ] = elist
return elist
-
end
--
-- The functions the inlet provides.
--
- local inletFuncs = {
-
+ local inletFuncs =
+ {
--
-- Adds an exclude.
--
- addExclude = function( sync, pattern )
+ addExclude = function
+ (
+ sync, -- the sync of the inlet
+ pattern -- exlusion pattern to add
+ )
sync:addExclude( pattern )
end,
+
+ --
+ -- Appens a filter.
+ --
+ appendFilter = function
+ (
+ sync, -- the sync of the inlet
+ rule, -- '+' or '-'
+ pattern -- exlusion pattern to add
+ )
+ sync:appendFilter( rule, pattern )
+ end,
+
--
-- Removes an exclude.
--
- rmExclude = function( sync, pattern )
+ rmExclude = function
+ (
+ sync, -- the sync of the inlet
+ pattern -- exlusion pattern to remove
+ )
sync:rmExclude( pattern )
end,
--
- -- Gets the list of excludes in their original rsynlike patterns form.
+ -- Gets the list of excludes in their
+ -- rsync-like patterns form.
--
- getExcludes = function( sync )
-
+ getExcludes = function
+ (
+ sync -- the sync of the inlet
+ )
-- creates a copy
local e = { }
local en = 1;
- for k, _ in pairs( sync.excludes.list ) do
+ for k, _ in pairs( sync.excludes.list )
+ do
e[ en ] = k;
en = en + 1;
end
@@ -1166,51 +1687,110 @@
end,
--
+ -- Gets the list of filters and excldues
+ -- as rsync-like filter/patterns form.
+ --
+ getFilters = function
+ (
+ sync -- the sync of the inlet
+ )
+ -- creates a copy
+ local e = { }
+ local en = 1;
+
+ -- first takes the filters
+ if sync.filters
+ then
+ for _, entry in ipairs( sync.filters.list )
+ do
+ e[ en ] = entry.rule .. ' ' .. entry.pattern;
+ en = en + 1;
+ end
+ end
+
+ -- then the excludes
+ for k, _ in pairs( sync.excludes.list )
+ do
+ e[ en ] = '- ' .. k;
+ en = en + 1;
+ end
+
+ return e;
+ end,
+
+ --
+ -- Returns true if the sync has filters
+ --
+ hasFilters = function
+ (
+ sync -- the sync of the inlet
+ )
+ return not not sync.filters
+ end,
+
+ --
-- Creates a blanketEvent that blocks everything
-- and is blocked by everything.
--
- createBlanketEvent = function( sync )
+ createBlanketEvent = function
+ (
+ sync -- the sync of the inlet
+ )
return d2e( sync:addBlanketDelay( ) )
end,
--
-- Discards a waiting event.
--
- discardEvent = function( sync, event )
+ discardEvent = function
+ (
+ sync,
+ event
+ )
local delay = e2d[ event ]
- if delay.status ~= 'wait' then
+
+ if delay.status ~= 'wait'
+ then
log(
'Error',
'Ignored cancel of a non-waiting event of type ',
event.etype
)
+
return
end
+
sync:removeDelay( delay )
end,
--
-- Gets the next not blocked event from queue.
--
- getEvent = function( sync )
+ getEvent = function
+ (
+ sync
+ )
return d2e( sync:getNextDelay( now( ) ) )
end,
--
-- Gets all events that are not blocked by active events.
--
- -- @param if not nil a function to test each delay
- --
- getEvents = function( sync, test )
+ getEvents = function
+ (
+ sync, -- the sync of the inlet
+ test -- if not nil use this function to test if to include an event
+ )
local dlist = sync:getDelays( test )
+
return dl2el( dlist )
end,
--
- -- Returns the configuration table specified by sync{}
+ -- Returns the configuration table specified by sync{ }
--
getConfig = function( sync )
- -- TODO gives a readonly handler only.
+ -- TODO give a readonly handler only.
return sync.config
end,
}
@@ -1218,14 +1798,18 @@
--
-- Forwards access to inlet functions.
--
- local inletMeta = {
- __index = function( inlet, func )
+ local inletMeta =
+ {
+ __index = function
+ (
+ inlet,
+ func
+ )
local f = inletFuncs[ func ]
- if not f then
- error(
- 'inlet does not have function "'..func..'"',
- 2
- )
+
+ if not f
+ then
+ error( 'inlet does not have function "'..func..'"', 2 )
end
return function( ... )
@@ -1235,30 +1819,40 @@
}
--
- -- Creates a new inlet for Sync.
+ -- Creates a new inlet for a sync.
--
- local function newInlet( sync )
-
+ local function newInlet
+ (
+ sync -- the sync to create the inlet for
+ )
-- Lsyncd runner controlled variables
local inlet = { }
-- sets use access methods
setmetatable( inlet, inletMeta )
+
inlets[ inlet ] = sync
+
return inlet
end
--
-- Returns the delay from a event.
--
- local function getDelayOrList( event )
+ local function getDelayOrList
+ (
+ event
+ )
return e2d[ event ]
end
--
-- Returns the sync from an event or list
--
- local function getSync( event )
+ local function getSync
+ (
+ event
+ )
return e2d[ event ].sync
end
@@ -1272,21 +1866,219 @@
getSync = getSync,
newInlet = newInlet,
}
+end )( )
+
+
+--
+-- A set of exclude patterns.
+--
+local Excludes = ( function
+( )
+ --
+ -- Turns a rsync like file pattern to a lua pattern.
+ -- ( at best it can )
+ --
+ local function toLuaPattern
+ (
+ p -- the rsync like pattern
+ )
+ local o = p
+
+ p = string.gsub( p, '%%', '%%%%' )
+ p = string.gsub( p, '%^', '%%^' )
+ p = string.gsub( p, '%$', '%%$' )
+ p = string.gsub( p, '%(', '%%(' )
+ p = string.gsub( p, '%)', '%%)' )
+ p = string.gsub( p, '%.', '%%.' )
+ p = string.gsub( p, '%[', '%%[' )
+ p = string.gsub( p, '%]', '%%]' )
+ p = string.gsub( p, '%+', '%%+' )
+ p = string.gsub( p, '%-', '%%-' )
+ p = string.gsub( p, '%?', '[^/]' )
+ p = string.gsub( p, '%*', '[^/]*' )
+ -- this was a ** before
+ p = string.gsub( p, '%[%^/%]%*%[%^/%]%*', '.*' )
+ p = string.gsub( p, '^/', '^/' )
+
+ if p:sub( 1, 2 ) ~= '^/'
+ then
+ -- if does not begin with '^/'
+ -- then all matches should begin with '/'.
+ p = '/' .. p;
+ end
+
+ log( 'Exclude', 'toLuaPattern "', o, '" = "', p, '"' )
+
+ return p
+ end
+
+ --
+ -- Adds a pattern to exclude.
+ --
+ local function add
+ (
+ self,
+ pattern -- the pattern to exclude
+ )
+ if self.list[ pattern ]
+ then -- already in the list
+ return
+ end
+
+ local lp = toLuaPattern( pattern )
+
+ self.list[ pattern ] = lp
+ end
+
+ --
+ -- Removes a pattern to exclude.
+ --
+ local function remove
+ (
+ self, -- self
+ pattern -- the pattern to remove
+ )
+ -- already in the list?
+ if not self.list[ pattern ]
+ then
+ log(
+ 'Normal',
+ 'Removing not excluded exclude "' .. pattern .. '"'
+ )
+
+ return
+ end
+
+ self.list[ pattern ] = nil
+ end
+
+ --
+ -- Adds a list of patterns to exclude.
+ --
+ local function addList
+ (
+ self,
+ plist
+ )
+ for _, v in ipairs( plist )
+ do
+ add( self, v )
+ end
+ end
+
+ --
+ -- Loads the excludes from a file.
+ --
+ local function loadFile
+ (
+ self, -- self
+ file -- filename to load from
+ )
+ f, err = io.open( file )
+
+ if not f
+ then
+ log( 'Error', 'Cannot open exclude file "', file,'": ', err )
+
+ terminate( -1 )
+ end
+
+ for line in f:lines()
+ do
+ -- lsyncd 2.0 does not support includes
+
+ if not string.match( line, '^%s*%+' )
+ and not string.match( line, '^%s*#' )
+ and not string.match( line, '^%s*$' )
+ then
+ local p = string.match( line, '%s*-?%s*(.*)' )
+
+ if p
+ then
+ add( self, p )
+ end
+ end
+ end
+
+ f:close( )
+ end
+
+ --
+ -- Tests if 'path' is excluded.
+ --
+ local function test
+ (
+ self, -- self
+ path -- the path to test
+ )
+ if path:byte( 1 ) ~= 47
+ then
+ error( 'Paths for exlusion tests must start with \'/\'' )
+ end
+
+ for _, p in pairs( self.list )
+ do
+ if p:byte( -1 ) == 36
+ then
+ -- ends with $
+ if path:match( p )
+ then
+ return true
+ end
+ else
+ -- ends either end with / or $
+ if path:match( p .. '/' )
+ or path:match( p .. '$' )
+ then
+ return true
+ end
+ end
+ end
+
+ return false
+ end
+
+ --
+ -- Cretes a new exclude set.
+ --
+ local function new
+ ( )
+ return {
+ list = { },
+
+ -- functions
+ add = add,
+ addList = addList,
+ loadFile = loadFile,
+ remove = remove,
+ test = test,
+ }
+ end
+ --
+ -- Public interface.
+ --
+ return { new = new }
end )( )
--
--- A set of exclude patterns
+-- A set of filter patterns.
--
-local Excludes = ( function( )
-
+-- Filters allow excludes and includes
+--
+local Filters = ( function
+( )
--
-- Turns a rsync like file pattern to a lua pattern.
-- ( at best it can )
--
- local function toLuaPattern( p )
+ local function toLuaPattern
+ (
+ p -- the rsync like pattern
+ )
local o = p
+
p = string.gsub( p, '%%', '%%%%' )
p = string.gsub( p, '%^', '%%^' )
p = string.gsub( p, '%$', '%%$' )
@@ -1303,96 +2095,78 @@
p = string.gsub( p, '%[%^/%]%*%[%^/%]%*', '.*' )
p = string.gsub( p, '^/', '^/' )
- if p:sub( 1, 2 ) ~= '^/' then
+ if p:sub( 1, 2 ) ~= '^/'
+ then
-- if does not begin with '^/'
-- then all matches should begin with '/'.
p = '/' .. p;
end
- log(
- 'Exclude',
- 'toLuaPattern "',
- o, '" = "', p, '"'
- )
+ log( 'Filter', 'toLuaPattern "', o, '" = "', p, '"' )
return p
end
--
- -- Adds a pattern to exclude
+ -- Appends a filter pattern
--
- local function add( self, pattern )
+ local function append
+ (
+ self, -- the filters object
+ line -- filter line
+ )
+ local rule, pattern = string.match( line, '%s*([+|-])%s*(.*)' )
- if self.list[ pattern ] then
- -- already in the list
- return
+ if not rule or not pattern
+ then
+ log( 'Error', 'Unknown filter rule: "', line, '"' )
+ terminate( -1 )
end
local lp = toLuaPattern( pattern )
- self.list[ pattern ] = lp
+ table.insert( self. list, { rule = rule, pattern = pattern, lp = lp } )
end
--
- -- Removes a pattern to exclude.
- --
- local function remove( self, pattern )
-
- if not self.list[ pattern ] then
- -- already in the list?
-
- log(
- 'Normal',
- 'Removing not excluded exclude "' .. pattern .. '"'
- )
-
- return
- end
-
- self.list[pattern] = nil
-
- end
-
-
- -----
-- Adds a list of patterns to exclude.
--
- local function addList(self, plist)
- for _, v in ipairs(plist) do
- add(self, v)
+ local function appendList
+ (
+ self,
+ plist
+ )
+ for _, v in ipairs( plist )
+ do
+ append( self, v )
end
end
--
- -- Loads the excludes from a file
+ -- Loads the filters from a file.
--
- local function loadFile( self, file )
-
+ local function loadFile
+ (
+ self, -- self
+ file -- filename to load from
+ )
f, err = io.open( file )
- if not f then
- log(
- 'Error',
- 'Cannot open exclude file "', file,'": ',
- err
- )
+ if not f
+ then
+ log( 'Error', 'Cannot open filter file "', file, '": ', err )
terminate( -1 )
end
- for line in f:lines() do
-
- -- lsyncd 2.0 does not support includes
-
- if not string.match(line, '^%s*%+') and
- not string.match(line, '^%s*#') and
- not string.match(line, '^%s*$') then
- local p = string.match(
- line, '%s*-?%s*(.*)'
- )
- if p then
- add(self, p)
- end
+ for line in f:lines( )
+ do
+ if string.match( line, '^%s*#' )
+ or string.match( line, '^%s*$' )
+ then
+ -- a comment or empty line: ignore
+ else
+ append( self, line )
end
end
@@ -1400,64 +2174,76 @@
end
--
- -- Tests if 'path' is excluded.
+ -- Tests if 'path' is filtered.
--
- local function test( self, path )
+ local function test
+ (
+ self, -- self
+ path -- the path to test
+ )
+ if path:byte( 1 ) ~= 47
+ then
+ error( 'Paths for filter tests must start with \'/\'' )
+ end
- for _, p in pairs( self.list ) do
+ for _, entry in ipairs( self.list )
+ do
+ local rule = entry.rule
+ local lp = entry.lp -- lua pattern
- if p:byte( -1 ) == 36 then
+ if lp:byte( -1 ) == 36
+ then
-- ends with $
-
- if path:match( p ) then
- return true
+ if path:match( lp )
+ then
+ return rule == '-'
end
-
else
-
-- ends either end with / or $
- if path:match(p .. '/') or path:match(p .. '$') then
- return true
+ if path:match( lp .. '/' )
+ or path:match( lp .. '$' )
+ then
+ return rule == '-'
end
-
end
end
- return false
-
+ -- nil means neither a positivie
+ -- or negative hit, thus excludes have to
+ -- be queried
+ return nil
end
-
--
- -- Cretes a new exclude set
+ -- Cretes a new filter set.
--
- local function new( )
-
+ local function new
+ ( )
return {
list = { },
-
-- functions
- add = add,
- addList = addList,
- loadFile = loadFile,
- remove = remove,
- test = test,
+ append = append,
+ appendList = appendList,
+ loadFile = loadFile,
+ test = test,
}
-
end
+
--
- -- Public interface
+ -- Public interface.
--
return { new = new }
end )( )
+
+
--
-- Holds information about one observed directory including subdirs.
--
-local Sync = ( function( )
-
+local Sync = ( function
+( )
--
-- Syncs that have no name specified by the user script
-- get an incremental default name 'Sync[X]'
@@ -1467,90 +2253,127 @@
--
-- Adds an exclude.
--
- local function addExclude( self, pattern )
-
+ local function addExclude
+ (
+ self,
+ pattern
+ )
return self.excludes:add( pattern )
+ end
+ local function appendFilter
+ (
+ self,
+ rule,
+ pattern
+ )
+ if not self.filters then self.filters = Filters.new( ) end
+
+ return self.filters:append( rule, pattern )
end
--
-- Removes an exclude.
--
- local function rmExclude( self, pattern )
-
+ local function rmExclude
+ (
+ self,
+ pattern
+ )
return self.excludes:remove( pattern )
-
end
--
-- Removes a delay.
--
- local function removeDelay( self, delay )
-
- if self.delays[ delay.dpos ] ~= delay then
- error( 'Queue is broken, delay not a dpos' )
+ local function removeDelay
+ (
+ self,
+ delay
+ )
+ if self.delays[ delay.dpos ] ~= delay
+ then
+ error( 'Queue is broken, delay not at dpos' )
end
- Queue.remove( self.delays, delay.dpos )
-
- -- free all delays blocked by this one.
- if delay.blocks then
-
- for i, vd in pairs( delay.blocks ) do
+ self.delays:remove( delay.dpos )
+ -- frees all delays blocked by this one.
+ if delay.blocks
+ then
+ for _, vd in pairs( delay.blocks )
+ do
vd.status = 'wait'
-
end
-
end
end
-
+
+
--
- -- Returns true if this Sync concerns about 'path'
- --
- local function concerns( self, path )
+ -- Returns true if the relative path is excluded or filtered
+ --
+ local function testFilter
+ (
+ self, -- the Sync
+ path -- the relative path
+ )
+ -- never filter the relative root itself
+ -- ( that would make zero sense )
+ if path == '/' then return false end
- -- not concerned if watch rootdir doesnt match
- if not path:starts( self.source ) then
+ local filter = self.filters and self.filters:test( path )
- return false
+ if filter ~= nil then return filter end
+
+ -- otherwise check excludes if concerned
+ return self.excludes:test( path )
+ end
+ --
+ -- Returns true if this Sync concerns about 'path'.
+ --
+ local function concerns
+ (
+ self, -- the Sync
+ path -- the absolute path
+ )
+ -- not concerned if watch rootdir doesn't match
+ if not path:starts( self.source )
+ then
+ return false
end
-- a sub dir and not concerned about subdirs
- if self.config.subdirs == false and
-
- path:sub( #self.source, -1 ):match( '[^/]+/?' )
-
+ if self.config.subdirs == false
+ and path:sub( #self.source, -1 ):match( '[^/]+/?' )
then
-
return false
-
end
- -- concerned if not excluded
- return not self.excludes:test( path:sub( #self.source ) )
-
+ return not testFilter( self, path:sub( #self.source ) )
end
--
- -- Collects a child process
+ -- Collects a child process.
--
- local function collect( self, pid, exitcode )
-
+ local function collect
+ (
+ self, -- the sync
+ pid, -- process id of collected child process
+ exitcode -- exitcode of child process
+ )
local delay = self.processes[ pid ]
- if not delay then
- -- not a child of this sync.
- return
- end
-
- if delay.status then
+ -- not a child of this sync?
+ if not delay then return end
+ if delay.status
+ then
log( 'Delay', 'collected an event' )
- if delay.status ~= 'active' then
- error('collecting a non-active process')
+ if delay.status ~= 'active'
+ then
+ error( 'collecting a non-active process' )
end
local rc = self.config.collect(
@@ -1558,14 +2381,16 @@
exitcode
)
- if rc == 'die' then
- log( 'Error', 'Critical exitcode.' );
- terminate( -1 )
- end
+ if rc == 'die'
+ then
+ log( 'Error', 'Critical exitcode.' )
- if rc ~= 'again' then
+ terminate( -1 )
+ elseif rc ~= 'again'
+ then
-- if its active again the collecter restarted the event
removeDelay( self, delay )
+
log(
'Delay',
'Finish of ',
@@ -1577,56 +2402,44 @@
)
else
-- sets the delay on wait again
- delay.status = 'wait'
-
local alarm = self.config.delay
-- delays at least 1 second
- if alarm < 1 then
- alarm = 1
- end
+ if alarm < 1 then alarm = 1 end
- delay.alarm = now( ) + alarm
+ delay:wait( now( ) + alarm )
end
else
- log(
- 'Delay',
- 'collected a list'
- )
+ log( 'Delay', 'collected a list' )
local rc = self.config.collect(
InletFactory.dl2el( delay ),
exitcode
)
- if rc == 'die' then
+ if rc == 'die'
+ then
log( 'Error', 'Critical exitcode.' );
- terminate( -1 )
- end
-
- if rc == 'again' then
+ terminate( -1 )
+ elseif rc == 'again'
+ then
-- sets the delay on wait again
- delay.status = 'wait'
local alarm = self.config.delay
- -- delays at least 1 second
- if alarm < 1 then
- alarm = 1
- end
+
+ -- delays are at least 1 second
+ if alarm < 1 then alarm = 1 end
alarm = now() + alarm
- for _, d in ipairs( delay ) do
- d.alarm = alarm
- d.status = 'wait'
+ for _, d in ipairs( delay )
+ do
+ d:wait( alarm )
end
- end
-
- for _, d in ipairs( delay ) do
- if rc ~= 'again' then
+ else
+ for _, d in ipairs( delay )
+ do
removeDelay( self, d )
- else
- d.status = 'wait'
end
end
@@ -1643,23 +2456,25 @@
-- A delay can block 'n' other delays,
-- but is blocked at most by one, the latest delay.
--
- local function stack( oldDelay, newDelay )
-
- newDelay.status = 'block'
-
- if not oldDelay.blocks then
- oldDelay.blocks = { }
- end
-
- table.insert( oldDelay.blocks, newDelay )
-
+ local function stack
+ (
+ oldDelay,
+ newDelay
+ )
+ newDelay:blockedBy( oldDelay )
end
--
-- Puts an action on the delay stack.
--
- local function delay( self, etype, time, path, path2 )
-
+ local function delay
+ (
+ self, -- the sync
+ etype, -- the event type
+ time, -- time of the event
+ path, -- path of the event
+ path2 -- desitination path of move events
+ )
log(
'Function',
'delay( ',
@@ -1670,147 +2485,120 @@
' )'
)
- -- TODO
- local function recurse( )
-
- if etype == 'Create' and path:byte( -1 ) == 47 then
-
+ --
+ -- In case new directories were created
+ -- looks through this directories and makes create events for
+ -- new stuff found in there.
+ --
+ local function recurse
+ ( )
+ if etype == 'Create' and path:byte( -1 ) == 47
+ then
local entries = lsyncd.readdir( self.source .. path )
- if entries then
-
- for dirname, isdir in pairs(entries) do
-
+ if entries
+ then
+ for dirname, isdir in pairs( entries )
+ do
local pd = path .. dirname
- if isdir then
- pd = pd..'/'
- end
+ if isdir then pd = pd..'/' end
- log(
- 'Delay',
- 'Create creates Create on ',
- pd
- )
- delay( self, 'Create', time, pd, nil )
+ log( 'Delay', 'Create creates Create on ', pd )
+ delay( self, 'Create', time, pd, nil )
end
-
end
-
end
-
end
-- exclusion tests
- if not path2 then
+ if not path2
+ then
-- simple test for single path events
- if self.excludes:test(path) then
- log(
- 'Exclude',
- 'excluded ',
- etype,
- ' on "',
- path,
- '"'
- )
+ if testFilter( self, path )
+ then
+ log( 'Filter', 'filtered ', etype, ' on "', path, '"' )
+
return
end
else
- -- for double paths (move) it might result into a split
- local ex1 = self.excludes:test( path )
- local ex2 = self.excludes:test( path2 )
+ -- for double paths ( move ) it might result into a split
+ local ex1 = testFilter( self, path )
- if ex1 and ex2 then
+ local ex2 = testFilter( self, path2 )
+ if ex1 and ex2
+ then
log(
- 'Exclude',
- 'excluded "',
- etype,
- ' on "',
- path,
- '" -> "',
- path2,
- '"'
+ 'Filter',
+ 'filtered "', etype, ' on "', path,
+ '" -> "', path2, '"'
)
return
-
- elseif not ex1 and ex2 then
-
+ elseif not ex1 and ex2
+ then
-- splits the move if only partly excluded
log(
- 'Exclude',
- 'excluded destination transformed ',
+ 'Filter',
+ 'filtered destination transformed ',
etype,
' to Delete ',
path
)
- delay(
- self,
- 'Delete',
- time,
- path,
- nil
- )
+ delay( self, 'Delete', time, path, nil )
return
-
- elseif ex1 and not ex2 then
+ elseif ex1 and not ex2
+ then
-- splits the move if only partly excluded
log(
- 'Exclude',
- 'excluded origin transformed ',
+ 'Filter',
+ 'filtered origin transformed ',
etype,
' to Create.',
path2
)
- delay(
- self,
- 'Create',
- time,
- path2,
- nil
- )
+ delay( self, 'Create', time, path2, nil )
return
end
end
- if etype == 'Move' and not self.config.onMove then
-
+ if etype == 'Move'
+ and not self.config.onMove
+ then
-- if there is no move action defined,
-- split a move as delete/create
-- layer 1 scripts which want moves events have to
-- set onMove simply to 'true'
log( 'Delay', 'splitting Move into Delete & Create' )
+
delay( self, 'Delete', time, path, nil )
+
delay( self, 'Create', time, path2, nil )
- return
+ return
end
-- creates the new action
local alarm
- if time and self.config.delay then
+
+ if time and self.config.delay
+ then
alarm = time + self.config.delay
else
alarm = now( )
end
-- new delay
- local nd = Delay.new(
- etype,
- self,
- alarm,
- path,
- path2
- )
-
- if nd.etype == 'Init' or nd.etype == 'Blanket' then
+ local nd = Delay.new( etype, self, alarm, path, path2 )
+ if nd.etype == 'Init' or nd.etype == 'Blanket'
+ then
-- always stack init or blanket events on the last event
log(
'Delay',
@@ -1819,77 +2607,124 @@
' event.'
)
- if self.delays.size > 0 then
- stack( self.delays[ self.delays.last ], nd )
+ if self.delays:size( ) > 0
+ then
+ stack( self.delays:last( ), nd )
end
- nd.dpos = Queue.push( self.delays, nd )
+ nd.dpos = self.delays:push( nd )
+
recurse( )
return
-
end
-- detects blocks and combos by working from back until
-- front through the fifo
- for il, od in Queue.qpairsReverse( self.delays ) do
-
+ for il, od in self.delays:qpairsReverse( )
+ do
-- asks Combiner what to do
local ac = Combiner.combine( od, nd )
- if ac then
- if ac == 'remove' then
- Queue.remove( self.delays, il )
- elseif ac == 'stack' then
+ if ac
+ then
+ Combiner.log( ac, od, nd )
+
+ if ac == 'remove'
+ then
+ self.delays:remove( il )
+ elseif ac == 'stack'
+ then
stack( od, nd )
- nd.dpos = Queue.push( self.delays, nd )
- elseif ac == 'absorb' then
+
+ nd.dpos = self.delays:push( nd )
+ elseif ac == 'toDelete,stack'
+ then
+ if od.status ~= 'active'
+ then
+ -- turns olddelay into a delete
+ local rd = Delay.new( 'Delete', self, od.alarm, od.path )
+
+ self.delays:replace( il, rd )
+
+ rd.dpos = il
+
+ -- and stacks delay2
+ stack( rd, nd )
+ else
+ -- and stacks delay2
+ stack( od, nd )
+ end
+
+ nd.dpos = self.delays:push( nd )
+ elseif ac == 'absorb'
+ then
-- nada
- elseif ac == 'replace' then
- od.etype = nd.etype
- od.path = nd.path
- od.path2 = nd.path2
- elseif ac == 'split' then
+ elseif ac == 'replace'
+ then
+ if od.status ~= 'active'
+ then
+ self.delays:replace( il, nd )
+
+ nd.dpos = il
+ else
+ stack( od, nd )
+
+ nd.dpos = self.delays:push( nd )
+ end
+ elseif ac == 'split'
+ then
delay( self, 'Delete', time, path, nil )
+
delay( self, 'Create', time, path2, nil )
else
error( 'unknown result of combine()' )
end
+
recurse( )
+
return
end
il = il - 1
end
- if nd.path2 then
- log( 'Delay','New ',nd.etype,':',nd.path,'->',nd.path2 )
+ if nd.path2
+ then
+ log( 'Delay', 'New ', nd.etype, ': ', nd.path, ' -> ', nd.path2 )
else
- log( 'Delay','New ',nd.etype,':',nd.path )
+ log( 'Delay', 'New ', nd.etype, ': ', nd.path )
end
-- no block or combo
- nd.dpos = Queue.push( self.delays, nd )
+ nd.dpos = self.delays:push( nd )
+
recurse( )
end
--
-- Returns the soonest alarm for this Sync.
--
- local function getAlarm( self )
-
- if self.processes:size( ) >= self.config.maxProcesses then
+ local function getAlarm
+ (
+ self
+ )
+ if self.processes:size( ) >= self.config.maxProcesses
+ then
return false
end
-- first checks if more processes could be spawned
- if self.processes:size( ) < self.config.maxProcesses then
-
+ if self.processes:size( ) < self.config.maxProcesses
+ then
-- finds the nearest delay waiting to be spawned
- for _, d in Queue.qpairs( self.delays ) do
- if d.status == 'wait' then return d.alarm end
+ for _, d in self.delays:qpairs( )
+ do
+ if d.status == 'wait'
+ then
+ return d.alarm
+ end
end
-
end
-- nothing to spawn
@@ -1899,32 +2734,53 @@
--
-- Gets all delays that are not blocked by active delays.
--
- -- @param test function to test each delay
- --
- local function getDelays( self, test )
- local dlist = { sync = self}
+ local function getDelays
+ (
+ self, -- the sync
+ test -- function to test each delay
+ )
+ local dlist = { sync = self }
+
local dlistn = 1
+
local blocks = { }
--
-- inheritly transfers all blocks from delay
--
- local function getBlocks( delay )
+ local function getBlocks
+ (
+ delay
+ )
blocks[ delay ] = true
- if delay.blocks then
- for i, d in ipairs( delay.blocks ) do
+
+ if delay.blocks
+ then
+ for _, d in ipairs( delay.blocks )
+ do
getBlocks( d )
end
end
end
- for i, d in Queue.qpairs( self.delays ) do
- if d.status == 'active' or
- ( test and not test( InletFactory.d2e( d ) ) )
+ for _, d in self.delays:qpairs( )
+ do
+ local tr = true
+
+ if test
+ then
+ tr = test( InletFactory.d2e( d ) )
+ end
+
+ if tr == 'break' then break end
+
+ if d.status == 'active' or not tr
then
getBlocks( d )
- elseif not blocks[ d ] then
+ elseif not blocks[ d ]
+ then
dlist[ dlistn ] = d
+
dlistn = dlistn + 1
end
end
@@ -1935,8 +2791,11 @@
--
-- Creates new actions
--
- local function invokeActions( self, timestamp )
-
+ local function invokeActions
+ (
+ self,
+ timestamp
+ )
log(
'Function',
'invokeActions( "',
@@ -1945,40 +2804,46 @@
' )'
)
- if self.processes:size( ) >= self.config.maxProcesses then
+ if self.processes:size( ) >= self.config.maxProcesses
+ then
-- no new processes
return
end
- for _, d in Queue.qpairs( self.delays ) do
-
+ for _, d in self.delays:qpairs( )
+ do
-- if reached the global limit return
- if uSettings.maxProcesses and
- processCount >= uSettings.maxProcesses
+ if uSettings.maxProcesses
+ and processCount >= uSettings.maxProcesses
then
log('Alarm', 'at global process limit.')
+
return
end
- if self.delays.size < self.config.maxDelays then
+ if self.delays:size( ) < self.config.maxDelays
+ then
-- time constrains are only concerned if not maxed
-- the delay FIFO already.
- if d.alarm ~= true and timestamp < d.alarm then
+ if d.alarm ~= true and timestamp < d.alarm
+ then
-- reached point in stack where delays are in future
return
end
end
- if d.status == 'wait' then
-
+ if d.status == 'wait'
+ then
-- found a waiting delay
- if d.etype ~= 'Init' then
+ if d.etype ~= 'Init'
+ then
self.config.action( self.inlet )
else
self.config.init( InletFactory.d2e( d ) )
end
- if self.processes:size( ) >= self.config.maxProcesses then
+ if self.processes:size( ) >= self.config.maxProcesses
+ then
-- no further processes
return
end
@@ -1989,34 +2854,44 @@
--
-- Gets the next event to be processed.
--
- local function getNextDelay( self, timestamp )
-
- for i, d in Queue.qpairs( self.delays ) do
-
- if self.delays.size < self.config.maxDelays then
+ local function getNextDelay
+ (
+ self,
+ timestamp
+ )
+ for i, d in self.delays:qpairs( )
+ do
+ if self.delays:size( ) < self.config.maxDelays
+ then
-- time constrains are only concerned if not maxed
-- the delay FIFO already.
- if d.alarm ~= true and timestamp < d.alarm then
+ if d.alarm ~= true and timestamp < d.alarm
+ then
-- reached point in stack where delays are in future
return nil
end
end
- if d.status == 'wait' then
+ if d.status == 'wait'
+ then
-- found a waiting delay
return d
end
end
-
end
- ------
+ --
-- Adds and returns a blanket delay thats blocks all.
-- Used as custom marker.
--
- local function addBlanketDelay( self )
+ local function addBlanketDelay
+ (
+ self
+ )
local newd = Delay.new( 'Blanket', self, true, '' )
- newd.dpos = Queue.push( self.delays, newd )
+
+ newd.dpos = self.delays:push( newd )
+
return newd
end
@@ -2024,11 +2899,13 @@
-- Adds and returns a blanket delay thats blocks all.
-- Used as startup marker to call init asap.
--
- local function addInitDelay( self )
-
+ local function addInitDelay
+ (
+ self
+ )
local newd = Delay.new( 'Init', self, true, '' )
- newd.dpos = Queue.push( self.delays, newd )
+ newd.dpos = self.delays:push( newd )
return newd
end
@@ -2036,20 +2913,27 @@
--
-- Writes a status report about delays in this sync.
--
- local function statusReport( self, f )
-
+ local function statusReport
+ (
+ self,
+ f
+ )
local spaces = ' '
f:write( self.config.name, ' source=', self.source, '\n' )
- f:write( 'There are ', self.delays.size, ' delays\n')
- for i, vd in Queue.qpairs( self.delays ) do
+ f:write( 'There are ', self.delays:size( ), ' delays\n')
+
+ for i, vd in self.delays:qpairs( )
+ do
local st = vd.status
+
f:write( st, string.sub( spaces, 1, 7 - #st ) )
f:write( vd.etype, ' ' )
f:write( vd.path )
- if vd.path2 then
+ if vd.path2
+ then
f:write( ' -> ',vd.path2 )
end
@@ -2057,15 +2941,34 @@
end
- f:write( 'Excluding:\n' )
+ f:write( 'Filtering:\n' )
local nothing = true
- for t, p in pairs( self.excludes.list ) do
- nothing = false
- f:write( t,'\n' )
+ if self.filters
+ then
+ for _, e in pairs( self.filters.list )
+ do
+ nothing = false
+
+ f:write( e.rule, ' ', e.pattern,'\n' )
+ end
+ end
+
+ if #self.excludes.list > 0
+ then
+ f:write( 'From excludes:\n' )
+
+ for t, p in pairs( self.excludes.list )
+ do
+ nothing = false
+
+ f:write( '- ', t,'\n' )
+ end
end
- if nothing then
+
+ if nothing
+ then
f:write(' nothing.\n')
end
@@ -2073,24 +2976,27 @@
end
--
- -- Creates a new Sync
+ -- Creates a new Sync.
--
- local function new( config )
-
- local s = {
+ local function new
+ (
+ config
+ )
+ local s =
+ {
-- fields
-
config = config,
delays = Queue.new( ),
source = config.source,
processes = CountArray.new( ),
excludes = Excludes.new( ),
+ filters = nil,
-- functions
-
addBlanketDelay = addBlanketDelay,
addExclude = addExclude,
addInitDelay = addInitDelay,
+ appendFilter = appendFilter,
collect = collect,
concerns = concerns,
delay = delay,
@@ -2106,7 +3012,8 @@
s.inlet = InletFactory.newInlet( s )
-- provides a default name if needed
- if not config.name then
+ if not config.name
+ then
config.name = 'Sync' .. nextDefaultName
end
@@ -2114,14 +3021,35 @@
-- so Sync{n} will be the n-th call to sync{}
nextDefaultName = nextDefaultName + 1
- -- loads exclusions
- if config.exclude then
+ -- loads filters
+ if config.filter
+ then
+ local te = type( config.filter )
+
+ s.filters = Filters.new( )
+
+ if te == 'table'
+ then
+ s.filters:appendList( config.filter )
+ elseif te == 'string'
+ then
+ s.filters:append( config.filter )
+ else
+ error( 'type for filter must be table or string', 2 )
+ end
+
+ end
+ -- loads exclusions
+ if config.exclude
+ then
local te = type( config.exclude )
- if te == 'table' then
+ if te == 'table'
+ then
s.excludes:addList( config.exclude )
- elseif te == 'string' then
+ elseif te == 'string'
+ then
s.excludes:add( config.exclude )
else
error( 'type for exclude must be table or string', 2 )
@@ -2129,20 +3057,22 @@
end
- if
- config.delay ~= nil and
- (
- type(config.delay) ~= 'number' or
- config.delay < 0
- )
+ if config.delay ~= nil
+ and ( type( config.delay ) ~= 'number' or config.delay < 0 )
then
error( 'delay must be a number and >= 0', 2 )
end
- if config.excludeFrom then
+ if config.filterFrom
+ then
+ if not s.filters then s.filters = Filters.new( ) end
- s.excludes:loadFile( config.excludeFrom )
+ s.filters:loadFile( config.filterFrom )
+ end
+ if config.excludeFrom
+ then
+ s.excludes:loadFile( config.excludeFrom )
end
return s
@@ -2151,10 +3081,7 @@
--
-- Public interface
--
- return {
- new = new
- }
-
+ return { new = new }
end )( )
@@ -2163,8 +3090,8 @@
--
-- Syncs maintains all configured syncs.
--
-local Syncs = ( function( )
-
+local Syncs = ( function
+( )
--
-- the list of all syncs
--
@@ -2179,11 +3106,12 @@
--
-- The cycle( ) sheduler goes into the next round of roundrobin.
--
- local function nextRound( )
-
+ local function nextRound
+ ( )
round = round + 1;
- if round > #syncsList then
+ if round > #syncsList
+ then
round = 1
end
@@ -2193,14 +3121,16 @@
--
-- Returns the round
--
- local function getRound( )
+ local function getRound
+ ( )
return round
end
--
-- Returns sync at listpos i
--
- local function get( i )
+ local function get
+ ( i )
return syncsList[ i ];
end
@@ -2214,48 +3144,48 @@
-- Recurvely inherits a source table to a destionation table
-- copying all keys from source.
--
- -- table copy source ( cs )
- -- table copy destination ( cd )
- --
-- All entries with integer keys are inherited as additional
-- sources for non-verbatim tables
--
- local function inherit( cd, cs )
-
+ local function inherit
+ (
+ cd, -- table copy destination
+ cs, -- table copy source
+ verbatim -- forced verbatim ( for e.g. 'exitcodes' )
+ )
+ -- First copies all entries with non-integer keys.
--
- -- First copies all entries with non-integer keys
- -- tables are merged, already present keys are not
+ -- Tables are merged; already present keys are not
-- overwritten
--
-- For verbatim tables integer keys are treated like
- -- non integer keys
- --
- for k, v in pairs( cs ) do
+ -- non-integer keys
+ for k, v in pairs( cs )
+ do
if
(
- type( k ) ~= 'number' or
- cs._verbatim == true
+ type( k ) ~= 'number'
+ or verbatim
+ or cs._verbatim == true
)
and
(
- type( cs._merge ) ~= 'table' or
- cs._merge[ k ] == true
+ type( cs._merge ) ~= 'table'
+ or cs._merge[ k ] == true
)
then
inheritKV( cd, k, v )
end
end
- --
-- recursevely inherits all integer keyed tables
-- ( for non-verbatim tables )
- --
- if cs._verbatim ~= true then
-
- local n = nil
- for k, v in ipairs( cs ) do
- n = k
- if type( v ) == 'table' then
+ if cs._verbatim ~= true
+ then
+ for k, v in ipairs( cs )
+ do
+ if type( v ) == 'table'
+ then
inherit( cd, v )
else
cd[ #cd + 1 ] = v
@@ -2268,53 +3198,55 @@
--
-- Helper to inherit. Inherits one key.
--
- inheritKV = function( cd, k, v )
+ inheritKV =
+ function(
+ cd, -- table copy destination
+ k, -- key
+ v -- value
+ )
-- don't merge inheritance controls
- if k == '_merge' or k == '_verbatim' then
- return
- end
+ if k == '_merge' or k == '_verbatim' then return end
local dtype = type( cd [ k ] )
- if type( v ) == 'table' then
-
- if dtype == 'nil' then
+ if type( v ) == 'table'
+ then
+ if dtype == 'nil'
+ then
cd[ k ] = { }
- inherit( cd[ k ], v )
+ inherit( cd[ k ], v, k == 'exitcodes' )
elseif
dtype == 'table' and
v._merge ~= false
then
- inherit( cd[ k ], v )
+ inherit( cd[ k ], v, k == 'exitcodes' )
end
-
- elseif dtype == 'nil' then
+ elseif dtype == 'nil'
+ then
cd[ k ] = v
end
-
end
--
- -- Adds a new sync (directory-tree to observe).
+ -- Adds a new sync.
--
- local function add( config )
-
- -- workaround for backwards compatibility
- -- FIXME: remove when dropping that
- if settings ~= settingsSafe then
+ local function add
+ (
+ config
+ )
+ -- Checks if user overwrote the settings function.
+ -- ( was Lsyncd <2.1 style )
+ if settings ~= settingsSafe
+ then
log(
- 'Warn',
- 'settings = { ... } is deprecated.\n'..
+ 'Error',
+ 'Do not use settings = { ... }\n'..
' please use settings{ ... } (without the equal sign)'
)
- for k, v in pairs( settings ) do
- uSettings[ k ] = v
- end
-
- settings = settingsSafe
+ os.exit( -1 )
end
-- Creates a new config table which inherits all keys/values
@@ -2337,15 +3269,19 @@
}
-- Lets settings override these values.
- for _, v in ipairs( inheritSettings ) do
- if uSettings[ v ] then
+ for _, v in ipairs( inheritSettings )
+ do
+ if uSettings[ v ]
+ then
config[ v ] = uSettings[ v ]
end
end
-- Lets commandline override these values.
- for _, v in ipairs( inheritSettings ) do
- if clSettings[ v ] then
+ for _, v in ipairs( inheritSettings )
+ do
+ if clSettings[ v ]
+ then
config[ v ] = clSettings[ v ]
end
end
@@ -2354,20 +3290,22 @@
-- lets the userscript 'prepare' function
-- check and complete the config
--
- if type( config.prepare ) == 'function' then
-
+ if type( config.prepare ) == 'function'
+ then
-- prepare is given a writeable copy of config
config.prepare( config, 4 )
-
end
- if not config[ 'source' ] then
+ if not config[ 'source' ]
+ then
local info = debug.getinfo( 3, 'Sl' )
+
log(
'Error',
info.short_src,':',
info.currentline,': source missing from sync.'
)
+
terminate( -1 )
end
@@ -2376,27 +3314,29 @@
--
local realsrc = lsyncd.realdir( config.source )
- if not realsrc then
+ if not realsrc
+ then
log(
'Error',
'Cannot access source directory: ',
config.source
)
+
terminate( -1 )
end
config._source = config.source
config.source = realsrc
- if
- not config.action and
- not config.onAttrib and
- not config.onCreate and
- not config.onModify and
- not config.onDelete and
- not config.onMove
+ if not config.action
+ and not config.onAttrib
+ and not config.onCreate
+ and not config.onModify
+ and not config.onDelete
+ and not config.onMove
then
local info = debug.getinfo( 3, 'Sl' )
+
log(
'Error',
info.short_src, ':',
@@ -2413,9 +3353,8 @@
config.monitor or
Monitors.default( )
- if
- config.monitor ~= 'inotify' and
- config.monitor ~= 'fsevents'
+ if config.monitor ~= 'inotify'
+ and config.monitor ~= 'fsevents'
then
local info = debug.getinfo( 3, 'Sl' )
@@ -2431,7 +3370,7 @@
terminate( -1 )
end
- --- creates the new sync
+ -- creates the new sync
local s = Sync.new( config )
table.insert( syncsList, s )
@@ -2442,23 +3381,30 @@
--
-- Allows a for-loop to walk through all syncs.
--
- local function iwalk( )
+ local function iwalk
+ ( )
return ipairs( syncsList )
end
--
-- Returns the number of syncs.
--
- local size = function( )
+ local size = function
+ ( )
return #syncsList
end
--
-- Tests if any sync is interested in a path.
--
- local function concerns( path )
- for _, s in ipairs( syncsList ) do
- if s:concerns( path ) then
+ local function concerns
+ (
+ path
+ )
+ for _, s in ipairs( syncsList )
+ do
+ if s:concerns( path )
+ then
return true
end
end
@@ -2486,12 +3432,17 @@
-- Returns the relative part of absolute path if it
-- begins with root
--
-local function splitPath( path, root )
-
+local function splitPath
+(
+ path,
+ root
+)
local rlen = #root
+
local sp = string.sub( path, 1, rlen )
- if sp == root then
+ if sp == root
+ then
return string.sub( path, rlen, -1 )
else
return nil
@@ -2505,8 +3456,8 @@
--
-- All inotify specific implementation is enclosed here.
--
-local Inotify = ( function( )
-
+local Inotify = ( function
+( )
--
-- A list indexed by inotify watch descriptors yielding
-- the directories absolute paths.
@@ -2528,19 +3479,21 @@
--
-- Stops watching a directory
--
- -- path ... absolute path to unwatch
- -- core ... if false not actually send the unwatch to the kernel
- -- (used in moves which reuse the watch)
- --
- local function removeWatch( path, core )
-
+ local function removeWatch
+ (
+ path, -- absolute path to unwatch
+ core -- if false not actually send the unwatch to the kernel
+ -- ( used in moves which reuse the watch )
+ )
local wd = pathwds[ path ]
- if not wd then
+ if not wd
+ then
return
end
- if core then
+ if core
+ then
lsyncd.inotify.rmwatch( wd )
end
@@ -2552,54 +3505,59 @@
--
-- Adds watches for a directory (optionally) including all subdirectories.
--
- -- @param path absolute path of directory to observe
- -- @param recurse true if recursing into subdirs
--
- local function addWatch(path)
+ local function addWatch
+ (
+ path -- absolute path of directory to observe
+ )
+ log( 'Function', 'Inotify.addWatch( ', path, ' )' )
- log(
- 'Function',
- 'Inotify.addWatch( ',
- path,
- ' )'
- )
+ if not Syncs.concerns( path )
+ then
+ log('Inotify', 'not concerning "', path, '"')
- if not Syncs.concerns(path) then
- log('Inotify', 'not concerning "',path,'"')
return
end
-- registers the watch
local inotifyMode = ( uSettings and uSettings.inotifyMode ) or '';
- local wd = lsyncd.inotify.addwatch( path, inotifyMode) ;
+ local wd = lsyncd.inotify.addwatch( path, inotifyMode ) ;
- if wd < 0 then
+ if wd < 0
+ then
log( 'Inotify','Unable to add watch "', path, '"' )
+
return
end
do
-- If this watch descriptor is registered already
- -- the kernel reuses it since old dir is gone.
+ -- the kernel reuses it since the old dir is gone.
local op = wdpaths[ wd ]
- if op and op ~= path then
+
+ if op and op ~= path
+ then
pathwds[ op ] = nil
end
end
pathwds[ path ] = wd
+
wdpaths[ wd ] = path
-- registers and adds watches for all subdirectories
local entries = lsyncd.readdir( path )
- if not entries then
+ if not entries
+ then
return
end
- for dirname, isdir in pairs( entries ) do
- if isdir then
+ for dirname, isdir in pairs( entries )
+ do
+ if isdir
+ then
addWatch( path .. dirname .. '/' )
end
end
@@ -2608,21 +3566,26 @@
--
-- Adds a Sync to receive events.
--
- -- sync: Object to receive events
- -- rootdir: root dir to watch
- --
- local function addSync( sync, rootdir )
- if syncRoots[ sync ] then
+ local function addSync
+ (
+ sync, -- object to receive events.
+ rootdir -- root dir to watch
+ )
+ if syncRoots[ sync ]
+ then
error( 'duplicate sync in Inotify.addSync()' )
end
+
syncRoots[ sync ] = rootdir
+
addWatch( rootdir )
end
--
-- Called when an event has occured.
--
- local function event(
+ local function event
+ (
etype, -- 'Attrib', 'Modify', 'Create', 'Delete', 'Move'
wd, -- watch descriptor, matches lsyncd.inotifyadd()
isdir, -- true if filename is a directory
@@ -2631,15 +3594,18 @@
wd2, -- watch descriptor for target if it's a Move
filename2 -- string filename without path of Move target
)
- if isdir then
+ if isdir
+ then
filename = filename .. '/'
- if filename2 then
+ if filename2
+ then
filename2 = filename2 .. '/'
end
end
- if filename2 then
+ if filename2
+ then
log(
'Inotify',
'got event ',
@@ -2663,47 +3629,59 @@
-- looks up the watch descriptor id
local path = wdpaths[ wd ]
- if path then
+
+ if path
+ then
path = path..filename
end
local path2 = wd2 and wdpaths[ wd2 ]
- if path2 and filename2 then
+ if path2 and filename2
+ then
path2 = path2..filename2
end
- if not path and path2 and etype == 'Move' then
+ if not path and path2 and etype == 'Move'
+ then
log(
'Inotify',
'Move from deleted directory ',
path2,
' becomes Create.'
)
+
path = path2
+
path2 = nil
+
etype = 'Create'
end
- if not path then
+ if not path
+ then
-- this is normal in case of deleted subdirs
log(
'Inotify',
'event belongs to unknown watch descriptor.'
)
+
return
end
- for sync, root in pairs( syncRoots ) do repeat
-
+ for sync, root in pairs( syncRoots )
+ do repeat
local relative = splitPath( path, root )
+
local relative2 = nil
- if path2 then
+ if path2
+ then
relative2 = splitPath( path2, root )
end
- if not relative and not relative2 then
+ if not relative and not relative2
+ then
-- sync is not interested in this dir
break -- continue
end
@@ -2711,32 +3689,43 @@
-- makes a copy of etype to possibly change it
local etyped = etype
- if etyped == 'Move' then
- if not relative2 then
+ if etyped == 'Move'
+ then
+ if not relative2
+ then
log(
'Normal',
'Transformed Move to Delete for ',
sync.config.name
)
+
etyped = 'Delete'
- elseif not relative then
+ elseif not relative
+ then
relative = relative2
+
relative2 = nil
+
log(
'Normal',
'Transformed Move to Create for ',
sync.config.name
)
+
etyped = 'Create'
end
end
- if isdir then
- if etyped == 'Create' then
+ if isdir
+ then
+ if etyped == 'Create'
+ then
addWatch( path )
- elseif etyped == 'Delete' then
+ elseif etyped == 'Delete'
+ then
removeWatch( path, true )
- elseif etyped == 'Move' then
+ elseif etyped == 'Move'
+ then
removeWatch( path, false )
addWatch( path2 )
end
@@ -2754,7 +3743,8 @@
f:write( 'Inotify watching ', wdpaths:size(), ' directories\n' )
- for wd, path in wdpaths:walk( ) do
+ for wd, path in wdpaths:walk( )
+ do
f:write( ' ', wd, ': ', path, '\n' )
end
end
@@ -2771,6 +3761,7 @@
end)( )
+
--
-- Interface to OSX /dev/fsevents
--
@@ -2779,25 +3770,24 @@
--
-- All fsevents specific implementation are enclosed here.
--
-local Fsevents = ( function( )
-
-
+local Fsevents = ( function
+( )
--
-- A list indexed by syncs yielding
-- the root path the sync is interested in.
--
local syncRoots = { }
-
--
-- Adds a Sync to receive events.
--
- -- @param sync Object to receive events
- -- @param dir dir to watch
- --
- local function addSync( sync, dir )
-
- if syncRoots[ sync ] then
+ local function addSync
+ (
+ sync, -- object to receive events
+ dir -- dir to watch
+ )
+ if syncRoots[ sync ]
+ then
error( 'duplicate sync in Fanotify.addSync()' )
end
@@ -2808,58 +3798,65 @@
--
-- Called when an event has occured.
--
- local function event(
+ local function event
+ (
etype, -- 'Attrib', 'Modify', 'Create', 'Delete', 'Move'
isdir, -- true if filename is a directory
time, -- time of event
path, -- path of file
path2 -- path of target in case of 'Move'
)
- if isdir then
+ if isdir
+ then
path = path .. '/'
- if path2 then
- path2 = path2 .. '/'
- end
+ if path2 then path2 = path2 .. '/' end
end
- log(
- 'Fsevents',
- etype, ',',
- isdir, ',',
- time, ',',
- path, ',',
- path2
- )
+ log( 'Fsevents', etype, ',', isdir, ',', time, ',', path, ',', path2 )
- for _, sync in Syncs.iwalk() do repeat
+ for _, sync in Syncs.iwalk()
+ do repeat
local root = sync.source
-- TODO combine ifs
- if not path:starts( root ) then
- if not path2 or not path2:starts( root ) then
+ if not path:starts( root )
+ then
+ if not path2 or not path2:starts( root )
+ then
break -- continue
end
end
- local relative = splitPath( path, root )
+ local relative = splitPath( path, root )
local relative2
- if path2 then
+
+ if path2
+ then
relative2 = splitPath( path2, root )
end
-- possibly change etype for this iteration only
local etyped = etype
- if etyped == 'Move' then
- if not relative2 then
- log('Normal', 'Transformed Move to Delete for ', sync.config.name)
+
+ if etyped == 'Move'
+ then
+ if not relative2
+ then
+ log( 'Normal', 'Transformed Move to Delete for ', sync.config.name )
+
etyped = 'Delete'
- elseif not relative then
+
+ elseif not relative
+ then
relative = relative2
+
relative2 = nil
- log('Normal', 'Transformed Move to Create for ', sync.config.name)
+
+ log( 'Normal', 'Transformed Move to Create for ', sync.config.name )
+
etyped = 'Create'
end
end
@@ -2874,7 +3871,10 @@
--
-- Writes a status report about fsevents to a filedescriptor.
--
- local function statusReport( f )
+ local function statusReport
+ (
+ f
+ )
-- TODO
end
@@ -2893,9 +3893,8 @@
-- Holds information about the event monitor capabilities
-- of the core.
--
-Monitors = ( function( )
-
-
+Monitors = ( function
+( )
--
-- The cores monitor list
--
@@ -2905,7 +3904,8 @@
--
-- The default event monitor.
--
- local function default( )
+ local function default
+ ( )
return list[ 1 ]
end
@@ -2914,7 +3914,8 @@
-- Initializes with info received from core
--
local function initialize( clist )
- for k, v in ipairs( clist ) do
+ for k, v in ipairs( clist )
+ do
list[ k ] = v
end
end
@@ -2978,8 +3979,8 @@
)
local args = { }
- while str ~= '' do
-
+ while str ~= ''
+ do
-- break where argument stops
local bp = #str
@@ -2987,13 +3988,17 @@
local inQuote = false
-- tests characters to be space and not within quotes
- for i=1, #str do
+ for i = 1, #str
+ do
local c = string.sub( str, i, i )
- if c == '"' then
+ if c == '"'
+ then
inQuote = not inQuote
- elseif c == ' ' and not inQuote then
+ elseif c == ' ' and not inQuote
+ then
bp = i - 1
+
break
end
end
@@ -3014,41 +4019,49 @@
-- Translates a call to a binary to a lua function.
-- TODO this has a little too blocking.
--
- local function translateBinary( str )
-
+ local function translateBinary
+ (
+ str
+ )
-- splits the string
local args = splitStr( str )
-- true if there is a second event
local haveEvent2 = false
- for ia, iv in ipairs( args ) do
-
+ for ia, iv in ipairs( args )
+ do
-- a list of arguments this arg is being split into
local a = { { true, iv } }
-- goes through all translates
- for _, v in ipairs( transVars ) do
+ for _, v in ipairs( transVars )
+ do
local ai = 1
- while ai <= #a do
- if a[ ai ][ 1 ] then
+ while ai <= #a
+ do
+ if a[ ai ][ 1 ]
+ then
local pre, post =
string.match( a[ ai ][ 2 ], '(.*)'..v[1]..'(.*)' )
- if pre then
-
- if v[3] > 1 then
+ if pre
+ then
+ if v[3] > 1
+ then
haveEvent2 = true
end
- if pre ~= '' then
+ if pre ~= ''
+ then
table.insert( a, ai, { true, pre } )
ai = ai + 1
end
a[ ai ] = { false, v[ 2 ] }
- if post ~= '' then
+ if post ~= ''
+ then
table.insert( a, ai + 1, { true, post } )
end
end
@@ -3061,13 +4074,12 @@
local as = ''
local first = true
- for _, v in ipairs( a ) do
-
- if not first then
- as = as..' .. '
- end
+ for _, v in ipairs( a )
+ do
+ if not first then as = as..' .. ' end
- if v[ 1 ] then
+ if v[ 1 ]
+ then
as = as .. '"' .. v[ 2 ] .. '"'
else
as = as .. v[ 2 ]
@@ -3080,18 +4092,21 @@
end
local ft
- if not haveEvent2 then
- ft = 'function(event)\n'
+
+ if not haveEvent2
+ then
+ ft = 'function( event )\n'
else
- ft = 'function(event, event2)\n'
+ ft = 'function( event, event2 )\n'
end
ft = ft ..
" log('Normal', 'Event ', event.etype, \n" ..
" ' spawns action \"".. str.."\"')\n" ..
- " spawn(event"
+ " spawn( event"
- for _, v in ipairs( args ) do
+ for _, v in ipairs( args )
+ do
ft = ft .. ',\n ' .. v
end
@@ -3104,40 +4119,45 @@
--
-- Translates a call using a shell to a lua function
--
- local function translateShell( str )
-
+ local function translateShell
+ (
+ str
+ )
local argn = 1
+
local args = { }
+
local cmd = str
+
local lc = str
-- true if there is a second event
local haveEvent2 = false
- for _, v in ipairs( transVars ) do
-
+ for _, v in ipairs( transVars )
+ do
local occur = false
cmd = string.gsub(
cmd,
v[ 1 ],
- function( )
+ function
+ ( )
occur = true
return '"$' .. argn .. '"'
end
)
- lc = string.gsub(
- lc,
- v[1],
- ']]..' .. v[2] .. '..[['
- )
+ lc = string.gsub( lc, v[1], ']]..' .. v[2] .. '..[[' )
- if occur then
+ if occur
+ then
argn = argn + 1
+
table.insert( args, v[ 2 ] )
- if v[ 3 ] > 1 then
+ if v[ 3 ] > 1
+ then
haveEvent2 = true
end
end
@@ -3145,10 +4165,12 @@
end
local ft
- if not haveEvent2 then
- ft = 'function(event)\n'
+
+ if not haveEvent2
+ then
+ ft = 'function( event )\n'
else
- ft = 'function(event, event2)\n'
+ ft = 'function( event, event2 )\n'
end
-- TODO do array joining instead
@@ -3157,7 +4179,8 @@
" [[ spawns shell \""..lc.."\"]])\n"..
" spawnShell(event, [["..cmd.."]]"
- for _, v in ipairs( args ) do
+ for _, v in ipairs( args )
+ do
ft = ft..',\n '..v
end
@@ -3170,28 +4193,28 @@
--
-- Writes a lua function for a layer 3 user script.
--
- local function translate( str )
- -- trim spaces
+ local function translate
+ (
+ str
+ )
+ -- trims spaces
str = string.match( str, '^%s*(.-)%s*$' )
local ft
- if string.byte( str, 1, 1 ) == 47 then
+
+ if string.byte( str, 1, 1 ) == 47
+ then
-- starts with /
ft = translateBinary( str )
- elseif string.byte( str, 1, 1 ) == 94 then
+ elseif string.byte( str, 1, 1 ) == 94
+ then
-- starts with ^
ft = translateShell( str:sub( 2, -1 ) )
else
ft = translateShell( str )
end
- log(
- 'FWrite',
- 'translated "',
- str,
- '" to \n',
- ft
- )
+ log( 'FWrite', 'translated "', str, '" to \n', ft )
return ft
end
@@ -3202,17 +4225,15 @@
--
return { translate = translate }
-
end )( )
--
--- Writes a status report file at most every [statusintervall] seconds.
+-- Writes a status report file at most every 'statusintervall' seconds.
--
-local StatusFile = ( function( )
-
-
+local StatusFile = ( function
+( )
--
-- Timestamp when the status file has been written.
--
@@ -3228,7 +4249,8 @@
--
-- Returns the alarm when the status file should be written-
--
- local function getAlarm()
+ local function getAlarm
+ ( )
return alarm
end
@@ -3236,46 +4258,33 @@
--
-- Called to check if to write a status file.
--
- local function write( timestamp )
-
- log(
- 'Function',
- 'write( ',
- timestamp,
- ' )'
- )
+ local function write
+ (
+ timestamp
+ )
+ log( 'Function', 'write( ', timestamp, ' )' )
--
-- takes care not write too often
--
- if uSettings.statusInterval > 0 then
-
+ if uSettings.statusInterval > 0
+ then
-- already waiting?
- if alarm and timestamp < alarm then
- log(
- 'Statusfile',
- 'waiting(',
- timestamp,
- ' < ',
- alarm,
- ')'
- )
+ if alarm and timestamp < alarm
+ then
+ log( 'Statusfile', 'waiting(', timestamp, ' < ', alarm, ')' )
+
return
end
-- determines when a next write will be possible
- if not alarm then
-
- local nextWrite =
- lastWritten and timestamp +
- uSettings.statusInterval
+ if not alarm
+ then
+ local nextWrite = lastWritten and timestamp + uSettings.statusInterval
- if nextWrite and timestamp < nextWrite then
- log(
- 'Statusfile',
- 'setting alarm: ',
- nextWrite
- )
+ if nextWrite and timestamp < nextWrite
+ then
+ log( 'Statusfile', 'setting alarm: ', nextWrite )
alarm = nextWrite
return
@@ -3290,7 +4299,8 @@
local f, err = io.open( uSettings.statusFile, 'w' )
- if not f then
+ if not f
+ then
log(
'Error',
'Cannot open status file "' ..
@@ -3303,12 +4313,15 @@
f:write( 'Lsyncd status report at ', os.date( ), '\n\n' )
- for i, s in Syncs.iwalk( ) do
+ for i, s in Syncs.iwalk( )
+ do
s:statusReport( f )
+
f:write( '\n' )
end
Inotify.statusReport( f )
+
f:close( )
end
@@ -3327,60 +4340,70 @@
--
-- Lets userscripts make their own alarms.
--
-local UserAlarms = ( function( )
-
+local UserAlarms = ( function
+( )
local alarms = { }
-
--
-- Calls the user function at timestamp.
--
- local function alarm( timestamp, func, extra )
-
+ local function alarm
+ (
+ timestamp,
+ func,
+ extra
+ )
local idx
- for k, v in ipairs( alarms ) do
- if timestamp < v.timestamp then
+
+ for k, v in ipairs( alarms )
+ do
+ if timestamp < v.timestamp
+ then
idx = k
+
break
end
end
- local a = {
+ local a =
+ {
timestamp = timestamp,
func = func,
extra = extra
}
- if idx then
+ if idx
+ then
table.insert( alarms, idx, a )
else
table.insert( alarms, a )
end
-
end
--
-- Retrieves the soonest alarm.
--
- local function getAlarm( )
-
- if #alarms == 0 then
+ local function getAlarm
+ ( )
+ if #alarms == 0
+ then
return false
else
return alarms[1].timestamp
end
-
end
--
-- Calls user alarms.
--
- local function invoke( timestamp )
- while
- #alarms > 0 and
- alarms[ 1 ].timestamp <= timestamp
+ local function invoke
+ (
+ timestamp
+ )
+ while #alarms > 0
+ and alarms[ 1 ].timestamp <= timestamp
do
alarms[ 1 ].func( alarms[ 1 ].timestamp, alarms[ 1 ].extra )
table.remove( alarms, 1 )
@@ -3397,7 +4420,6 @@
invoke = invoke
}
-
end )( )
--============================================================================
@@ -3428,16 +4450,21 @@
--
-- Logs a backtrace
--
-function runner.callError( message )
- log('Error', 'in Lua: ', message )
+function runner.callError
+(
+ message
+)
+ log( 'Error', 'in Lua: ', message )
-- prints backtrace
local level = 2
- while true do
+ while true
+ do
local info = debug.getinfo( level, 'Sl' )
- if not info then
+ if not info
+ then
terminate( -1 )
end
@@ -3458,18 +4485,22 @@
-- Called from core whenever a child process has finished and
-- the zombie process was collected by core.
--
-function runner.collectProcess( pid, exitcode )
-
+function runner.collectProcess
+(
+ pid, -- process id
+ exitcode -- exitcode
+)
processCount = processCount - 1
- if processCount < 0 then
+ if processCount < 0
+ then
error( 'negative number of processes!' )
end
- for _, s in Syncs.iwalk() do
- if s:collect(pid, exitcode) then return end
+ for _, s in Syncs.iwalk( )
+ do
+ if s:collect( pid, exitcode ) then return end
end
-
end
--
@@ -3484,11 +4515,12 @@
function runner.cycle(
timestamp -- the current kernel time (in jiffies)
)
+ log( 'Function', 'cycle( ', timestamp, ' )' )
- if lsyncdStatus == 'fade' then
-
- if processCount > 0 then
-
+ if lsyncdStatus == 'fade'
+ then
+ if processCount > 0
+ then
if
lastReportedWaiting == false or
timestamp >= lastReportedWaiting + 60
@@ -3505,12 +4537,12 @@
return true
else
-
return false
end
end
- if lsyncdStatus ~= 'run' then
+ if lsyncdStatus ~= 'run'
+ then
error( 'runner.cycle() called while not running!' )
end
@@ -3519,22 +4551,22 @@
-- if possibly. But only let Syncs invoke actions if
-- not at global limit
--
- if
- not uSettings.maxProcesses or
- processCount < uSettings.maxProcesses
+ if not uSettings.maxProcesses
+ or processCount < uSettings.maxProcesses
then
local start = Syncs.getRound( )
local ir = start
repeat
-
local s = Syncs.get( ir )
+
s:invokeActions( timestamp )
- ir = ir + 1
- if ir > Syncs.size( ) then
+ ir = ir + 1
+ if ir > Syncs.size( )
+ then
ir = 1
end
until ir == start
@@ -3544,7 +4576,8 @@
UserAlarms.invoke( timestamp )
- if uSettings.statusFile then
+ if uSettings.statusFile
+ then
StatusFile.write( timestamp )
end
@@ -3622,119 +4655,143 @@
--
-- second paramter is the function to call
--
- local options = {
-
+ local options =
+ {
-- log is handled by core already.
delay =
- {
- 1,
- function( secs )
- clSettings.delay = secs + 0
- end
- },
+ {
+ 1,
+ function
+ (
+ secs
+ )
+ clSettings.delay = secs + 0
+ end
+ },
insist =
- {
- 0,
- function( )
- clSettings.insist = true
- end
- },
+ {
+ 0,
+ function
+ ( )
+ clSettings.insist = true
+ end
+ },
log =
- {
- 1,
- nil
- },
+ {
+ 1,
+ nil
+ },
logfile =
- {
- 1,
- function( file )
- clSettings.logfile = file
- end
- },
+ {
+ 1,
+ function
+ (
+ file
+ )
+ clSettings.logfile = file
+ end
+ },
monitor =
- {
- -1,
- function( monitor )
- if not monitor then
- io.stdout:write( 'This Lsyncd supports these monitors:\n' )
- for _, v in ipairs(Monitors.list) do
- io.stdout:write(' ',v,'\n')
- end
+ {
+ -1,
+ function
+ (
+ monitor
+ )
+ if not monitor
+ then
+ io.stdout:write( 'This Lsyncd supports these monitors:\n' )
+ for _, v in ipairs( Monitors.list )
+ do
+ io.stdout:write( ' ', v, '\n' )
+ end
- io.stdout:write('\n')
+ io.stdout:write('\n')
- lsyncd.terminate(-1)
- else
- clSettings.monitor = monitor
- end
+ lsyncd.terminate( -1 )
+ else
+ clSettings.monitor = monitor
end
- },
+ end
+ },
nodaemon =
- {
- 0,
- function( )
- clSettings.nodaemon = true
- end
- },
+ {
+ 0,
+ function
+ ( )
+ clSettings.nodaemon = true
+ end
+ },
pidfile =
- {
- 1,
- function( file )
- clSettings.pidfile=file
- end
- },
+ {
+ 1,
+ function
+ (
+ file
+ )
+ clSettings.pidfile=file
+ end
+ },
- rsync =
- {
- 2,
- function( src, trg )
- clSettings.syncs = clSettings.syncs or { }
- table.insert(
- clSettings.syncs,
- { 'rsync', src, trg }
- )
- end
- },
+ rsync =
+ {
+ 2,
+ function
+ (
+ src,
+ trg
+ )
+ clSettings.syncs = clSettings.syncs or { }
+ table.insert( clSettings.syncs, { 'rsync', src, trg } )
+ end
+ },
rsyncssh =
- {
- 3,
- function( src, host, tdir )
- clSettings.syncs = clSettings.syncs or { }
- table.insert(
- clSettings.syncs,
- { 'rsyncssh', src, host, tdir }
- )
- end
- },
+ {
+ 3,
+ function
+ (
+ src,
+ host,
+ tdir
+ )
+ clSettings.syncs = clSettings.syncs or { }
+
+ table.insert( clSettings.syncs, { 'rsyncssh', src, host, tdir } )
+ end
+ },
direct =
- {
- 2,
- function( src, trg )
- clSettings.syncs = clSettings.syncs or { }
- table.insert(
- clSettings.syncs,
- { 'direct', src, trg }
- )
- end
- },
+ {
+ 2,
+ function
+ (
+ src,
+ trg
+ )
+ clSettings.syncs = clSettings.syncs or { }
+
+ table.insert( clSettings.syncs, { 'direct', src, trg } )
+ end
+ },
version =
- {
- 0,
- function( )
- io.stdout:write( 'Version: ', lsyncd_version, '\n' )
- os.exit( 0 )
- end
- }
+ {
+ 0,
+ function
+ ( )
+ io.stdout:write( 'Version: ', lsyncd_version, '\n' )
+
+ os.exit( 0 )
+ end
+ }
}
-- non-opts is filled with all args that were no part dash options
@@ -3742,14 +4799,17 @@
local nonopts = { }
local i = 1
- while i <= #args do
+ while i <= #args
+ do
local a = args[ i ]
- if a:sub( 1, 1 ) ~= '-' then
+ if a:sub( 1, 1 ) ~= '-'
+ then
table.insert( nonopts, args[ i ] )
else
- if a:sub( 1, 2 ) == '--' then
+ if a:sub( 1, 2 ) == '--'
+ then
a = a:sub( 3 )
else
a = a:sub( 2 )
@@ -3757,70 +4817,67 @@
local o = options[ a ]
- if not o then
- log(
- 'Error',
- 'unknown option command line option ',
- args[i]
- )
+ if not o
+ then
+ log( 'Error', 'unknown option command line option ', args[ i ] )
+
os.exit( -1 )
end
- if o[ 1 ] >= 0 and i + o[ 1 ] > #args then
+ if o[ 1 ] >= 0 and i + o[ 1 ] > #args
+ then
log( 'Error', a ,' needs ', o[ 1 ],' arguments' )
+
os.exit( -1 )
- elseif o[1] < 0 then
+ elseif o[1] < 0
+ then
o[ 1 ] = -o[ 1 ]
end
- if o[ 2 ] then
- if o[ 1 ] == 0 then
+ if o[ 2 ]
+ then
+ if o[ 1 ] == 0
+ then
o[ 2 ]( )
- elseif o[ 1 ] == 1 then
- o[ 2 ]( args[i + 1] )
- elseif o[ 1 ] == 2 then
- o[ 2 ]( args[i + 1], args[i + 2] )
- elseif o[ 1 ] == 3 then
- o[ 2 ]( args[i + 1], args[i + 2], args[i + 3] )
+ elseif o[ 1 ] == 1
+ then
+ o[ 2 ]( args[ i + 1] )
+ elseif o[ 1 ] == 2
+ then
+ o[ 2 ]( args[ i + 1], args[ i + 2] )
+ elseif o[ 1 ] == 3
+ then
+ o[ 2 ]( args[ i + 1], args[ i + 2], args[ i + 3] )
end
end
+
i = i + o[1]
end
- i = i + 1
+ i = i + 1
end
- if clSettings.syncs then
+ if clSettings.syncs
+ then
+ if #nonopts ~= 0
+ then
+ log( 'Error', 'There cannot be command line syncs and a config file together.' )
- if #nonopts ~= 0 then
- log(
- 'Error',
- 'There cannot be command line syncs and config file together.'
- )
os.exit( -1 )
end
-
else
-
- if #nonopts == 0 then
-
+ if #nonopts == 0
+ then
runner.help( args[ 0 ] )
-
- elseif #nonopts == 1 then
-
+ elseif #nonopts == 1
+ then
return nonopts[ 1 ]
-
else
-
-- TODO make this possible
- log(
- 'Error',
- 'There can only be one config file in command line.'
- )
- os.exit( -1 )
+ log( 'Error', 'There can only be one config file in the command line.' )
+ os.exit( -1 )
end
-
end
end
@@ -3834,17 +4891,17 @@
--
function runner.initialize( firstTime )
- if settings ~= settingsSafe then
+ -- Checks if user overwrote the settings function.
+ -- ( was Lsyncd <2.1 style )
+ if settings ~= settingsSafe
+ then
log(
- 'Warn',
- 'settings = { ... } is deprecated.\n'..
- ' please use settings{ ... } (without the equal sign)'
+ 'Error',
+ 'Do not use settings = { ... }\n'..
+ ' please use settings{ ... } ( without the equal sign )'
)
- for k, v in pairs( settings ) do
- uSettings[ k ] = v
- end
-
+ os.exit( -1 )
end
lastReportedWaiting = false
@@ -3855,30 +4912,12 @@
lockGlobals( )
--
- -- copies simple settings with numeric keys to 'key = true' settings.
- --
- -- FIXME this can be removed when
- -- Lsyncd 2.0.x backwards compatibility is dropped
- --
- for k, v in ipairs( uSettings ) do
-
- if uSettings[ v ] then
- log(
- 'Error',
- 'Double setting "' .. v.. '"'
- )
- os.exit( -1 )
- end
-
- uSettings[ v ]= true
-
- end
-
- --
-- all command line settings overwrite config file settings
--
- for k, v in pairs( clSettings ) do
- if k ~= 'syncs' then
+ for k, v in pairs( clSettings )
+ do
+ if k ~= 'syncs'
+ then
uSettings[ k ] = v
end
end
@@ -3886,81 +4925,81 @@
--
-- implicitly forces 'insist' on Lsyncd resets.
--
- if not firstTime then
+ if not firstTime
+ then
uSettings.insist = true
end
--
-- adds syncs specified by command line.
--
- if clSettings.syncs then
-
- for _, s in ipairs( clSettings.syncs ) do
-
- if s[ 1 ] == 'rsync' then
-
+ if clSettings.syncs
+ then
+ for _, s in ipairs( clSettings.syncs )
+ do
+ if s[ 1 ] == 'rsync'
+ then
sync{
default.rsync,
source = s[ 2 ],
target = s[ 3 ]
}
-
- elseif s[ 1 ] == 'rsyncssh' then
-
+ elseif s[ 1 ] == 'rsyncssh'
+ then
sync{
default.rsyncssh,
source = s[ 2 ],
host = s[ 3 ],
targetdir=s[ 4 ]
}
-
- elseif s[ 1 ] == 'direct' then
+ elseif s[ 1 ] == 'direct'
+ then
sync{
default.direct,
source=s[ 2 ],
target=s[ 3 ]
}
-
end
-
end
-
end
- if uSettings.nodaemon then
+ if uSettings.nodaemon
+ then
lsyncd.configure( 'nodaemon' )
end
- if uSettings.logfile then
+ if uSettings.logfile
+ then
lsyncd.configure( 'logfile', uSettings.logfile )
end
- if uSettings.logident then
+ if uSettings.logident
+ then
lsyncd.configure( 'logident', uSettings.logident )
end
- if uSettings.logfacility then
+ if uSettings.logfacility
+ then
lsyncd.configure( 'logfacility', uSettings.logfacility )
end
- if uSettings.pidfile then
+ if uSettings.pidfile
+ then
lsyncd.configure( 'pidfile', uSettings.pidfile )
end
--
-- Transfers some defaults to uSettings
--
- if uSettings.statusInterval == nil then
+ if uSettings.statusInterval == nil
+ then
uSettings.statusInterval = default.statusInterval
end
-- makes sure the user gave Lsyncd anything to do
- if Syncs.size() == 0 then
-
- log(
- 'Error',
- 'Nothing to watch!'
- )
+ if Syncs.size() == 0
+ then
+ log( 'Error', 'Nothing to watch!' )
os.exit( -1 )
end
@@ -3970,7 +5009,8 @@
lsyncd.configure( 'running' );
- local ufuncs = {
+ local ufuncs =
+ {
'onAttrib',
'onCreate',
'onDelete',
@@ -3980,102 +5020,96 @@
}
-- translates layer 3 scripts
- for _, s in Syncs.iwalk() do
-
+ for _, s in Syncs.iwalk()
+ do
-- checks if any user functions is a layer 3 string.
local config = s.config
- for _, fn in ipairs(ufuncs) do
-
- if type(config[fn]) == 'string' then
-
- local ft = functionWriter.translate(config[fn])
- config[fn] = assert(loadstring('return '..ft))()
+ for _, fn in ipairs( ufuncs )
+ do
+ if type(config[fn]) == 'string'
+ then
+ local ft = functionWriter.translate( config[ fn ] )
+ config[ fn ] = assert( load( 'return '..ft ) )( )
end
-
end
end
-- runs through the Syncs created by users
- for _, s in Syncs.iwalk( ) do
-
- if s.config.monitor == 'inotify' then
-
+ for _, s in Syncs.iwalk( )
+ do
+ if s.config.monitor == 'inotify'
+ then
Inotify.addSync( s, s.source )
-
- elseif s.config.monitor == 'fsevents' then
-
+ elseif s.config.monitor == 'fsevents'
+ then
Fsevents.addSync( s, s.source )
-
else
-
error(
'sync ' ..
s.config.name ..
' has no known event monitor interface.'
)
-
end
-- if the sync has an init function, the init delay
-- is stacked which causes the init function to be called.
- if s.config.init then
-
+ if s.config.init
+ then
s:addInitDelay( )
-
end
end
-
end
--
-- Called by core to query the soonest alarm.
--
--- @return false ... no alarm, core can in untimed sleep, or
+-- @return false ... no alarm, core can go in untimed sleep
-- true ... immediate action
-- times ... the alarm time (only read if number is 1)
--
-function runner.getAlarm( )
+function runner.getAlarm
+( )
+ log( 'Function', 'getAlarm( )' )
- if lsyncdStatus ~= 'run' then
- return false
- end
+ if lsyncdStatus ~= 'run' then return false end
local alarm = false
--
-- Checks if 'a' is sooner than the 'alarm' up-value.
--
- local function checkAlarm( a )
-
- if a == nil then
- error('got nil alarm')
- end
+ local function checkAlarm
+ (
+ a -- alarm time
+ )
+ if a == nil then error( 'got nil alarm' ) end
- if alarm == true or not a then
+ if alarm == true or not a
+ then
-- 'alarm' is already immediate or
-- a not a new alarm
return
end
-- sets 'alarm' to a if a is sooner
- if not alarm or a < alarm then
+ if not alarm or a < alarm
+ then
alarm = a
end
-
end
--
-- checks all syncs for their earliest alarm,
-- but only if the global process limit is not yet reached.
--
- if
- not uSettings.maxProcesses or
- processCount < uSettings.maxProcesses
+ if not uSettings.maxProcesses
+ or processCount < uSettings.maxProcesses
then
- for _, s in Syncs.iwalk( ) do
- checkAlarm( s:getAlarm ( ))
+ for _, s in Syncs.iwalk( )
+ do
+ checkAlarm( s:getAlarm( ) )
end
else
log(
@@ -4090,14 +5124,9 @@
-- checks for an userAlarm
checkAlarm( UserAlarms.getAlarm( ) )
- log(
- 'Alarm',
- 'runner.getAlarm returns: ',
- alarm
- )
+ log( 'Alarm', 'runner.getAlarm returns: ', alarm )
return alarm
-
end
@@ -4110,12 +5139,15 @@
--
-- Collector for every child process that finished in startup phase
--
-function runner.collector(
+function runner.collector
+(
pid, -- pid of the child process
exitcode -- exitcode of the child process
)
- if exitcode ~= 0 then
- log('Error', 'Startup process',pid,' failed')
+ if exitcode ~= 0
+ then
+ log( 'Error', 'Startup process', pid, ' failed' )
+
terminate( -1 )
end
@@ -4125,54 +5157,41 @@
--
-- Called by core when an overflow happened.
--
-function runner.overflow( )
-
- log(
- 'Normal',
- '--- OVERFLOW in event queue ---'
- )
+function runner.overflow
+( )
+ log( 'Normal', '--- OVERFLOW in event queue ---' )
lsyncdStatus = 'fade'
-
end
--
-- Called by core on a hup signal.
--
-function runner.hup( )
-
- log(
- 'Normal',
- '--- HUP signal, resetting ---'
- )
+function runner.hup
+( )
+ log( 'Normal', '--- HUP signal, resetting ---' )
lsyncdStatus = 'fade'
-
end
--
-- Called by core on a term signal.
--
-function runner.term( sigcode )
-
- local sigtexts = {
- [ 2 ] =
- 'INT',
-
- [ 15 ] =
- 'TERM'
+function runner.term
+(
+ sigcode -- signal code
+)
+ local sigtexts =
+ {
+ [ 2 ] = 'INT',
+ [ 15 ] = 'TERM'
};
local sigtext = sigtexts[ sigcode ];
- if not sigtext then
- sigtext = 'UNKNOWN'
- end
+ if not sigtext then sigtext = 'UNKNOWN' end
- log(
- 'Normal',
- '--- ', sigtext, ' signal, fading ---'
- )
+ log( 'Normal', '--- ', sigtext, ' signal, fading ---' )
lsyncdStatus = 'fade'
@@ -4187,17 +5206,16 @@
--
-- Returns an Inlet to that sync.
--
-function sync( opts )
-
- if lsyncdStatus ~= 'init' then
- error(
- 'Sync can only be created during initialization.',
- 2
- )
+function sync
+(
+ opts
+)
+ if lsyncdStatus ~= 'init'
+ then
+ error( 'Sync can only be created during initialization.', 2 )
end
return Syncs.add( opts ).inlet
-
end
@@ -4211,60 +5229,51 @@
binary, -- binary to call
... -- arguments
)
- if
- agent == nil or
- type( agent ) ~= 'table'
+ if agent == nil
+ or type( agent ) ~= 'table'
then
- error(
- 'spawning with an invalid agent',
- 2
- )
+ error( 'spawning with an invalid agent', 2 )
end
- if lsyncdStatus == 'fade' then
- log(
- 'Normal',
- 'ignored process spawning while fading'
- )
+ if lsyncdStatus == 'fade'
+ then
+ log( 'Normal', 'ignored process spawning while fading' )
return
end
- if type( binary ) ~= 'string' then
- error(
- 'calling spawn(agent, binary, ...): binary is not a string',
- 2
- )
+ if type( binary ) ~= 'string'
+ then
+ error( 'calling spawn(agent, binary, ...): binary is not a string', 2 )
end
local dol = InletFactory.getDelayOrList( agent )
- if not dol then
- error(
- 'spawning with an unknown agent',
- 2
- )
+ if not dol
+ then
+ error( 'spawning with an unknown agent', 2 )
end
--
-- checks if a spawn is called on an already active event
--
- if dol.status then
-
+ if dol.status
+ then
-- is an event
- if dol.status ~= 'wait' then
+ if dol.status ~= 'wait'
+ then
error('spawn() called on an non-waiting event', 2)
end
-
else
-- is a list
-
- for _, d in ipairs(dol) do
- if d.status ~= 'wait' and d.status ~= 'block' then
- error('spawn() called on an non-waiting event list', 2)
+ for _, d in ipairs( dol )
+ do
+ if d.status ~= 'wait'
+ and d.status ~= 'block'
+ then
+ error( 'spawn() called on an non-waiting event list', 2 )
end
end
-
end
--
@@ -4272,12 +5281,12 @@
--
local pid = lsyncd.exec( binary, ... )
- if pid and pid > 0 then
-
+ if pid and pid > 0
+ then
processCount = processCount + 1
- if
- uSettings.maxProcesses and
- processCount > uSettings.maxProcesses
+
+ if uSettings.maxProcesses
+ and processCount > uSettings.maxProcesses
then
error( 'Spawned too much processes!' )
end
@@ -4285,67 +5294,61 @@
local sync = InletFactory.getSync( agent )
-- delay or list
- if dol.status then
-
+ if dol.status
+ then
-- is a delay
- dol.status = 'active'
- sync.processes[ pid ] = dol
+ dol:setActive( )
+ sync.processes[ pid ] = dol
else
-
-- is a list
- for _, d in ipairs( dol ) do
- d.status = 'active'
+ for _, d in ipairs( dol )
+ do
+ d:setActive( )
end
- sync.processes[ pid ] = dol
+ sync.processes[ pid ] = dol
end
-
end
end
--
-- Spawns a child process using the default shell.
--
-function spawnShell(
+function spawnShell
+(
agent, -- the delay(list) to spawn the command for
command, -- the shell command
... -- additonal arguments
)
- return spawn(
- agent,
- '/bin/sh',
- '-c',
- command,
- '/bin/sh',
- ...
- )
+ return spawn( agent, '/bin/sh', '-c', command, '/bin/sh', ... )
end
------
--- Observes a filedescriptor
+
--
-function observefd(
+-- Observes a filedescriptor.
+--
+function observefd
+(
fd, -- file descriptor
ready, -- called when fd is ready to be read
writey -- called when fd is ready to be written
)
- return lsyncd.observe_fd(
- fd,
- ready,
- writey
- )
+ return lsyncd.observe_fd( fd, ready, writey )
end
+
--
--- Stops observeing a filedescriptor
+-- Stops observeing a filedescriptor.
--
-function nonobservefd(
+function nonobservefd
+(
fd -- file descriptor
)
return lsyncd.nonobserve_fd( fd )
end
+
--
-- Calls func at timestamp.
--
@@ -4354,44 +5357,70 @@
--
alarm = UserAlarms.alarm
+
--
-- Comfort routine also for user.
-- Returns true if 'String' starts with 'Start'
--
-function string.starts( String, Start )
-
+function string.starts
+(
+ String,
+ Start
+)
return string.sub( String, 1, #Start )==Start
-
end
+
--
-- Comfort routine also for user.
-- Returns true if 'String' ends with 'End'
--
-function string.ends( String, End )
-
+function string.ends
+(
+ String,
+ End
+)
return End == '' or string.sub( String, -#End ) == End
-
end
+
--
--- The Lsyncd 2.1 settings call
+-- The settings call
--
-function settings( a1 )
+function settings
+(
+ a1 -- a string for getting a setting
+ -- or a table of key/value pairs to set these settings
+)
+
-- if a1 is a string this is a get operation
- if type( a1 ) == 'string' then
+ if type( a1 ) == 'string'
+ then
return uSettings[ a1 ]
end
-- if its a table it sets all the value of the bale
- for k, v in pairs( a1 ) do
- if type( k ) ~= 'number' then
+ for k, v in pairs( a1 )
+ do
+ if type( k ) ~= 'number'
+ then
+ if not settingsCheckgauge[ k ]
+ then
+ error( 'setting "'..k..'" unknown.', 2 )
+ end
+
uSettings[ k ] = v
else
+ if not settingsCheckgauge[ v ]
+ then
+ error( 'setting "'..v..'" unknown.', 2 )
+ end
+
uSettings[ v ] = true
end
end
end
+
settingsSafe = settings
--
diff -Nru lsyncd-2.1.6/README.md lsyncd-2.2.3/README.md
--- lsyncd-2.1.6/README.md 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/README.md 2018-03-09 12:39:11.000000000 +0000
@@ -6,7 +6,9 @@
Rsync+ssh is an advanced action configuration that uses a SSH to act file and directory moves directly on the target instead of re-transmitting the move destination over the wire.
-Fine-grained customization can be achieved through the config file. Custom action configs can even be written from scratch in cascading layers ranging from shell scripts to code written in the [Lua language](http://www.lua.org/). This way simple, powerful and flexible configurations can be acheived. See the manual for details [Lsyncd21Manual](https://github.com/axkibe/lsyncd/wiki/Manual-to-Lsyncd-2.1.x)
+Fine-grained customization can be achieved through the config file. Custom action configs can even be written from scratch in cascading layers ranging from shell scripts to code written in the [Lua language](http://www.lua.org/). This way simple, powerful and flexible configurations can be acheived. See [the manual](https://axkibe.github.io/lsyncd/) for details.
+
+Lsyncd 2.2.1 requires rsync >= 3.1 on all source and target machines.
License: [GPLv2](http://www.fsf.org/licensing/licenses/info/GPLv2.html) or any later GPL version.
@@ -20,6 +22,8 @@
[GlusterFS](http://www.gluster.org) and [BindFS](http://bindfs.org/) use a FUSE-Filesystem to interject kernel/userspace filesystem events.
+[Mirror](https://github.com/stephenh/mirror) is an asynchronous synchronisation tool that takes use of the inotify notifications much like Lsyncd. The main differences are: it is developed specifically for master-master use, thus running on a daemon on both systems, uses its own transportation layer instead of rsync and is Java instead of Lsyncd's C core with Lua scripting.
+
Lsyncd usage examples
---------------------
```lsyncd -rsync /home remotehost.org::share/```
@@ -31,8 +35,9 @@
This will also rsync/watch '/home', but it uses a ssh connection to make moves local on the remotehost instead of re-transmitting the moved file over the wire.
-Some more complicated examples, tips and tricks you can find in the [Lsyncd21Manual](https://github.com/axkibe/lsyncd/wiki/Manual-to-Lsyncd-2.1.x).
+Some more complicated examples, tips and tricks you can find in the [manual](https://axkibe.github.io/lsyncd/).
Disclaimer
----------
Besides the usual disclaimer in the license, we want to specifically emphasize that the authors, and any organizations the authors are associated with, can not be held responsible for data-loss caused by possible malfunctions of Lsyncd.
+
diff -Nru lsyncd-2.1.6/tests/churn-direct.lua lsyncd-2.2.3/tests/churn-direct.lua
--- lsyncd-2.1.6/tests/churn-direct.lua 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/tests/churn-direct.lua 2018-03-09 12:39:11.000000000 +0000
@@ -1,8 +1,8 @@
-#!/usr/bin/lua
-- a heavy duty test.
-- makes thousends of random changes to the source tree
require( 'posix' )
+
dofile( 'tests/testlib.lua' )
cwriteln( '****************************************************************' )
@@ -12,7 +12,7 @@
local tdir, srcdir, trgdir = mktemps( )
-- makes some startup data
-churn( srcdir, 10 )
+churn( srcdir, 10, init )
local logs = { }
--local logs = {'-log', 'Exec', '-log', 'Delay' }
@@ -20,13 +20,13 @@
'./lsyncd',
'-nodaemon',
'-direct', srcdir, trgdir,
- unpack( logs )
+ table.unpack( logs )
)
cwriteln( 'waiting for Lsyncd to startup' )
posix.sleep( 1 )
-churn( srcdir, 500 )
+churn( srcdir, 500, false )
cwriteln( 'waiting for Lsyncd to finish its jobs.' )
posix.sleep( 10 )
diff -Nru lsyncd-2.1.6/tests/churn-rsync.lua lsyncd-2.2.3/tests/churn-rsync.lua
--- lsyncd-2.1.6/tests/churn-rsync.lua 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/tests/churn-rsync.lua 2018-03-09 12:39:11.000000000 +0000
@@ -1,6 +1,6 @@
-#!/usr/bin/lua
-- a heavy duty test.
-- makes thousends of random changes to the source tree
+
require( 'posix' )
dofile( 'tests/testlib.lua' )
@@ -11,7 +11,7 @@
local tdir, srcdir, trgdir = mktemps( )
-- makes some startup data
-churn( srcdir, 100 )
+churn( srcdir, 100, true )
local logs = { }
-- logs = { "-log", "Delay", "-log", "Fsevents" }
@@ -20,14 +20,14 @@
'-nodaemon',
'-delay', '5',
'-rsync', srcdir, trgdir,
- unpack( logs )
+ table.unpack( logs )
)
cwriteln( 'waiting for Lsyncd to startup' )
posix.sleep( 1 )
-churn( srcdir, 500 )
+churn( srcdir, 500, false )
cwriteln( 'waiting for Lsyncd to finish its jobs.' )
@@ -48,7 +48,7 @@
cwriteln( 'Signal terminating diff = ', code )
end
-if exitcode ~= 0
+if code ~= 0
then
os.exit( 1 )
else
diff -Nru lsyncd-2.1.6/tests/churn-rsyncssh.lua lsyncd-2.2.3/tests/churn-rsyncssh.lua
--- lsyncd-2.1.6/tests/churn-rsyncssh.lua 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/tests/churn-rsyncssh.lua 2018-03-09 12:39:11.000000000 +0000
@@ -1,4 +1,3 @@
-#!/usr/bin/lua
-- a heavy duty test.
-- makes thousends of random changes to the source tree
@@ -15,7 +14,7 @@
local tdir, srcdir, trgdir = mktemps()
-- makes some startup data
-churn( srcdir, 100 )
+churn( srcdir, 5, true )
local logs = {}
logs = { '-log', 'Delay' }
@@ -29,20 +28,23 @@
srcdir,
'localhost',
trgdir,
- unpack(logs)
+ table.unpack(logs)
)
cwriteln( 'waiting for Lsyncd to startup' )
posix.sleep( 1 )
-churn( srcdir, 100 )
+churn( srcdir, 150, false )
cwriteln( 'waiting for Lsyncd to finish its jobs.' )
posix.sleep( 10 )
cwriteln( 'killing the Lsyncd daemon' )
+
posix.kill(pid)
-local _, exitmsg, lexitcode = posix.wait(lpid)
+
+local _, exitmsg, lexitcode = posix.wait( lpid )
+
cwriteln( 'Exitcode of Lsyncd = ', exitmsg, ' ', lexitcode )
_, result, code = os.execute( 'diff -r ' .. srcdir .. ' ' .. trgdir )
@@ -54,7 +56,7 @@
cwriteln( 'Signal terminating diff = ', code )
end
-if exitcode ~= 0
+if code ~= 0
then
os.exit( 1 )
else
diff -Nru lsyncd-2.1.6/tests/exclude-rsync.lua lsyncd-2.2.3/tests/exclude-rsync.lua
--- lsyncd-2.1.6/tests/exclude-rsync.lua 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/tests/exclude-rsync.lua 2018-03-09 12:39:11.000000000 +0000
@@ -1,124 +1,152 @@
-#!/usr/bin/lua
-require("posix")
-dofile("tests/testlib.lua")
-
-cwriteln("****************************************************************")
-cwriteln(" Testing excludes ")
-cwriteln("****************************************************************")
-
-local tdir, srcdir, trgdir = mktemps()
-local logfile = tdir .. "log"
-local cfgfile = tdir .. "config.lua"
+require( 'posix' )
+dofile( 'tests/testlib.lua' )
+
+cwriteln( '****************************************************************' )
+cwriteln( ' Testing excludes (rsync)' )
+cwriteln(' ****************************************************************' )
+
+local tdir, srcdir, trgdir = mktemps( )
+local logfile = tdir .. 'log'
+local cfgfile = tdir .. 'config.lua'
local range = 5
-local log = {"-log", "all"}
+local log = { '-log', 'all' }
writefile(cfgfile, [[
-settings = {
+settings {
logfile = "]]..logfile..[[",
nodaemon = true,
- delay = 3,
}
sync {
default.rsync,
source = "]]..srcdir..[[",
target = "]]..trgdir..[[",
+ delay = 3,
exclude = {
"erf",
"/eaf",
"erd/",
"/ead/",
},
-}]]);
+}]])
-- writes all files
-local function writefiles()
- posix.mkdir(srcdir .. "d");
- writefile(srcdir .. "erf", "erf");
- writefile(srcdir .. "eaf", "erf");
- writefile(srcdir .. "erd", "erd");
- writefile(srcdir .. "ead", "ead");
- writefile(srcdir .. "d/erf", "erf");
- writefile(srcdir .. "d/eaf", "erf");
- writefile(srcdir .. "d/erd", "erd");
- writefile(srcdir .. "d/ead", "ead");
-end
-
--- test if the filename exists, fails if this is different to expect
-local function testfile(filename, expect)
- local stat, err = posix.stat(filename)
- if stat and not expect then
- cwriteln("failure: ",filename," should be excluded");
- os.exit(1);
+local function writefiles
+( )
+ posix.mkdir( srcdir .. 'd' )
+ writefile( srcdir .. 'erf', 'erf' )
+ writefile( srcdir .. 'eaf', 'erf' )
+ writefile( srcdir .. 'erd', 'erd' )
+ writefile( srcdir .. 'ead', 'ead' )
+ writefile( srcdir .. 'd/erf', 'erf' )
+ writefile( srcdir .. 'd/eaf', 'erf' )
+ writefile( srcdir .. 'd/erd', 'erd' )
+ writefile( srcdir .. 'd/ead', 'ead' )
+end
+
+--
+-- Tests if the filename exists
+-- fails if this is different to expect.
+--
+local function testfile
+(
+ filename,
+ expect
+)
+ local stat, err = posix.stat( filename )
+
+ if stat and not expect
+ then
+ cwriteln( 'failure: ', filename, ' should be excluded')
+
+ os.exit( 1 )
end
- if not stat and expect then
- cwriteln("failure: ",filename," should not be excluded");
- os.exit(1);
+
+ if not stat and expect
+ then
+ cwriteln( 'failure: ', filename, ' should not be excluded' )
+ os.exit( 1 )
end
end
-- test all files
-local function testfiles()
- testfile(trgdir .. "erf", false);
- testfile(trgdir .. "eaf", false);
- testfile(trgdir .. "erd", true);
- testfile(trgdir .. "ead", true);
- testfile(trgdir .. "d/erf", false);
- testfile(trgdir .. "d/eaf", true);
- testfile(trgdir .. "d/erd", true);
- testfile(trgdir .. "d/ead", true);
+local function testfiles
+( )
+ testfile( trgdir .. 'erf', false )
+ testfile( trgdir .. 'eaf', false )
+ testfile( trgdir .. 'erd', true )
+ testfile( trgdir .. 'ead', true )
+ testfile( trgdir .. 'd/erf', false )
+ testfile( trgdir .. 'd/eaf', true )
+ testfile( trgdir .. 'd/erd', true )
+ testfile( trgdir .. 'd/ead', true )
end
-cwriteln( 'testing startup excludes' );
-writefiles( );
-cwriteln( 'starting Lsyncd' );
-local pid = spawn( './lsyncd', cfgfile, '-log', 'all');
+cwriteln( 'testing startup excludes' )
+
+writefiles( )
+
+cwriteln( 'starting Lsyncd' )
+
+local pid = spawn( './lsyncd', cfgfile, '-log', 'all' )
+
+cwriteln( 'waiting for Lsyncd to start' )
-cwriteln( 'waiting for Lsyncd to start' );
posix.sleep( 3 )
-cwriteln( 'testing excludes after startup' );
-testfiles( );
-cwriteln( 'ok, removing sources' );
+
+cwriteln( 'testing excludes after startup' )
+
+testfiles( )
+
+cwriteln( 'ok, removing sources' )
if srcdir:sub( 1,4 ) ~= '/tmp'
then
-- just to make sure before rm -rf
- cwriteln( 'exit before drama, srcdir is "', srcdir, '"' );
- os.exit( 1 );
+ cwriteln( 'exit before drama, srcdir is "', srcdir, '"' )
+
+ os.exit( 1 )
end
-os.execute( 'rm -rf '..srcdir..'/*' );
+os.execute( 'rm -rf '..srcdir..'/*' )
-cwriteln( 'waiting for Lsyncd to remove destination' );
+cwriteln( 'waiting for Lsyncd to remove destination' )
-posix.sleep( 5 );
+posix.sleep( 5 )
_, result, code = os.execute( 'diff -urN ' .. srcdir .. ' ' .. trgdir )
if result ~= 'exit' or code ~= 0
then
- cwriteln( 'fail, target directory not empty!' );
- os.exit( 1 );
+ cwriteln( 'fail, target directory not empty!' )
+
+ os.exit( 1 )
end
-cwriteln( 'writing files after startup' );
-writefiles( );
-cwriteln( 'waiting for Lsyncd to transmit changes' );
-posix.sleep( 5 );
-testfiles( );
-
-cwriteln( 'killing started Lsyncd' );
-posix.kill( pid );
-local _, exitmsg, lexitcode = posix.wait( lpid );
-cwriteln( 'Exitcode of Lsyncd = ', exitmsg, ' ', lexitcode );
+cwriteln( 'writing files after startup' )
+
+writefiles( )
+
+cwriteln( 'waiting for Lsyncd to transmit changes' )
+
+posix.sleep( 5 )
+
+testfiles( )
+
+cwriteln( 'killing started Lsyncd' )
+
+posix.kill( pid )
+local _, exitmsg, exitcode = posix.wait( lpid )
+
+cwriteln( 'Exitcode of Lsyncd = ', exitmsg, ' ', exitcode );
-if lexitcode == 143
+if exitcode == 143
then
- cwriteln( "OK" );
- os.exit( 0 );
+ cwriteln( 'OK' )
+ os.exit( 0 )
else
- os.exit( 1 );
+ os.exit( 1 )
end
-- TODO remove temp
diff -Nru lsyncd-2.1.6/tests/exclude-rsyncssh.lua lsyncd-2.2.3/tests/exclude-rsyncssh.lua
--- lsyncd-2.1.6/tests/exclude-rsyncssh.lua 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/tests/exclude-rsyncssh.lua 2018-03-09 12:39:11.000000000 +0000
@@ -1,10 +1,8 @@
-#!/usr/bin/lua
-
require( 'posix' )
dofile( 'tests/testlib.lua' )
cwriteln( '****************************************************************' );
-cwriteln( ' Testing excludes' );
+cwriteln( ' Testing excludes (rsyncssh)' );
cwriteln( '****************************************************************' );
cwriteln( ' (this test needs passwordless ssh localhost access ' );
cwriteln( ' for current user)' );
@@ -17,14 +15,14 @@
log = {'-log', 'all'}
writefile(cfgfile, [[
-settings = {
+settings {
logfile = ']]..logfile..[[',
- nodaemon = true,
- delay = 3,
+ nodaemon = true
}
sync {
default.rsyncssh,
+ delay = 3,
host = 'localhost',
source = ']]..srcdir..[[',
targetdir = ']]..trgdir..[[',
@@ -37,64 +35,77 @@
}]]);
-- writes all files
-local function writefiles()
- posix.mkdir(srcdir .. 'd');
- writefile(srcdir .. 'erf', 'erf');
- writefile(srcdir .. 'eaf', 'erf');
- writefile(srcdir .. 'erd', 'erd');
- writefile(srcdir .. 'ead', 'ead');
- writefile(srcdir .. 'd/erf', 'erf');
- writefile(srcdir .. 'd/eaf', 'erf');
- writefile(srcdir .. 'd/erd', 'erd');
- writefile(srcdir .. 'd/ead', 'ead');
+local function writefiles
+( )
+ posix.mkdir( srcdir .. 'd' )
+ writefile( srcdir .. 'erf', 'erf' )
+ writefile( srcdir .. 'eaf', 'erf' )
+ writefile( srcdir .. 'erd', 'erd' )
+ writefile( srcdir .. 'ead', 'ead' )
+ writefile( srcdir .. 'd/erf', 'erf' )
+ writefile( srcdir .. 'd/eaf', 'erf' )
+ writefile( srcdir .. 'd/erd', 'erd' )
+ writefile( srcdir .. 'd/ead', 'ead' )
end
-- test if the filename exists, fails if this is different to expect
-local function testfile(filename, expect)
- local stat, err = posix.stat(filename)
- if stat and not expect then
- cwriteln('failure: ',filename,' should be excluded');
- os.exit(1);
+local function testfile
+(
+ filename,
+ expect
+)
+ local stat, err = posix.stat( filename )
+
+ if stat and not expect
+ then
+ cwriteln( 'failure: ',filename,' should be excluded' );
+ os.exit( 1 );
end
- if not stat and expect then
- cwriteln('failure: ',filename,' should not be excluded');
- os.exit(1);
+
+ if not stat and expect
+ then
+ cwriteln( 'failure: ',filename,' should not be excluded' );
+ os.exit( 1 );
end
end
-- test all files
-local function testfiles()
- testfile( trgdir .. 'erf', false );
- testfile( trgdir .. 'eaf', false );
- testfile( trgdir .. 'erd', true );
- testfile( trgdir .. 'ead', true );
- testfile( trgdir .. 'd/erf', false );
- testfile( trgdir .. 'd/eaf', true );
- testfile( trgdir .. 'd/erd', true );
- testfile( trgdir .. 'd/ead', true );
+local function testfiles
+( )
+ testfile( trgdir .. 'erf', false )
+ testfile( trgdir .. 'eaf', false )
+ testfile( trgdir .. 'erd', true )
+ testfile( trgdir .. 'ead', true )
+ testfile( trgdir .. 'd/erf', false )
+ testfile( trgdir .. 'd/eaf', true )
+ testfile( trgdir .. 'd/erd', true )
+ testfile( trgdir .. 'd/ead', true )
end
-cwriteln('testing startup excludes');
-writefiles();
-cwriteln('starting Lsyncd');
-local pid = spawn('./lsyncd', cfgfile, unpack(log));
-cwriteln('waiting for Lsyncd to start');
-posix.sleep(10)
-cwriteln('testing excludes after startup');
-testfiles();
-cwriteln('ok, removing sources');
-if srcdir:sub(1,4) ~= '/tmp' then
+cwriteln( 'testing startup excludes' )
+writefiles( )
+cwriteln( 'starting Lsyncd' )
+local pid = spawn( './lsyncd', cfgfile, table.unpack( log ) )
+cwriteln( 'waiting for Lsyncd to start' )
+posix.sleep( 10 )
+cwriteln( 'testing excludes after startup' )
+testfiles( )
+cwriteln( 'ok, removing sources' )
+
+if srcdir:sub(1,4) ~= '/tmp'
+then
-- just to make sure before rm -rf
- cwriteln('exist before drama, srcdir is "', srcdir, '"');
- os.exit(1);
+ cwriteln('exit before drama, srcdir is "', srcdir, '"')
+
+ os.exit( 1 )
end
-os.execute( 'rm -rf ' .. srcdir .. '/*' );
-cwriteln( 'waiting for Lsyncd to remove destination' );
-posix.sleep( 5 );
+os.execute( 'rm -rf ' .. srcdir .. '/*' )
+cwriteln( 'waiting for Lsyncd to remove destination' )
+posix.sleep( 5 )
-_, result, code = os.execute('diff -urN '..srcdir..' '..trgdir) ~= 0
+_, result, code = os.execute( 'diff -urN '..srcdir..' '..trgdir )
if result ~= 'exit' or code ~= 0
then
@@ -102,17 +113,17 @@
os.exit( 1 );
end
-cwriteln( 'writing files after startup' );
-writefiles( );
-cwriteln( 'waiting for Lsyncd to transmit changes' );
-posix.sleep( 15 );
-testfiles( );
-
-cwriteln( 'killing started Lsyncd' );
-posix.kill( pid );
-local _, exitmsg, lexitcode = posix.wait( lpid );
-cwriteln( 'Exitcode of Lsyncd = ', exitmsg, ' ', lexitcode );
-posix.sleep( 1 );
+cwriteln( 'writing files after startup' )
+writefiles( )
+cwriteln( 'waiting for Lsyncd to transmit changes' )
+posix.sleep( 15 )
+testfiles( )
+
+cwriteln( 'killing started Lsyncd' )
+posix.kill( pid )
+local _, exitmsg, lexitcode = posix.wait( lpid )
+cwriteln( 'Exitcode of Lsyncd = ', exitmsg, ' ', lexitcode )
+posix.sleep( 1 )
if lexitcode == 143
then
diff -Nru lsyncd-2.1.6/tests/filter-rsync.lua lsyncd-2.2.3/tests/filter-rsync.lua
--- lsyncd-2.1.6/tests/filter-rsync.lua 1970-01-01 00:00:00.000000000 +0000
+++ lsyncd-2.2.3/tests/filter-rsync.lua 2018-03-09 12:39:11.000000000 +0000
@@ -0,0 +1,146 @@
+require( 'posix' )
+dofile( 'tests/testlib.lua' )
+
+cwriteln( '****************************************************************' )
+cwriteln( ' Testing filters (rsync)' )
+cwriteln( '****************************************************************' )
+
+local tdir, srcdir, trgdir = mktemps( )
+local logfile = tdir .. "log"
+local cfgfile = tdir .. "config.lua"
+local range = 5
+local log = {"-log", "all"}
+
+writefile(cfgfile, [[
+settings {
+ logfile = ']]..logfile..[[',
+ nodaemon = true,
+}
+
+sync {
+ default.rsync,
+ source = ']]..srcdir..[[',
+ target = ']]..trgdir..[[',
+ delay = 3,
+ filter = {
+ '- /ab**',
+ '+ /a**',
+ '- /**',
+ },
+}]])
+
+-- writes all files
+local function writefiles
+( )
+ writefile( srcdir .. 'abc', 'abc' )
+ writefile( srcdir .. 'acc', 'acc' )
+ writefile( srcdir .. 'baa', 'baa' )
+ posix.mkdir( srcdir .. 'abx' )
+ writefile( srcdir .. 'abx/a', 'abxa' )
+ posix.mkdir( srcdir .. 'acx' )
+ writefile( srcdir .. 'acx/x', 'acxx' )
+end
+
+--
+-- Tests if the filename exists
+-- fails if this is different to expect.
+--
+local function testfile
+(
+ filename,
+ expect
+)
+ local stat, err = posix.stat( filename )
+
+ if stat and not expect
+ then
+ cwriteln( 'failure: ', filename, ' should be filtered')
+
+ os.exit( 1 )
+ end
+
+ if not stat and expect
+ then
+ cwriteln( 'failure: ', filename, ' should not be filtered' )
+ os.exit( 1 )
+ end
+end
+
+-- test all files
+local function testfiles
+( )
+ testfile( trgdir .. 'abc', false )
+ testfile( trgdir .. 'acc', true )
+ testfile( trgdir .. 'baa', false )
+ testfile( trgdir .. 'abx/a', false )
+ testfile( trgdir .. 'acx/x', true )
+end
+
+
+cwriteln( 'testing startup filters' )
+
+writefiles( )
+
+cwriteln( 'starting Lsyncd' )
+
+local pid = spawn( './lsyncd', cfgfile, '-log', 'all' )
+
+cwriteln( 'waiting for Lsyncd to start' )
+
+posix.sleep( 3 )
+
+cwriteln( 'testing filters after startup' )
+
+testfiles( )
+
+cwriteln( 'ok, removing sources' )
+
+if srcdir:sub( 1,4 ) ~= '/tmp'
+then
+ -- just to make sure before rm -rf
+ cwriteln( 'exit before drama, srcdir is "', srcdir, '"' )
+
+ os.exit( 1 )
+end
+
+os.execute( 'rm -rf '..srcdir..'/*' )
+
+cwriteln( 'waiting for Lsyncd to remove destination' )
+
+posix.sleep( 5 )
+
+_, result, code = os.execute( 'diff -urN ' .. srcdir .. ' ' .. trgdir )
+
+if result ~= 'exit' or code ~= 0
+then
+ cwriteln( 'fail, target directory not empty!' )
+
+ os.exit( 1 )
+end
+
+cwriteln( 'writing files after startup' )
+
+writefiles( )
+
+cwriteln( 'waiting for Lsyncd to transmit changes' )
+
+posix.sleep( 5 )
+
+testfiles( )
+
+cwriteln( 'killing started Lsyncd' )
+
+posix.kill( pid )
+local _, exitmsg, exitcode = posix.wait( lpid )
+
+cwriteln( 'Exitcode of Lsyncd = ', exitmsg, ' ', exitcode );
+
+if exitcode == 143
+then
+ cwriteln( 'OK' )
+ os.exit( 0 )
+else
+ os.exit( 1 )
+end
+
+-- TODO remove temp
diff -Nru lsyncd-2.1.6/tests/l4rsyncdata.lua lsyncd-2.2.3/tests/l4rsyncdata.lua
--- lsyncd-2.1.6/tests/l4rsyncdata.lua 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/tests/l4rsyncdata.lua 2018-03-09 12:39:11.000000000 +0000
@@ -1,4 +1,3 @@
-#!/usr/bin/lua
require( 'posix' )
dofile( 'tests/testlib.lua' )
@@ -28,7 +27,7 @@
'-nodaemon',
'-delay', '5',
"-rsync", srcdir, trgdir,
- unpack( logs )
+ table.unpack( logs )
)
cwriteln( 'waiting for lsyncd to start' )
@@ -70,7 +69,7 @@
cwriteln( 'Signal terminating diff = "', code, '"')
end
-if result ~= 'exit' or exitcode ~= 0
+if result ~= 'exit' or code ~= 0
then
os.exit( 1 )
else
diff -Nru lsyncd-2.1.6/tests/schedule.lua lsyncd-2.2.3/tests/schedule.lua
--- lsyncd-2.1.6/tests/schedule.lua 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/tests/schedule.lua 2018-03-09 12:39:11.000000000 +0000
@@ -1,10 +1,9 @@
-#!/usr/bin/lua
-require("posix")
-dofile("tests/testlib.lua")
-
-cwriteln("****************************************************************")
-cwriteln(" Testing Lsyncd scheduler ")
-cwriteln("****************************************************************")
+require( 'posix' )
+dofile( 'tests/testlib.lua' )
+
+cwriteln( '****************************************************************' )
+cwriteln( ' Testing Lsyncd scheduler ' )
+cwriteln( '****************************************************************' )
local tdir, srcdir, trgdir = mktemps()
local logfile = tdir .. "log"
@@ -12,10 +11,10 @@
local logs = {"-log", "all" }
writefile(cfgfile, [[
-settings = {
- logfile = "]]..logfile..[[",
+settings {
+ logfile = "]]..logfile..[[",
log = all,
- nodaemon = true,
+ nodaemon = true,
maxProcesses = 1
}
@@ -47,30 +46,36 @@
-- test if the filename exists, fails if this is different to expect
-local function testfile(filename)
+local function testfile
+(
+ filename
+)
local stat, err = posix.stat(filename)
- if not stat then
- cwriteln("failure: ",filename," missing")
- os.exit(1)
+
+ if not stat
+ then
+ cwriteln( 'failure: ', filename, ' missing' )
+ os.exit( 1 )
end
end
-cwriteln("starting Lsyncd")
-local pid = spawn("./lsyncd", cfgfile, unpack(logs))
-cwriteln("waiting for Lsyncd to do a few cycles")
-posix.sleep(30)
-cwriteln("look if every circle got a chance to run")
-testfile(srcdir.."a")
-testfile(srcdir.."b")
-testfile(srcdir.."c")
-cwriteln("killing started Lsyncd")
-posix.kill(pid)
-local _, exitmsg, lexitcode = posix.wait(lpid)
-cwriteln("Exitcode of Lsyncd = ", exitmsg, " ", lexitcode)
+cwriteln( 'starting Lsyncd' )
+local pid = spawn( './lsyncd', cfgfile, table.unpack( logs ) )
+cwriteln( 'waiting for Lsyncd to do a few cycles' )
+posix.sleep( 30 )
+cwriteln( 'look if every circle got a chance to run' )
+testfile( srcdir..'a' )
+testfile( srcdir..'b' )
+testfile( srcdir..'c' )
+cwriteln( 'killing started Lsyncd' )
+posix.kill( pid )
+local _, exitmsg, lexitcode = posix.wait( lpid )
+cwriteln( 'Exitcode of Lsyncd = ', exitmsg, ' ', lexitcode)
posix.sleep(1);
-if lexitcode == 143 then
- cwriteln("OK")
+if lexitcode == 143
+then
+ cwriteln( 'OK' )
os.exit( 0 )
else
os.exit( 1 )
diff -Nru lsyncd-2.1.6/tests/testlib.lua lsyncd-2.2.3/tests/testlib.lua
--- lsyncd-2.1.6/tests/testlib.lua 2015-10-15 06:29:47.000000000 +0000
+++ lsyncd-2.2.3/tests/testlib.lua 2018-03-09 12:39:11.000000000 +0000
@@ -1,136 +1,185 @@
-- common testing environment
-posix = require('posix')
+posix = require( 'posix' )
-- escape codes to colorize output on terminal
local c1='\027[47;34m'
+
local c0='\027[0m'
----
--- writes colorized
--
-function cwriteln(...)
- io.write(c1, ...)
- io.write(c0, '\n')
+-- Writes colorized.
+--
+function cwriteln
+(...)
+ io.write( c1, '++ ', ... )
+ io.write( c0, '\n' )
end
------
--- initializes the pseudo random generator
--- if environemnt 'SEED' is set, use that as seed.
-local seed = os.getenv('SEED') or os.time()
-math.randomseed(seed)
-cwriteln('random seed: ', seed)
-
------
--- creates a tmp directory
---
--- @returns the name of the directory
---
-function mktempd()
- local f = io.popen('mktemp -td ltest.XXX', 'r')
- local s = f:read('*a')
- f:close()
- s = s:gsub('[\n\r]+', ' ')
- s = s:match('^%s*(.-)%s*$')
+--
+-- Initializes the pseudo random generator
+--
+-- If environment variable 'SEED' is set,
+-- that one is used seed.
+--
+local seed = os.getenv( 'SEED') or os.time( )
+
+math.randomseed( seed )
+
+cwriteln( 'random seed: ', seed )
+
+--
+-- Creates a tmp directory.
+--
+-- Returns the name of the directory.
+--
+function mktempd
+( )
+ local f = io.popen( 'mktemp -td ltest.XXX', 'r' )
+
+ local s = f:read( '*a' )
+
+ f:close( )
+
+ s = s:gsub( '[\n\r]+', ' ' )
+
+ s = s:match( '^%s*(.-)%s*$' )
+
return s
end
------
--- creates a tmp directory with the
--- typical lsyncd test architecture
---
--- @returns path of tmpdir
--- path of srcdir
--- path of trgdir
--
-
-function mktemps()
- local tdir = mktempd()..'/'
- cwriteln('using ', tdir, ' as test root')
+-- Creates a tmp directory with the
+-- typical lsyncd test architecture.
+--
+-- returns path of tmpdir
+-- path of srcdir
+-- path of trgdir
+--
+function mktemps
+( )
+ local tdir = mktempd() .. '/'
+ cwriteln( 'using ', tdir, ' as test root' )
local srcdir = tdir..'src/'
local trgdir = tdir..'trg/'
- posix.mkdir(srcdir)
- posix.mkdir(trgdir)
+ posix.mkdir( srcdir )
+ posix.mkdir( trgdir )
return tdir, srcdir, trgdir
end
-----
--- Writes a file with 'text' in it.
+--
+-- Writes a file with 'text' in it
-- and adds a newline.
--
-function writefile(filename, text)
- local f = io.open(filename, 'w')
- if not f then
- cwriteln('Cannot open "'..filename..'" for writing.')
+function writefile
+(
+ filename,
+ text
+)
+ local f = io.open( filename, 'w' )
+
+ if not f
+ then
+ cwriteln( 'Cannot open "'..filename..'" for writing.' )
return false
end
- f:write(text)
- f:write('\n')
- f:close()
+
+ f:write( text )
+ f:write( '\n' )
+ f:close( )
+
return true
end
------
--- spawns a subprocess.
--
--- @returns the processes pid
+-- Spawns a subprocess.
+--
+-- Returns the processes pid.
--
-function spawn(...)
- args = {...}
- cwriteln('spawning: ', table.concat(args, ' '))
- local pid = posix.fork()
- if pid < 0 then
- cwriteln('Error, failed fork!')
- os.exit(-1)
+function spawn
+(...)
+ args = { ... }
+
+ cwriteln( 'spawning: ', table.concat( args, ' ' ) )
+
+ local pid = posix.fork( )
+
+ if pid < 0
+ then
+ cwriteln( 'Error, failed fork!' )
+
+ os.exit( -1 )
end
- if pid == 0 then
- posix.exec(...)
+
+ if pid == 0
+ then
+ posix.exec( ... )
-- should not return
- cwriteln('Error, failed to spawn: ', ...)
- os.exit(-1);
+
+ cwriteln( 'Error, failed to spawn: ', ... )
+
+ os.exit( -1 )
end
+
return pid
end
------
+--
-- Makes a lot of random data
--
--- @param rootdir ... the directory to make data in
--- @param n ... roughly how much data action will done
--
-function churn(rootdir, n)
+function churn
+(
+ rootdir, -- the directory to make data in
+ n, -- roughly how much data action will be done
+ init -- if true init random data only, no sleeps or moves
+)
-- all dirs created, indexed by integer and path
- root = {name=''}
- alldirs = {root}
- dirsWithFileI = {}
- dirsWithFileD = {}
+ root = { name = '' }
+ alldirs = { root }
+ dirsWithFileI = { }
+ dirsWithFileD = { }
- -----
+ --
-- returns the name of a directory
--
-- name is internal recursive paramter, keep it nil.
--
- local function dirname(dir, name)
+ local function dirname
+ (
+ dir,
+ name
+ )
name = name or ''
- if not dir then
+
+ if not dir
+ then
return name
end
- return dirname(dir.parent, dir.name .. '/' .. name)
+
+ return dirname( dir.parent, dir.name .. '/' .. name )
end
- -----
+ --
-- Picks a random dir.
--
- local function pickDir(notRoot)
- if notRoot then
- if #alldirs <= 2 then
+ local function pickDir
+ (
+ notRoot
+ )
+ if notRoot
+ then
+ if #alldirs <= 2
+ then
return nil
end
- return alldirs[math.random(2, #alldirs)]
+
+ return alldirs[ math.random( 2, #alldirs ) ]
end
- return alldirs[math.random(#alldirs)]
+
+ return alldirs[ math.random( #alldirs ) ]
end
- ----
+ --
-- Picks a random file.
--
-- Returns 3 values:
@@ -138,212 +187,312 @@
-- * the filename
-- * number of files in directory
--
- local function pickFile()
+ local function pickFile
+ ( )
-- picks the random directory
- if #dirsWithFileI < 1 then
+ if #dirsWithFileI < 1
+ then
return
end
- local rdir = dirsWithFileI[math.random(1, #dirsWithFileI)]
- if not rdir then
+
+ local rdir = dirsWithFileI[ math.random( 1, #dirsWithFileI ) ]
+
+ if not rdir
+ then
return
end
-- counts the files in there
local c = 0
- for name, _ in pairs(rdir) do
- if #name == 2 then
+
+ for name, _ in pairs(rdir)
+ do
+ if #name == 2
+ then
c = c + 1
end
end
-- picks one file at random
- local cr = math.random(1, c)
+ local cr = math.random( 1, c )
+
local fn
- for name, _ in pairs(rdir) do
- if #name == 2 then
+
+ for name, _ in pairs( rdir )
+ do
+ if #name == 2
+ then
-- filenames are 2 chars wide.
cr = cr - 1
- if cr == 0 then
+ if cr == 0
+ then
fn = name
break
end
end
end
+
return rdir, fn, c
end
- -----
+ --
-- Removes a reference to a file
--
- -- @param dir --- directory reference
- -- @param fn --- filename
- -- @param c --- number of files in dir
+ -- @param dir -- directory reference
+ -- @param fn -- filename
+ -- @param c -- number of files in dir
--
- local function rmFileReference(dir, fn, c)
+ local function rmFileReference
+ ( dir, fn, c )
dir[fn] = nil
- if c == 1 then
+
+ if c == 1
+ then
-- if last file from origin dir, it has no files anymore
- for i, v in ipairs(dirsWithFileI) do
- if v == dir then
- table.remove(dirsWithFileI, i)
+ for i, v in ipairs( dirsWithFileI )
+ do
+ if v == dir
+ then
+ table.remove( dirsWithFileI, i )
+
break
end
end
- dirsWithFileD[dir] = nil
+
+ dirsWithFileD[ dir ] = nil
end
end
- ----
+ --
-- possible randomized behaviour.
-- just gives it a pause
--
- local function sleep()
- cwriteln('..zzz..')
- posix.sleep(1)
+ local function sleep
+ ( )
+ cwriteln( '..zzz..' )
+
+ posix.sleep( 1 )
end
- ----
+ --
-- possible randomized behaviour.
-- creates a directory
--
- local function mkdir()
+ local function mkdir
+ ( )
-- chooses a random directory to create it into
- local rdir = pickDir()
+ local rdir = pickDir( )
+
-- creates a new random one letter name
- local nn = string.char(96 + math.random(26))
- if not rdir[nn] then
+ local nn = string.char( 96 + math.random( 26 ) )
+
+ if not rdir[nn]
+ then
local ndir = {
name = nn,
parent = rdir,
}
- local dn = dirname(ndir)
- rdir[nn] = dn
- table.insert(alldirs, ndir)
- cwriteln('mkdir '..rootdir..dn)
- posix.mkdir(rootdir..dn)
+
+ local dn = dirname( ndir )
+
+ rdir[ nn ] = dn
+
+ table.insert( alldirs, ndir )
+
+ cwriteln( 'mkdir '..rootdir..dn )
+
+ posix.mkdir( rootdir..dn )
end
end
- ----
- -- possible randomized behaviour.
+ --
+ -- Possible randomized behaviour:
-- Creates a file.
--
- local function mkfile()
+ local function mkfile
+ ( )
-- chooses a random directory to create it into
local rdir = pickDir()
+
-- creates a new random one letter name
- local nn = 'f'..string.char(96 + math.random(26))
- local fn = dirname(rdir) .. nn
- cwriteln('mkfile '..rootdir..fn)
+ local nn = 'f'..string.char( 96 + math.random( 26 ) )
+
+ local fn = dirname( rdir ) .. nn
+
+ cwriteln( 'mkfile ' .. rootdir .. fn )
+
local f = io.open(rootdir..fn, 'w')
- if f then
- for i=1,10 do
- f:write(string.char(96 + math.random(26)))
+
+ if f
+ then
+ for i = 1, 10
+ do
+ f:write( string.char( 96 + math.random( 26 ) ) )
end
- f:write('\n')
- f:close()
- rdir[nn]=true
- if not dirsWithFileD[rdir] then
- table.insert(dirsWithFileI, rdir)
- dirsWithFileD[rdir]=true
+
+ f:write( '\n' )
+
+ f:close( )
+
+ rdir[ nn ]=true
+
+ if not dirsWithFileD[ rdir ]
+ then
+ table.insert( dirsWithFileI, rdir )
+
+ dirsWithFileD[ rdir ]=true
end
end
end
- ----
- -- possible randomized behaviour,
- -- moves a directory.
--
- local function mvdir()
- if #alldirs <= 2 then
+ -- Possible randomized behaviour:
+ -- Moves a directory.
+ --
+ local function mvdir
+ ( )
+ if #alldirs <= 2
+ then
return
end
+
-- chooses a random directory to move
- local odir = pickDir(true)
+ local odir = pickDir( true )
+
-- chooses a random directory to move to
- local tdir = pickDir()
+ local tdir = pickDir( )
-- makes sure tdir is not a subdir of odir
local dd = tdir
- while dd do
- if odir == dd then
+
+ while dd
+ do
+ if odir == dd
+ then
return
end
+
dd = dd.parent
end
+
-- origin name in the target dir already
- if tdir[odir.name] ~= nil then
+ if tdir[odir.name] ~= nil
+ then
return
end
- local on = dirname(odir)
- local tn = dirname(tdir)
- cwriteln('mvdir ',rootdir,on,' -> ',rootdir,tn,odir.name)
- os.rename(rootdir..on, rootdir..tn..odir.name)
- odir.parent[odir.name] = nil
+
+ local on = dirname( odir )
+
+ local tn = dirname( tdir )
+
+ cwriteln( 'mvdir ', rootdir,on, ' -> ', rootdir, tn, odir.name )
+
+ os.rename( rootdir..on, rootdir..tn..odir.name )
+
+ odir.parent[ odir.name ] = nil
+
odir.parent = tdir
- tdir[odir.name] = odir
+
+ tdir[ odir.name ] = odir
end
- ----
+ --
-- possible randomized behaviour,
-- moves a file.
--
- local function mvfile()
- local odir, fn, c = pickFile()
- if not odir then
+ local function mvfile
+ ( )
+ local odir, fn, c = pickFile( )
+
+ if not odir
+ then
return
end
+
-- picks a directory with a file at random
-- picks a target directory at random
- local tdir = pickDir()
- local on = dirname(odir)
- local tn = dirname(tdir)
- cwriteln('mvfile ',rootdir,on,fn,' -> ',rootdir,tn,fn)
- os.rename(rootdir..on..fn, rootdir..tn..fn)
- rmFileReference(odir, fn, c)
+ local tdir = pickDir( )
+
+ local on = dirname( odir )
+
+ local tn = dirname( tdir )
+
+ cwriteln( 'mvfile ', rootdir, on, fn, ' -> ', rootdir, tn, fn )
+
+ os.rename( rootdir..on..fn, rootdir..tn..fn )
+ rmFileReference( odir, fn, c )
+
+ tdir[ fn ] = true
- tdir[fn] = true
- if not dirsWithFileD[tdir] then
- dirsWithFileD[tdir] = true
- table.insert(dirsWithFileI, tdir)
+ if not dirsWithFileD[ tdir ]
+ then
+ dirsWithFileD[ tdir ] = true
+
+ table.insert( dirsWithFileI, tdir )
end
end
- ----
- -- possible randomized behaviour,
- -- removes a file.
--
- local function rmfile()
- local dir, fn, c = pickFile()
- if dir then
- local dn = dirname(dir)
- cwriteln('rmfile ',rootdir,dn,fn)
- posix.unlink(rootdir..dn..fn)
- rmFileReference(dir, fn, c)
+ -- Possible randomized behaviour:
+ -- Removes a file.
+ --
+ local function rmfile
+ ( )
+ local dir, fn, c = pickFile( )
+
+ if dir
+ then
+ local dn = dirname( dir )
+
+ cwriteln( 'rmfile ', rootdir, dn, fn )
+
+ posix.unlink( rootdir..dn..fn )
+
+ rmFileReference( dir, fn, c )
end
end
- local dice = {
- { 10, sleep },
- { 20, mkfile },
- { 20, mkdir },
- { 20, mvdir },
- { 20, rmfile },
- }
+ local dice
+
+ if init
+ then
+ dice =
+ {
+ { 10, mkfile },
+ { 10, mkdir },
+ }
+ else
+ dice =
+ {
+ { 50, sleep },
+ { 20, mkfile },
+ { 20, mkdir },
+ { 20, mvdir },
+ { 20, rmfile },
+ }
+ end
+
+ cwriteln( 'making random data' )
- cwriteln('making random data')
local ndice = 0
- for i, d in ipairs(dice) do
- ndice = ndice + d[1]
- d[1] = ndice
+
+ for i, d in ipairs( dice )
+ do
+ ndice = ndice + d[ 1 ]
+ d[ 1 ] = ndice
end
- for ai=1,n do
+ for ai = 1, n
+ do
-- throws a die what to do
- local acn = math.random(ndice)
- for i, d in ipairs(dice) do
- if acn <= d[1] then
- d[2]()
+ local acn = math.random( ndice )
+
+ for i, d in ipairs( dice )
+ do
+ if acn <= d[ 1 ]
+ then
+ d[ 2 ]( )
+
break
end
end