Compare commits

...

317 Commits

Author SHA1 Message Date
Daniel Poelzleithner e1e13503b2 Use github discussions 2023-03-03 14:37:43 +01:00
Daniel Poelzleithner 95a2c2fca2 Update docs and build manpage on build process 2023-03-03 14:25:46 +01:00
Daniel Poelzleithner 5d3e0fe417
Merge pull request #697 from jandsu/patch-1
Added the 'filter' parameter to the manual
2023-03-03 13:07:56 +01:00
Jan Vorwerk 77f5a61bab Added the 'filter' parameter to the manual
According to
https://github.com/lsyncd/lsyncd/issues/510#issuecomment-390921153
2023-03-03 08:29:42 +01:00
Daniel Poelzleithner 3872ca77ac Check ssh arguments and issue warning 2023-03-02 18:45:58 +01:00
Daniel Poelzleithner a4556b835f Fix spacing/wording 2023-03-02 17:22:01 +01:00
Daniel Poelzleithner d4fc88ba10 Update README
- write about 2 way sync
- more alternatives
2023-02-28 19:22:00 +01:00
Daniel Poelzleithner dd48284cd0 fix changelog dates 2022-11-17 15:43:48 +01:00
Daniel Poelzleithner 6d59f16140 release 2.3.1 2022-11-17 02:22:35 +01:00
Daniel Poelzleithner e831ad6945 check alarm for type first. fixes #679 2022-11-11 20:05:09 +01:00
Daniel Poelzleithner d779eb434f Allow lsyncd to be build in the source folder without breaking. Not recommended 2022-11-11 19:32:09 +01:00
Daniel Poelzleithner 818fd4115f also test executable files 2022-11-11 19:00:26 +01:00
Daniel Poelzleithner f4c15496dc fix cron test 2022-11-11 18:26:57 +01:00
Daniel Poelzleithner 96a6276440 disable lua 5.4.4 due segfault on luac invocation 2022-11-11 18:05:50 +01:00
Daniel Poelzleithner 5096f27bbd Fix more warnings from static code analysis 2022-11-11 15:27:21 +01:00
Daniel Poelzleithner 945b57d8fb Add Cron test 2022-11-11 15:11:09 +01:00
Daniel Poelzleithner 787b2b0015 fix static code analysis warnings, typos 2022-11-10 04:21:17 +01:00
Daniel Poelzleithner e09b58b721 Fix wrong arguments for Sync:appendFilter proxy function 2022-11-09 22:44:04 +01:00
Daniel Poelzleithner 9b81bb1785 fix dump for more complex structures 2022-11-09 22:41:04 +01:00
Daniel Poelzleithner cda98d6ba9 add Queue.inject, test Queue, bugfixes in remove() 2022-11-09 15:22:58 +01:00
Daniel Poelzleithner daa8abb4cf nix: extract version from lsyncd.lua 2022-11-08 13:27:20 +01:00
Daniel Poelzleithner 7604dd0e16 cmake: extract lsyncd version from lsyncd.lua 2022-10-31 14:36:39 +01:00
Daniel Poelzleithner e371078222 update nix locks 2022-10-28 15:12:54 +02:00
Daniel Poelzleithner a2f7df504c bump version 2022-10-28 15:12:41 +02:00
Daniel Poelzleithner e08685cfd6 Search crontab library with different names 2022-10-28 14:08:00 +02:00
Daniel Poelzleithner 1eb8a83500 Print version in debug and help messages 2022-10-28 14:07:17 +02:00
Daniel Poelzleithner e6f3427c5f reduce required cmake version to 3.5 2022-06-30 11:43:12 +02:00
Daniel Poelzleithner f02bf700c1 bump version to 2.3.0 2022-06-07 23:26:04 +02:00
Daniel Poelzleithner 6c8a3c2490 don't fail fast in CI 2022-06-07 22:30:51 +02:00
Daniel Poelzleithner 82f9f27172 Add proper compatibilty wrappers for 5.1 2022-06-07 22:28:30 +02:00
Daniel Poelzleithner 63f506582a fix ci 2022-06-07 11:12:48 +02:00
Daniel Poelzleithner 18008e1d15 enable more versions 2022-06-03 07:08:18 +02:00
Daniel Poelzleithner dd84b2e6b6 lua 5.4 fixes 2022-06-03 07:07:08 +02:00
Daniel Poelzleithner 8c71941f9c Remove old debug prints, fix error() calls 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner c84e3d0e05 fix compile warning about write() return code 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner c5514e0451 Document crontab support 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner e94edac51e Update docs 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner f244405bce Use ^ instead of ${} syntax in commandSubstitution as well 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner 5b8fa2deda don't export splitQuotedString 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner f2272f1aa7 Support tunnel command as string 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner 867a3cec8e Add debug function 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner 22259eee49 Add rsync tunnel example 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner a16d714d6e fix alarm calculation 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner 1b7bf7577f do not export inhert functions 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner 075d64a069 Implement batchSizeLimit for rsync based transfers
If the batchSizeLimit is set, only files lower then this limit will
be grouped in one rsync transfer.
Each file larger then this limit will spawn their own transfer process.
This will cause large files to no longer block small file transfers under the
circumstance the maxProcess limit on the sync is larger then 1

A very optimized, very secure transfer configuration based on a
pool of ssh connection looks like this:

```
sync {
        default.rsync,
        tunnel = tunnel {
            command = {"ssh", "-N", "-L", "localhost:${localport}:localhost:873", "user@targetmachine"},
            mode = "pool",
            parallel = 2,
        },
        source = "/tmp/src",
        target    = "rsync://localhost:${localport}/test",
        delay = 5,
        batchSizeLimit = 1024 * 1024 * 30,
        maxProcesses = 4,
        rsync = {
                verbose = true,
                inplace = true,
        }
}
```

If you configure remote ssh configuration only allows portforwarding and your rsync daemon
is configured correctly, you can very securely transfer data without giving shell access.
2022-06-03 06:45:38 +02:00
Daniel Poelzleithner 7f0127548b Add function to return file size 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner 32e2cac8cc Add script to simulate fs changes 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner 4a0896f58f fix logic error in alarm compare 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner 6d9164a29a Fix wrong alarm selection 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner ff84a35d4d Finish full sync cron trigger 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner 9ec4f42888 Only check Tunnels if there is at least one tunnel defined 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner 95f8ea67dd Log source of next alarm as well 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner c8fbe955fe Add debug functions and timestamp2string helpers 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner 58f1991e0b [WIP] implement crontab full sync 2022-06-03 06:45:38 +02:00
Daniel Poelzleithner d6c49b6858 Fixes for diffrent lua versions 2022-06-03 06:45:30 +02:00
Daniel Poelzleithner 46188f9f60 Implement tunnel pool mode.
In this mode, multiple tunnel processes are started and connection a load
balanced on the pool of connections.

Example config:

...
sync {
  default.rsync,
  tunnel = tunnel {
    command = {"ssh", "-N", "-L", "localhost:${localport}:localhost:873", "user@testmachine"},
    mode = "pool",
    parallel = 2,
  },
  target    = "rsync://localhost:${localport}/test",
  ...
}
2022-06-03 06:44:02 +02:00
Daniel Poelzleithner 6e60f6b89e Call runner cleanup method when exiting through error codes 2022-06-03 06:44:02 +02:00
Daniel Poelzleithner 2b9de7d4c3 rename traceback function 2022-06-03 06:44:02 +02:00
Daniel Poelzleithner fb86124f30 Call user provideable check function 2022-06-03 06:44:01 +02:00
Daniel Poelzleithner eda846deed Add function to return a free port 2022-06-03 06:44:01 +02:00
Daniel Poelzleithner f66b7147fd Report tunnel status in status file 2022-06-03 06:44:01 +02:00
Daniel Poelzleithner c543a21162 Add function to substitude commands with placeholders 2022-06-03 06:44:01 +02:00
Daniel Poelzleithner 3d2288dccf Updates to tunnel logic.
Delay before tunnel is considered up.
Working delay if restart fails.
Disabled state.
Kill all tunnel processes on graceful exit
2022-06-03 06:44:01 +02:00
Daniel Poelzleithner f603d41c6c bump version 2022-06-03 06:44:01 +02:00
Daniel Poelzleithner 274b2b0416 [WIP] Add tunnel support 2022-06-03 06:44:01 +02:00
Daniel Poelzleithner 24ef43f5fd Add lua-crontab to dependencies 2022-06-03 06:43:43 +02:00
Daniel Poelzleithner 1796fdb71b fix manual link 2022-04-20 23:16:44 +02:00
Daniel Poelzleithner 3fae375201 only link to the releases page 2022-04-20 23:11:18 +02:00
Daniel Poelzleithner b9c459b94d update flakes 2022-04-20 23:08:29 +02:00
Daniel Poelzleithner 96725fa494 correctly install html docs when existing. build docs in nixpkg 2022-04-20 19:50:35 +02:00
Daniel Poelzleithner 0de678ab3c move docs from gh-pages into src folder to keep it easier in sync 2022-04-20 19:20:52 +02:00
Daniel Poelzleithner 2841ef8528 add .editorconfig 2022-03-11 07:12:39 +01:00
Daniel Poelzleithner a410ddebb8 Install man page to correct subfolder
fixes #655
2022-03-04 12:10:32 +01:00
Daniel Poelzleithner 8e002d64b5
Merge pull request #656 from LeonGaultier/patch-lua_version_mismatch_error_start_binary
fix error - bad binary format (version mismatch)
2022-02-28 21:12:09 +01:00
Marko Oldenburg b7d11f6b00 change header discription 2022-02-24 14:40:46 +01:00
Marko Oldenburg efa8155b68 fix error - bad binary format (version mismatch)
The problem is that the generated make files end up referencing a mix of lua versions
This patch will fix it.
2022-02-14 12:55:05 +01:00
Daniel Poelzleithner e2a27af6e7 use relative sh path 2022-01-12 20:46:41 +01:00
Daniel Poelzleithner f65cdd282d finish lua2.4 changes 2022-01-12 20:46:14 +01:00
Daniel Poelzleithner a609f34971 fix compilation with lua 5.4
fixes #621
2021-12-14 16:45:25 +01:00
Daniel Poelzleithner 012b0cdd3e Merge branch 'master' of github.com:lsyncd/lsyncd 2021-12-13 13:26:14 +01:00
Daniel Poelzleithner 0b59ae7652 Merge remote-tracking branch 'origin/pr/595' 2021-12-10 15:22:23 +01:00
Daniel Poelzleithner b7aa3d9a75
Merge pull request #538 from schneiderl/master
Fixed typo at readme.md
2021-12-10 15:19:17 +01:00
Daniel Poelzleithner 39f2f3a373 Finish -onepass option to exit when all syncs ran sucessfully once 2021-12-10 15:05:30 +01:00
Daniel Poelzleithner d737c03c03 Merge remote-tracking branch 'origin/pr/541' 2021-12-10 12:51:01 +01:00
Daniel Poelzleithner f812cd4cd5 Merge remote-tracking branch 'origin/pr/567' 2021-12-10 12:25:17 +01:00
Daniel Poelzleithner 938c702525 cleanup ssh server when aborting ci run 2021-12-10 12:20:18 +01:00
Daniel Poelzleithner e5f71ea3ae Merge remote-tracking branch 'origin/pr/568' 2021-12-10 12:20:08 +01:00
Daniel Poelzleithner dcebacb554
Merge pull request #615 from ajdavis/catalina-inotify-fix
Remove /System/Volumes/Data from fsevent paths on macOS
2021-12-09 15:43:27 +01:00
Daniel Poelzleithner 92af746a73 Enable CI 2021-12-09 14:37:30 +01:00
Daniel Poelzleithner 5212fd4794 don't depend on which. Add CI script 2021-12-09 13:50:15 +01:00
Daniel Poelzleithner aea80964f3 install examples into docs 2021-12-08 18:54:02 +01:00
Daniel Poelzleithner 99a682f4cd add advanced ssh example 2021-12-08 18:28:03 +01:00
Daniel Poelzleithner 75bb0fb084 ignore tests/ssh folder 2021-12-08 18:27:32 +01:00
Daniel Poelzleithner 3e8aad3b2e Use sshopts for rsyncssh tests 2021-12-08 18:26:11 +01:00
Daniel Poelzleithner deb425a075 use relative paths for direct mode 2021-12-08 18:25:11 +01:00
Daniel Poelzleithner 5b0d266669 add cmd option to pass ssh options 2021-12-08 18:24:13 +01:00
Daniel Poelzleithner bb247e0267 Merge remote-tracking branch 'origin/pr/583'
fixes #583
2021-12-06 20:31:12 +01:00
Daniel Poelzleithner 185e317119 Merge branch 'master' of github.com:lsyncd/lsyncd 2021-12-03 12:23:27 +01:00
Daniel Poelzleithner 4b1b4d0104 update changelog 2021-12-02 18:33:27 +01:00
Daniel Poelzleithner 960c357045
Update README.md 2021-12-02 17:54:27 +01:00
Daniel Poelzleithner 4c3cf2e6af Use local ssh server for tests 2021-12-02 17:51:32 +01:00
Daniel Poelzleithner 564d54be76 Allow and default to relative paths for binaries executed 2021-12-02 17:51:15 +01:00
Daniel Poelzleithner 9b2b3ad6e6 Add flake.nix file for easy building 2021-11-24 14:28:46 +01:00
A. Jesse Jiryu Davis 7bb8715bfd Remove /System/Volumes/Data from fsevent paths on macOS
Apparently, beginning with macOS 10.15 Catalina, fsevent paths are prefixed
with "/System/Volumes/Data". Remove it to restore event notification feature
on macOS.

Fixes #587
2020-09-25 15:36:25 -04:00
Thomas Nixon c4b5465622 allow deleting excluded files with rsync
This option can't be added to both init and action, because the rsync
exclude mechanism is used to select the files to send, so would delete
all non-changed files. It's only relevant during init anyway, as after
that no excluded files will be made on the destination.
2020-03-17 00:01:46 +00:00
Taylor Dean cd9672699e
Enable ssh._extra args
ssh._extra args are currently ignored and never actually appended
to the rsh arg. This PR addresses this issue (#402 which is closed
despite never being properly resolved)
2019-11-13 03:19:31 -05:00
Torben Jaeger c02ee3b6d7 unsetting vars and cleanup 2019-08-08 17:58:15 +02:00
Torben Jaeger 5e08ea2cef fix different version format in lua.h 2019-08-08 17:58:15 +02:00
Dan Rose 390de3dbaa
Expose rsync's copy_unsafe_links
As per the rsync docs: "This tells rsync to copy the referent of symbolic links that point outside the copied tree. Absolute symlinks are also treated like ordinary files, and so are any symlinks in the source path itself when --relative is used. This option has no additional effect if --copy-links was also specified."
2019-08-06 18:09:52 -05:00
Bernard Gray a57b80f9c8 make indenting consistent 2018-12-06 08:41:53 +11:00
Bernard Gray 188b691bea add onepass option to exit after initial sync 2018-12-05 17:05:18 +11:00
Lucas Gabriel Schneider d32957d5a2
Update README.md 2018-10-06 11:51:07 -03:00
Axel Kittenberger 42413cabbe some older glibc still need this it seems 2018-05-22 09:11:09 +02:00
Axel Kittenberger 13bdd9075f
Merge pull request #500 from yokogawa-k/fix-search-non-versioned-lua
Fix for non-versioned lua not being searched in Cmake
2018-03-26 11:36:26 +02:00
yokogawa-k 0af99d8d5b fix non-versioned lua not search in cmake
"FOREACH" ignore empty elements, but "FOREACH IN LISTS" include emoty elements.
https://cmake.org/cmake/help/latest/command/foreach.html
2018-03-26 17:11:29 +09:00
Axel Kittenberger 9b47a1f8fe cleanups 2018-03-09 15:41:16 +01:00
Axel Kittenberger a26eb6f94a releasing 2.2.3 2018-03-09 13:39:11 +01:00
Axel Kittenberger f77df7f3e2 cleanups 2018-03-09 13:32:58 +01:00
Axel Kittenberger 465deb1549 prefer Lua 5.2 over 5.3 (due to second missing supportlibs like posix on debian) 2018-03-09 13:11:16 +01:00
Axel Kittenberger 1094759975 adding filter to testsuite, removing #! from testsuite 2018-03-09 13:05:45 +01:00
Axel Kittenberger ff0f4fbd7a fixing table.unpack issue in testsuite 2018-03-09 11:07:53 +01:00
Axel Kittenberger c0a31c215d fixing loadstring() Lua5.3 issue 2018-03-09 10:42:40 +01:00
Axel Kittenberger 99b6b18ac2 fixing loadstring() Lua5.3 issue 2018-03-09 10:42:10 +01:00
Axel Kittenberger a23426d8a4 preparing 2.2.3 2018-03-09 10:27:41 +01:00
Axel Kittenberger f3d65770cd Merge branch 'master' of github.com:axkibe/lsyncd 2018-03-09 10:13:47 +01:00
Axel Kittenberger 4bc456b544 preparing 2.2.3 2018-03-09 10:13:29 +01:00
Axel Kittenberger 2e5f4462b4 preparing 2.2.3 2018-03-09 10:08:21 +01:00
Axel Kittenberger 2dc8dc665b preparing 2.2.3 2018-03-09 10:04:50 +01:00
Axel Kittenberger 8904710acb preparing 2.2.3 2018-03-09 10:04:48 +01:00
Axel Kittenberger ebd2600914 cleanups 2018-03-09 10:03:08 +01:00
Axel Kittenberger faa582e259 cleanups 2018-03-01 15:19:30 +01:00
Axel Kittenberger ba52ee1a6f cleanups 2018-03-01 15:08:26 +01:00
Axel Kittenberger a78f239fa2 apply filters before inotify watching dirs, actually filter events 2018-03-01 14:14:28 +01:00
Axel Kittenberger 465e173983 cleanups 2018-03-01 11:26:12 +01:00
Axel Kittenberger 1e0d867f80 adding inclusion filters 2018-02-27 17:14:36 +01:00
Axel Kittenberger c50aa7c9c1 Changelog 2018-02-27 10:14:57 +01:00
Axel Kittenberger d72a59e70f do not append a '/' to targets when there it ends with a ':' 2018-02-27 10:09:28 +01:00
Axel Kittenberger bbe9c5f38c Merge pull request #463 from danielkza/aws-s3-example
Add example config for syncing with an AWS S3 bucket
2017-08-22 15:57:08 +02:00
Daniel Miranda 62093f609f
Add authorship information to S3 example 2017-08-22 10:36:13 -03:00
Daniel Miranda 1dfff8d13a
Add example config for syncing with an AWS S3 bucket
It requires the official AWS CLI to be available, and that credentials
be set up through some external method, such as environment variables,
IAM profiles, the AWS SDK configuratin.

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.
2017-08-21 23:19:08 -03:00
Axel Kittenberger fb4301d515 Merge pull request #459 from gsouf/patch-1
config file url
2017-08-07 14:39:21 +02:00
Soufiane Ghzal ee51e3edd9 Update lsyncd.1 2017-07-29 18:02:27 +02:00
Soufiane Ghzal 1460b5795c doc config-file url 2017-07-29 18:00:05 +02:00
Axel Kittenberger ea068b4014 reverting to rsync filter useage, until rysnc bug is fixed 2017-02-16 12:17:37 +01:00
Axel Kittenberger 24d58f7741 ChangeLog 2017-02-06 17:10:27 +01:00
Axel Kittenberger 785a7dd503 fixing again setting with new delay objects, cleanups 2017-02-06 17:00:39 +01:00
Axel Kittenberger 544f6066b7 write pid of forked process 2017-01-09 13:13:05 +01:00
Axel Kittenberger 3f78514273 fix exclusion tests 2017-01-09 11:43:23 +01:00
Axel Kittenberger 288f09ad01 a 2017-01-09 11:14:23 +01:00
Axel Kittenberger a0ab850ec0 readme 2017-01-05 19:18:23 +01:00
Axel Kittenberger e21a2184ba preparing 2.2.1 2017-01-05 11:54:29 +01:00
Axel Kittenberger 4c83a9b263 fixing test 2017-01-05 11:39:00 +01:00
Axel Kittenberger 0c5e2c707e new rsync control is no longer recursive, fixing excludes 2017-01-05 11:31:29 +01:00
Axel Kittenberger 1b09a56320 fixing test value 2017-01-05 10:40:32 +01:00
Axel Kittenberger 29f3e63bfa Changelog, adding 'tests' make target 2017-01-05 10:37:43 +01:00
Axel Kittenberger b7c4fe437c typo 2017-01-05 10:29:38 +01:00
Axel Kittenberger 467a69eba2 typo 2017-01-05 10:23:12 +01:00
Axel Kittenberger d1ef5f3d51 use missing-args for rsyncssh too 2017-01-05 10:22:00 +01:00
Axel Kittenberger 0a0fef20ac fixing typos 2017-01-05 10:10:55 +01:00
Axel Kittenberger ccae4ac142 trying delete-missing-args 2017-01-05 10:02:16 +01:00
Axel Kittenberger 620304c92f fixing dates in ChangeLog 2017-01-04 16:27:09 +01:00
Axel Kittenberger fed5cdd2bd changelog 2017-01-04 12:46:10 +01:00
Axel Kittenberger f6f360b9dc fixing test 2017-01-04 12:32:10 +01:00
Axel Kittenberger ac5ef0474e fixing test 2017-01-04 12:25:38 +01:00
Axel Kittenberger ac82ea4708 fixing test 2017-01-04 12:24:59 +01:00
Axel Kittenberger dbe9935328 fixing test 2017-01-04 12:23:22 +01:00
Axel Kittenberger a0bdc8778d ChangeLog 2017-01-04 11:40:17 +01:00
Axel Kittenberger 12d4f43d0f realtive paths do not start with a slash 2017-01-04 11:24:55 +01:00
Axel Kittenberger ff891441c8 removing xargs 2017-01-04 11:00:14 +01:00
Axel Kittenberger c08719ce00 Preparing 2.2.0, removing manpage from 'clean' target 2017-01-04 10:59:01 +01:00
Axel Kittenberger 4909dd3b2c fixing racecondition in default.rsyncssh 2017-01-03 15:30:13 +01:00
Axel Kittenberger 7bae036f03 lifting some inline funcs 2017-01-03 13:08:48 +01:00
Axel Kittenberger 83c6436e84 typo 2016-12-22 15:15:17 +01:00
Axel Kittenberger 16919a893e do not replace active delays 2016-12-22 14:59:18 +01:00
Axel Kittenberger 62923459f4 Actually stack when toDelete,stack 2016-12-22 13:55:59 +01:00
Axel Kittenberger 98c83a7fbf typo 2016-12-21 16:38:04 +01:00
Axel Kittenberger 8e1a85a12d Splitted Delay combination log from logging about it. 2016-12-21 16:29:29 +01:00
Axel Kittenberger ee50768743 reworking tests 2016-12-21 14:21:32 +01:00
Axel Kittenberger 837a3cd927 Fixed Delay changes potentially not reflect on Event layer. Fixed test exitcode 2016-12-21 13:11:55 +01:00
Axel Kittenberger 9e647f8113 objectifying queues 2016-12-14 16:29:33 +01:00
Axel Kittenberger d8b565ae02 fixing encapsulated delays 2016-12-14 14:25:20 +01:00
Axel Kittenberger 4005c2899b encapuslating Delay 2016-12-14 09:02:51 +01:00
Axel Kittenberger 20edbe5f6e cleanups 2016-12-14 08:45:33 +01:00
Axel Kittenberger 155dbf155c cleanups, removing Lsyncd 2.0.x backward compatibility syntax it was blocked by checkgauge already anyway 2016-12-13 17:40:30 +01:00
Axel Kittenberger 8927426be2 cleanup 2016-12-13 16:37:57 +01:00
Axel Kittenberger 1bec061109 adding a distclean script 2016-12-13 14:56:23 +01:00
Axel Kittenberger 982552e6ec cleanups 2016-12-13 14:53:01 +01:00
Axel Kittenberger b15f1d39d9 cleanups 2016-12-13 14:41:35 +01:00
Axel Kittenberger 40a46b6048 code cleanups, improved delay debug logging 2016-12-12 19:53:44 +01:00
Axel Kittenberger 4c77666281 code cleanups 2016-12-06 13:19:35 +01:00
Axel Kittenberger 815d2c4779 cleanups 2016-12-06 12:57:32 +01:00
Axel Kittenberger ccc492cac3 code cleanups, fixing exclusion test 2016-12-06 11:11:48 +01:00
Axel Kittenberger a4481c98e6 preparing 2.1.7 release 2016-12-05 16:41:12 +01:00
Axel Kittenberger 19d0478bd7 added rsync options backup, backup_dir and suffix 2016-12-05 16:38:07 +01:00
Axel Kittenberger db488a86ac adding append/append_verify 2016-12-05 15:47:12 +01:00
Axel Kittenberger 2ff9ae72be adding usermap/groupmap 2016-12-05 15:25:58 +01:00
Axel Kittenberger d444ccb2d2 cleanups 2016-12-05 15:11:00 +01:00
Axel Kittenberger b79defadb3 adding -p to cp for default.direct. Put a log message before daemonizing to test logging. Fix a crash for default.direct 2016-12-02 16:24:07 +01:00
Axel Kittenberger c58658e642 adding rsync chmod parameter 2016-12-01 14:12:56 +01:00
Axel Kittenberger b71089b12e settings{} will now error if an entry is unknown 2016-12-01 13:25:49 +01:00
Axel Kittenberger dbbe5dfdf7 code cleanups, do no longer workaround settings = {...} deprecation 2016-12-01 12:52:34 +01:00
Axel Kittenberger 2f4dd3aa7e code cleanups 2016-12-01 12:40:30 +01:00
Axel Kittenberger 319681e7eb added copy_dirlinks 2016-12-01 12:35:02 +01:00
Axel Kittenberger dc93af01aa code cleanups 2016-12-01 12:30:58 +01:00
Axel Kittenberger de2d47f7e6 added --existing 2016-11-28 11:20:38 +01:00
Axel Kittenberger d5957200a2 added --existing 2016-11-28 11:20:12 +01:00
Axel Kittenberger aaae8f4083 prepending arguments with -- so they can start with a - 2016-11-25 16:23:19 +01:00
Axel Kittenberger 6b6d718a48 fixing shell injection issue 2016-11-25 15:47:46 +01:00
Axel Kittenberger b3f46e2462 Merge pull request #349 from wodny/master
fix: direct mode allows injecting unauthorized filesystem operations
2016-11-25 15:40:52 +01:00
Axel Kittenberger 25ce41a136 do not rebuild manpage by default 2016-11-25 15:09:27 +01:00
Axel Kittenberger 4d2c793b01 code beautify 2016-11-25 14:55:59 +01:00
Axel Kittenberger 8367ae89a0 made compatible with lua5.3 2016-11-24 15:44:08 +01:00
Axel Kittenberger b2da0ea6d2 Merge pull request #398 from mralexjuarez/master
Updated lbash.lua and lrsync.lau to remove the equals (=)
2016-09-01 14:25:25 +02:00
Alex Juarez f81e5d64d2 Updated lbash.lua and lrsync.lau to remove the equals (=) from the settings directive. 2016-08-31 10:46:38 -05:00
Axel Kittenberger 5d2126a472 style cleanup 2016-08-29 13:15:29 +02:00
Axel Kittenberger fa3028321b a 2016-08-29 13:14:51 +02:00
Axel Kittenberger 545eb341cf add rsync.chown 2016-08-29 13:12:09 +02:00
Axel Kittenberger 315379072c fixing files with only ] as special char in their name 2016-06-27 09:26:45 +02:00
Axel Kittenberger 60d84c3ea1 updating manpage 2016-06-08 09:15:31 +02:00
Axel Kittenberger e1c9e325d0 moving the manual 2016-06-08 09:10:13 +02:00
Axel Kittenberger 8e361d1f77 moving manpage 2016-05-30 16:23:14 +02:00
Axel Kittenberger 2e572a1391 moving doc manpage into own subdir 2016-05-24 11:08:25 +02:00
Axel Kittenberger 2a3e4aeb05 Merge pull request #378 from msabramo/patch-1
Fix typo: Exlcusions => Exclusions
2016-05-24 10:30:30 +02:00
Axel Kittenberger 7ec63e1eba Merge pull request #379 from msabramo/default_collect_print_dest
default.collect: Print target
2016-05-24 10:30:06 +02:00
Axel Kittenberger ee5d693846 Merge pull request #380 from msabramo/fix_typo_width_with
Fix typo: width => with
2016-05-24 10:28:37 +02:00
Marc Abramowitz 16c854df8d Fix typo: width => with 2016-05-23 22:54:07 -07:00
Marc Abramowitz 47bb377efc default.collect: Print target
so when a source dir is being synced to multiple targets, one can see
which one is being handled in the log.

e.g.:

```
$ tail -f /var/log/lsyncd/lsyncd.log
...
Mon May 23 22:48:41 2016 Normal: Startup of /mnt/filevolume/fileshare/ -> 10.3.0.40::ACMe_shared/ finished.
...
Mon May 23 22:48:42 2016 Normal: Startup of /mnt/filevolume/fileshare/ -> 10.3.0.41::ACMe_shared/ finished.
```
2016-05-23 22:49:40 -07:00
Marc Abramowitz 86e7a5ab2a Fix typo: Exlcusions => Exclusions 2016-05-23 20:39:10 -07:00
Axel Kittenberger abc2a509f6 omit-[dir/links]-times 2016-05-04 11:25:40 +02:00
Axel Kittenberger 0b9297b790 force _verbatim for 'exitcodes' 2016-05-03 10:45:22 +02:00
Axel Kittenberger 4740a2ca8f Merge pull request #355 from mattwells/patch-1
Correct typo
2015-11-26 11:33:59 +01:00
Matt Wells a0389d6f08 Correct typo 2015-11-26 10:04:52 +00:00
Marcin Szewczyk c4f4ac3e01 fix: direct mode allows injecting unauthorized filesystem operations 2015-10-27 15:18:26 +01:00
Axel Kittenberger cce7db5ebd preparing version 2.1.6 2015-10-15 08:29:47 +02:00
Axel Kittenberger 4bdde8a672 updating ChangeLog 2015-10-15 08:16:28 +02:00
Axel Kittenberger 8fb9fc4bbb Merge branch 'master' of github.com:axkibe/lsyncd 2015-10-15 08:15:16 +02:00
Axel Kittenberger bddc9a7fde updating ChangeLog 2015-10-15 08:15:06 +02:00
Axel Kittenberger ea31ea92a7 Merge pull request #283 from rolandwalker/doc_disclaimer
Clarify the disclaimer.
2015-10-14 14:49:17 +02:00
Axel Kittenberger 5f74865f68 added inplace option for rsync 2015-10-14 14:39:14 +02:00
Axel Kittenberger a18ba20365 Merge pull request #307 from fabled/master
Fix lpostcmd example
2015-10-14 14:38:38 +02:00
Axel Kittenberger b301838eb3 o 2015-10-14 14:30:02 +02:00
Axel Kittenberger 2493476f64 fixing tests for lua 5.2 os.execute call semantics 2015-10-14 14:23:37 +02:00
Axel Kittenberger 1f95925304 compiles on OSX better 2015-10-14 12:57:49 +02:00
Timo Teräs abbf307b97 Add checkgauge in lpostcmd 2015-01-30 09:35:38 +02:00
Timo Teräs 5b76dc1cb6 Fix lpostcmd prepare to work with recent changes 2015-01-30 09:12:25 +02:00
Timo Teräs 19b08adf35 Fix lpostcmd to use isPostcmd via rawget as intended 2015-01-30 09:09:45 +02:00
Axel Kittenberger e9ffda07f0 Merge pull request #298 from creshal/master
Properly sanitize mv parameters (CVE-2014-8990)
2014-11-26 11:39:46 +01:00
Ángel González e6016b3748 Properly sanitize mv parameters (CVE-2014-8990)
When using -rsyncssh option, some filenames
could -in addition of not syncing correctly-
crash the service and execute arbitrary commands
under the credentials of the remote user.

These issues have been assigned CVE-2014-8990

This commit fixes the incomplete and lua5.2-incompatible
sanitization performed by 18f02ad0

Signed-off-by: Sven Schwedas <sven.schwedas@tao.at>
2014-11-26 09:01:25 +01:00
Sven Schwedas 18f02ad013 Sanitize mv arguments:
1. Fixes crashes on file names containing `, $ or "
2. Also prevents shell execution of ``, $() … in file names, which can be
   used to gain remote shell access as lsyncd's (target) user.
2014-10-29 13:32:20 +01:00
Roland Walker aea57a5a11 Clarify the disclaimer.
The previous phrasing might be misread as claiming responsibility
rather than disclaiming it.
2014-07-08 10:43:52 -04:00
Axel Kittenberger 4da2257758 adding install targets and reenabling the manpage stuff 2014-04-29 16:38:25 +02:00
Axel Kittenberger f199fd1866 this can now also go 2014-04-29 16:19:18 +02:00
Axel Kittenberger 8572a6dacc simply adding the compiled manpage to the git repository. that is giving up the idea not to put anything generated into the source repository. it is going to be easier that way. 2014-04-29 16:18:06 +02:00
Axel Kittenberger 23dfeb6a05 ignoring stuff for intree builds 2014-04-29 16:13:12 +02:00
Axel Kittenberger b6228f442d Replacing autotools with CMake 2014-04-29 16:11:27 +02:00
Axel Kittenberger 0a1cab6609 updating changelog 2014-04-24 13:33:07 +02:00
Axel Kittenberger 6d69134971 Merge pull request #238 from st63jun/fix-logging-msg
Fix logging message
2014-04-24 13:17:21 +02:00
Axel Kittenberger 110a525392 Merge pull request #255 from plouj/patch-1
Typo in print format.
2014-04-24 12:47:25 +02:00
Axel Kittenberger f8fea06944 Merge pull request #253 from exKAZUu/master 2014-04-24 12:45:15 +02:00
Axel Kittenberger 85e95ef150 cosmetics 2014-02-28 10:15:48 +01:00
Michael Ploujnikov 786f37d22d Typo in print format.
This causes a segfault when addwatch fails due to a disappeared
directory. Eg: often caused by a race condition with a short lived
directory created by Firefox' safebrowsing subsystem:
~/.cache/mozilla/firefox/pr0f1l3.default/safebrowsing-to_delete/
2014-01-08 23:21:18 -05:00
Kazunori SAKAMOTO 7addc7060b Add missed comma. 2014-01-03 17:11:48 +09:00
Axel Kittenberger 178f315907 Merge pull request #246 from andrewfenn/patch-1
Fix for older kernels to make sure O_CLOEXEC is defined
2013-11-11 01:24:53 -08:00
Andrew Fenn b2383227cd Fix for older kernels to make sure O_CLOEXEC is defined
This patch fixes an issue where I was getting build errors that O_CLOEXEC was undefined on an older 2.6.32 kernel. Even though the flag is supported in this kernel it was not getting defined.
2013-10-30 13:03:24 +07:00
Jun SAITO 1bfd98d64d Fix logging message 2013-09-30 23:05:59 +09:00
Axel Kittenberger c23e9841ee a2x instead of 2013-09-15 18:03:27 +02:00
Axel Kittenberger b6f4c6f990 Merge pull request #231 from rhunter/patch-1
Link to official BindFS homepage in README
2013-08-06 21:27:56 -07:00
Rob Hunter 54e721e94b Update README link to latest official home 2013-08-07 13:34:52 +10:00
Axel Kittenberger 25d2405906 lock pidfile, remove pidfile on INT or TERM 2013-07-30 12:20:23 +02:00
Axel Kittenberger 6f4613c53a adding ssh.identityFile and ssh.options options 2013-07-30 11:16:29 +02:00
Axel Kittenberger 1dacb68745 Merge pull request #221 from dreiss/config
Fixes for parsing excludes file
2013-07-07 23:15:33 -07:00
David Reiss d772fcba0f Ignore blank lines and rsync comments in excludes file 2013-07-05 10:46:27 -07:00
David Reiss 716b88909a Escape + in pattern for extra safety
The old version worked, but it seems like an accident that a "+" after a
"*" is treated as a literal "+" and not a repetition.
2013-07-05 10:45:56 -07:00
David Reiss 502e2e0eed Don't treat exclude lines with embedded "+" characters as inclusions
Previously, this code prevented any file name with a "+" character from
being excluded.
2013-07-05 10:27:18 -07:00
Axel Kittenberger e880c607be wrong month :/ 2013-06-07 14:44:14 +02:00
Axel Kittenberger d0c9a60213 changing delay from command line to a number 2013-06-07 14:24:02 +02:00
Axel Kittenberger c785f0a2ad Changelog 2013-06-07 14:10:39 +02:00
Axel Kittenberger 1c299c14dd checking delay to a number > 0 2013-06-07 14:09:57 +02:00
Axel Kittenberger 3aa8ed1182 * fixing ssh port overwriting the last rsync option
* preparing 2.1.5
2013-06-07 13:40:54 +02:00
Axel Kittenberger 7fe13abab8 Merge branch 'master' of https://github.com/axkibe/lsyncd 2013-06-07 13:26:03 +02:00
Axel Kittenberger 72fa0e8865 Merge branch 'master' of github.com:axkibe/lsyncd 2013-06-07 13:23:55 +02:00
Axel Kittenberger 6290bd6ea7 + fixing 0 characters in -log Exec message for pipes 2013-06-07 11:12:24 +02:00
Axel Kittenberger 25a2274d83 added bwlimit 2013-06-07 11:11:42 +02:00
Axel Kittenberger eb4370db9c a2x is actually not required when building from a tarball 2013-06-07 10:13:55 +02:00
Axel Kittenberger ec15abd244 bugfix: don't replace variables when spawning binary, by flygoast 2013-06-07 10:10:14 +02:00
Axel Kittenberger d0e56565a8 Merge pull request #213 from flygoast/master
fix the size of the buffer allocated for pipemsg
2013-06-06 10:53:36 -07:00
flygoast 367d5e940a bugfix: fix the buffer size of pipemsg 2013-06-07 00:23:50 +08:00
Axel Kittenberger 3c9f88330b link to libmath when checking for LUA_COMPAT 2013-06-03 14:15:46 +02:00
Axel Kittenberger 46d23d59b3 Merge branch 'master' of github.com:axkibe/lsyncd 2013-05-15 00:44:36 +02:00
Axel Kittenberger 6c6ab9adf0 adding onAttrib to checkgauge 2013-05-14 23:13:09 +02:00
Axel Kittenberger 9619062764 Merge pull request #203 from grooverdan/docfix
Fix documentation spelling bits, and a few links
2013-05-14 14:08:20 -07:00
Daniel Black 99ea89ad0d Fix documentation spelling bits, and a few links 2013-05-04 13:11:09 +10:00
Axel Kittenberger dd4a1134a5 Merge pull request #195 from kashyapp/master
Adding --timeout to default.rsync
2013-03-20 04:51:32 -07:00
Kashyap Paidimarri 769fb3c26b Adding timeout to default.rsync 2013-03-19 12:55:34 +05:30
Axel Kittenberger c13af5df7e Merge pull request #190 from kenyon/patch-2
README.md: update link to latest manual
2013-03-15 02:39:45 -07:00
Kenyon Ralph 8c36e20877 README.md: update link to latest manual 2013-03-15 02:37:25 -07:00
Axel Kittenberger 02cc23909a Merge pull request #189 from kenyon/patch-1
README.md: fix spelling errors
2013-03-13 21:37:00 -07:00
Kenyon Ralph 50b6e879a6 README.md: update link to latest manual 2013-03-13 16:36:57 -07:00
Kenyon Ralph 9dafacbf5e README.md: fix spelling errors 2013-03-13 16:33:47 -07:00
Axel Kittenberger 7bfb535da3 forgot password file in the check_gauge 2012-12-06 10:02:07 +01:00
Axel Kittenberger d25eb11a3f preparing release 2.1.4 2012-11-24 14:33:07 +01:00
Axel Kittenberger 5b7057bfd7 adding custom ssh port to rsync calls as well 2012-11-23 16:05:15 +01:00
Axel Kittenberger 824797645b beautified some tests 2012-11-23 16:04:43 +01:00
Axel Kittenberger ca3d3eca59 typeof -> type 2012-11-23 14:03:52 +01:00
Axel Kittenberger e7b757bbc6 checking correct ssh config options 2012-11-23 12:01:21 +01:00
Axel Kittenberger 9ff62e15c6 fixing another uSettings crash condition 2012-11-23 11:03:22 +01:00
Axel Kittenberger 78fa6208bf adding a settings('string') get call, so a upstart again operation works again, adding archive to the checkgauge, adding keep_dirs 2012-11-09 20:15:42 +01:00
Axel Kittenberger fe41e18f7b release 2.1.2 2012-11-03 13:57:53 +01:00
Axel Kittenberger 9c6e703ff4 Merge pull request #165 from DavidWittman/164-excludeFrom
Add excludeFrom to default-rsync checkgauge for #164
2012-11-03 01:08:58 -07:00
David Wittman 2c2570e314 Add excludeFrom to default-rsync checkgauge for #164 2012-11-02 18:32:44 -05:00
Axel Kittenberger 3616dc64fe fixing rsync parameter generation, added password-file, thx to foxban 2012-10-30 09:57:54 +01:00
Axel Kittenberger b1257b0d48 release 2.1.1 2012-10-27 21:25:32 +02:00
Axel Kittenberger 856186168f Merge pull request #160 from bs-github/master
fix for non working rsync options
2012-10-24 00:33:46 -07:00
Birger Schmidt 33c8f88bd3 delete bad (in the big scale) non working example 2012-10-24 17:51:25 +11:00
Birger Schmidt e116ce0a6e fix parameter handling for rsync options 2012-10-24 17:46:18 +11:00
Birger Schmidt 848f6e1f06 Merge remote-tracking branch 'upstream/master' 2012-10-24 17:43:32 +11:00
Birger Schmidt 3b015f8c05 lsyncd/csync2 config: initial commit with ugly glob hack 2012-10-10 11:30:50 +02:00
77 changed files with 11527 additions and 3564 deletions

8
.editorconfig Normal file
View File

@ -0,0 +1,8 @@
# 4 tab indentation
indent_style = tab
indent_size = 4
[*.nix]
indent_style = space
indent_size = 2

20
.github/workflows/build.yaml vendored Normal file
View File

@ -0,0 +1,20 @@
name: "Build"
on:
pull_request:
push:
jobs:
tests:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
version:
- lsyncd_lua5_3
# broken with lua 5.4.4. luac segfault
# - lsyncd_lua5_4
- lsyncd_lua5_1
steps:
- uses: actions/checkout@v2.4.0
- uses: cachix/install-nix-action@v15
- run: chmod og-rw ~
- run: nix develop .#${{ matrix.version }} --command ./tests/ci-run.sh

42
.gitignore vendored
View File

@ -1,38 +1,28 @@
#~~~~~
# Compiled output
#
# compiled stuff
*.o
*.out
lsyncd
#~~~~~
# Autoconf/Automake
#
aclocal.m4
# cmake
AdditionalInfo.txt
config.h
config.h.in
config.log
config.status
configure
install-sh
Makefile
depcomp
Makefile.in
missing
stamp-h1
build*/
CMakeCache.txt
CMakeFiles/
cmake_install.cmake
install_manifest.txt
.deps/
autom4te.cache/
#~~~~~
# Manpages
#
doc/lsyncd.1.xml
doc/lsyncd.1
# generated C code
#####
# Generated C code
#
defaults.c
runner.c
result
tests/ssh
# docs cache
.jekyll-cache

142
CMakeLists.txt Normal file
View File

@ -0,0 +1,142 @@
# preamble
project( Lsyncd )
cmake_minimum_required( VERSION 3.5 )
# extract version
file(STRINGS "lsyncd.lua" LSYNCD_VERSION_RAW REGEX "lsyncd_version = '.*'")
string(REGEX REPLACE "lsyncd_version = \'(.*)\'"
"\\1" LSYNCD_VERSION
${LSYNCD_VERSION_RAW})
set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/" )
# finding Lua
find_package( Lua REQUIRED )
include_directories ( ${LUA_INCLUDE_DIR} )
# setting Lsyncd sources
set( LSYNCD_SRC lsyncd.c runner.c defaults.c )
# selecting the file notification mechanisms to compile against
option( WITH_INOTIFY "Compile with inotify file notifications (Linux)" ON )
option( WITH_FSEVENTS "Compile with inotify file notifications (OSX)" OFF )
if( WITH_INOTIFY )
set( LSYNCD_SRC ${LSYNCD_SRC} inotify.c )
endif( WITH_INOTIFY )
if( WITH_FSEVENTS )
set( LSYNCD_SRC ${LSYNCD_SRC} fsevents.c )
option( XNU_DIR "Path to the xnu sources" )
# if( NOT XNU_DIR/bsd/sys/fsevents.h )
# message( SEND_ERROR "Cannot find bsd/sys/fsevents.h in XNU_DIR" )
# endif( )
include_directories( ${XNU_DIR} )
endif( WITH_FSEVENTS )
if ( APPLE )
set( LSYNCD_TARGET_APPLE 1 )
endif ( APPLE )
# generating the config.h file
configure_file (
"${PROJECT_SOURCE_DIR}/config.h.in"
"${PROJECT_BINARY_DIR}/config.h"
)
include_directories("${PROJECT_BINARY_DIR}")
# 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"
COMMAND ${LUA_EXECUTABLE} ${PROJECT_SOURCE_DIR}/bin2carray.lua runner.out runner runner.c
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_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"
COMMAND ${LUA_EXECUTABLE} ${PROJECT_SOURCE_DIR}/bin2carray.lua defaults.out defaults defaults.c
DEPENDS defaults.out
)
set( DEFAULT_CONFIGS
${PROJECT_SOURCE_DIR}/default.lua
${PROJECT_SOURCE_DIR}/default-rsync.lua
${PROJECT_SOURCE_DIR}/default-rsyncssh.lua
${PROJECT_SOURCE_DIR}/default-direct.lua
)
add_custom_command( OUTPUT defaults.out
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_target( manpage
COMMAND ${CMAKE_COMMAND} -E echo "Updating the manpage"
COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/man
COMMAND a2x --format=manpage ${PROJECT_SOURCE_DIR}/docs/manpage/lsyncd.1.txt -D ${CMAKE_CURRENT_BINARY_DIR}/man
DEPENDS docs/manpage/lsyncd.1.txt
)
# the html documention
add_custom_target( docs-html
COMMAND ${CMAKE_COMMAND} -E echo "Generate html documentation"
COMMAND env JEKYLL_ENV=local jekyll build -d ${CMAKE_CURRENT_BINARY_DIR}/html
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/docs
DEPENDS ${CMAKE_SOURCE_DIR}/docs
)
add_custom_command(
OUTPUT tests
COMMAND ${CMAKE_COMMAND} -E create_symlink ${CMAKE_SOURCE_DIR}/tests tests)
ADD_CUSTOM_TARGET(prepare_tests ALL
DEPENDS tests/
)
add_custom_target( run-tests
COMMAND echo "Running the tests"
COMMAND echo "Note you are expected to:"
COMMAND echo " * have lua-posix installed"
COMMAND ${LUA_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/setup.lua
COMMAND ${CMAKE_BINARY_DIR}/lsyncd -log all -script ${CMAKE_SOURCE_DIR}/tests/utils_test.lua
COMMAND ${LUA_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/cron-rsync.lua
COMMAND ${LUA_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/schedule.lua
COMMAND ${LUA_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/l4rsyncdata.lua
COMMAND ${LUA_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/filter-rsync.lua
COMMAND ${LUA_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/exclude-rsync.lua
COMMAND ${LUA_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/exclude-rsyncssh.lua
COMMAND ${LUA_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/churn-rsync.lua
COMMAND ${LUA_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/churn-rsyncssh.lua
COMMAND ${LUA_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/churn-direct.lua
COMMAND ${LUA_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tests/teardown.lua
COMMAND echo "Finished all successfull!"
DEPENDS prepare_tests
)
# 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 ${CMAKE_CURRENT_BINARY_DIR}/man/lsyncd.1 DESTINATION ${CMAKE_INSTALL_MANDIR}/man1 COMPONENT man )
install( DIRECTORY examples DESTINATION doc )
install( DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION doc OPTIONAL)

View File

@ -313,8 +313,8 @@ Also add information on how to contact you by electronic and paper mail.
If the program is interactive, make it output a short notice like this
when it starts in an interactive mode:
Gnomovision version 69, Copyright (C) year name of author
Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
Lsyncd version 2.X, Copyright (C) 2013 Axel Kittenberger
Lsyncd comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
@ -328,7 +328,7 @@ school, if any, to sign a "copyright disclaimer" for the program, if
necessary. Here is a sample; alter the names:
Yoyodyne, Inc., hereby disclaims all copyright interest in the program
`Gnomovision' (which makes passes at compilers) written by James Hacker.
`Lsyncd' (which makes synchronises directories) written by Axel Kittenberger.
<signature of Ty Coon>, 1 April 1989
Ty Coon, President of Vice

158
ChangeLog
View File

@ -1,5 +1,129 @@
23-10-2012: 2.1.0
2022-11-17: 2.3.1
change: multiple bugfixes, style fixes
2022-03-09: 2.3.0
add: nix flake support
add: support for tunnel commands
add: support for batchSizeLimit
add: -onepass option
add: crontab support
change: support relative executable paths
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
fix: ignore blank lines and rsync commenits in exclude files (David Reiss)
fix: don't tread exclude lines with embedded "+" chars as inclusions (David Reiss)
fix: crash when debugging inotify (Michael Ploujnikov)
fix: fixed Finished/Retrying error messages being swapped around (Jun Saito)
fix: properly encapsulate filenames on ssh mv commands to avoid shell command injections.
fix: postcmd example (Timo Teräs)
change: closes also on INT signals
change: now removes its pidfile on INT and TERM signals
change: changed build system from autotools to cmake
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
fix: onAttrib is accepted again
fix: -log Exec now prints now fully all arguments
fix: configure script lua detection now includes math lib to workaround
wrongly created "needs COMPAT_ALL" messages.
fix: repaired variable replacement for layer 3 scripts
fix: config.delay is now checked to a number >= 0
change: a2x is no longer checked by configure script.
should not be needed when building from tarball
2012-11-24: 2.1.4
fix: making ssh custom port changes work with ssh and rsync
2012-11-23: 2.1.3
fix: fixed 2 crash conditions due to failure to read 'uSettings'
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
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.
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
enhancement: rsyncOpts has been replaced by rsync = {...} parameter lists
enhancement: default.rsyncssh has now a ssh = {...} parameter similar to default.rsync to
add option to ssh calls. Ditto for xargs = {...}
@ -11,7 +135,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
@ -21,7 +145,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.
@ -52,7 +176,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.
@ -81,14 +205,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)
@ -103,7 +227,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
@ -111,7 +235,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.
@ -119,7 +243,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.
@ -141,7 +265,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 <file-filter/> from config xml
fix: correctly close and free the inotify file descriptor in case of restart
@ -149,7 +273,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
@ -157,7 +281,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
@ -168,22 +292,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
@ -194,5 +318,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

56
INSTALL Normal file
View File

@ -0,0 +1,56 @@
INSTALLING
==========
Prerequisites
-------------
CMake
Lsyncd now uses CMake as configuration tool
Common compiler stuff
The C compiler, make, binutils, etc.
Lua
For building Lsyncd the Lua interpreter 'lua'
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.
Use Lua 5.2 or later.
Note, this has to be exactly the same Version as the
lua compiler used above!
Building
--------
Building with a seperate build directory:
mkdir build
cd build
cmake ..
make
sudo make install
Building intree:
cmake .
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
FIXME make install not yet done

View File

@ -1,60 +0,0 @@
ACLOCAL_AMFLAGS = -I m4
CFLAGS += -Wall $(LUA_CFLAGS)
bin_PROGRAMS = lsyncd
lsyncd_SOURCES = lsyncd.h lsyncd.c lsyncd.lua default-rsync.lua
if INOTIFY
lsyncd_SOURCES += inotify.c
endif
if FSEVENTS
lsyncd_SOURCES += fsevents.c
endif
lsyncd_LDADD = $(LUA_LIBS)
exampledir = $(docdir)/
dist_example_DATA = \
examples/lbash.lua \
examples/lecho.lua \
examples/lgforce.lua \
examples/limagemagic.lua \
examples/lpostcmd.lua \
examples/lrsync.lua \
examples/lrsyncssh.lua
TESTS = \
tests/churn-rsync.lua \
tests/churn-rsyncssh.lua \
tests/churn-direct.lua \
tests/exclude-rsync.lua \
tests/exclude-rsyncssh.lua \
tests/schedule.lua \
tests/l4rsyncdata.lua
dist_man1_MANS = doc/lsyncd.1
EXTRA_DIST = doc/lsyncd.1.txt inotify.c fsevents.c bin2carray.lua \
default.lua default-rsync.lua default-rsyncssh.lua default-direct.lua
doc/lsyncd.1: doc/lsyncd.1.txt
$(A2X) --format=manpage $<
CLEANFILES = runner.out defaults.out runner.c defaults.c
# compiles the runner and the defaults into the binary
lsyncd_LDADD += runner.o defaults.o
runner.o: runner.c
defaults.o: defaults.c
runner.c: runner.out bin2carray.lua
$(LUA) ./bin2carray.lua $< runner $@
defaults.c: defaults.out bin2carray.lua
$(LUA) ./bin2carray.lua $< defaults $@
runner.out: lsyncd.lua
$(LUAC) -o $@ $<
defaults.out: default.lua default-rsync.lua default-rsyncssh.lua default-direct.lua
$(LUAC) -o $@ $^

View File

@ -2,11 +2,13 @@ Lsyncd -- Live Syncing (Mirror) Daemon
======================================
Description
-----------
Lsyncd watches a local directory trees event monitor interface (inotify or fsevents). It aggregates and combines events for a few seconds and then spawns one (or more) process(es) to synchronize the changes. By default this is [rsync](http://rsync.samba.org/). Lsyncd is thus a light-weight live mirror solution that is comparatively easy to install not requiring new filesystems or blockdevices and does not hamper local filesystem performance.
Lsyncd watches a local directory trees event monitor interface (inotify or fsevents). It aggregates and combines events for a few seconds and then spawns one (or more) process(es) to synchronize the changes. By default this is [rsync](http://rsync.samba.org/). Lsyncd is thus a light-weight 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 to act file and directory moves directly on the target instead of retransmitting the move destination over the wire.
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 customizaton 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 simplicity can be balanced with powerfulness. See the manual for details [Lsyncd20Manual](https://github.com/axkibe/lsyncd/wiki/Manual-to-Lsyncd-2.0.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 achieved. See [the manual](https://lsyncd.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.
@ -14,26 +16,41 @@ When to use
-----------
Lsyncd is designed to synchronize a local directory tree with low profile of expected changes to a remote mirror. Lsyncd is especially useful to sync data from a secure area to a not-so-secure area.
2-way/bidirection synchronization
---------------------------------
It is not possible to use lsyncd to synchronize for example `folder1` with `folder2` and vice versa. Only one source to one target. Two way synchronization is a very hard problem that needs specialized tools.
Imagine you start writing a very large file to `folder1`, lsyncd will start synchronizing this file to `folder2`, which might be on a different machine. The lsyncd on that machine will see a new file, and try to synchronize it back to `folder1`. If at the same time, you change bytes in this file, those changes will be overwritten with old data. Using lsyncd in such a way might work in practice, but data corruption is easily possible if you write into files afterwards.
`git-annex` is a good way to do this, if you don't mind working with git repositories. It stores each change as a revision that can be rolled back.
Other synchronization tools
------------------------
[DRBD](http://www.drbd.org) operates on block device level. This makes it useful for synchronizing systems that are under heavy load. Lsyncd on the other hand does not require you to change block devices and/or mount points, allows you to change uid/gid of the transferred files, separates the receiver through the one-way nature of rsync. DRBD is likely the better option if you are syncing Databases.
[DRBD](http://www.drbd.org) operates on block device level. This makes it useful for synchronizing systems that are under heavy load. Lsyncd on the other hand does not require you to change block devices and/or mount points, allows you to change uid/gid of the transferred files, separates the receiver through the one-way nature of rsync. DRBD is likely the better option if you are syncing databases.
[GlusterFS](http://www.gluster.org) and [BindFS](http://www.cs.helsinki.fi/u/partel/bindfs/) use a FUSE-Filesystem to interject kernel/userspace filesystem events.
[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.
[git-annex](https://git-annex.branchable.com/) allows managing large files with git, without storing the file contents in git. It can sync, backup, and archive your data, offline and online. Checksums and encryption keep your data safe and secure. Bring the power and distributed nature of git to bear on your large files with git-annex.
git-annex is designed for git users who love the command line. For everyone else, the git-annex assistant turns git-annex into an easy to use folder synchroniser.
[Wikipedia](https://en.wikipedia.org/wiki/Comparison_of_file_synchronization_software) Comparison of file synchronization software
Lsyncd usage examples
---------------------
```lsyncd -rsync /home remotehost.org::share/```
This watches and rsycn´s the local directory /home with all subdirectories and
This watches and rsyncs the local directory /home with all sub-directories and
transfers them to 'remotehost' using the rsync-share 'share'.
```lsyncd -rsyncssh /home remotehost.org backup-home/```
This will also rsync/watch '/home', but it uses a ssh connection to make moves local on the remotehost instead of retransmitting the moved file over the wire.
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 [Lsyncd20Manual](https://github.com/axkibe/lsyncd/wiki/Manual-to-Lsyncd-2.0.x).
Some more complicated examples, tips and tricks you can find in the [manual](https://lsyncd.github.io/lsyncd/).
Disclaimer
----------
Besides the usual disclaimer in the license, we want to specifically emphasize that neither the authors nor any organization the authors are associated with can and will hold responsible for data-loss caused by possible malfunctions of Lsyncd.
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.

View File

@ -1,9 +0,0 @@
#!/bin/sh
echo "Generating configure files... may take a while."
autoreconf --install --force && \
echo "Preparing was successful if there was no error messages above." && \
echo "Now type:" && \
echo " ./configure && make" && \
echo "Run './configure --help' for more information"

129
cmake/FindLua.cmake Normal file
View File

@ -0,0 +1,129 @@
# Locate Lua library
# This module defines
# LUA_EXECUTABLE, if found
# 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)
#
# Note that the expected include convention is
# #include "lua.h"
# and not
# #include <lua/lua.h>
# This is because, the lua location is not standardized and may exist
# in locations other than lua/
#=============================================================================
# Copyright 2007-2009 Kitware, Inc.
# Modified to support Lua 5.2 by LuaDist 2012
# Modified to support Lua 5.4 by LuaDist 2022
#
# Distributed under the OSI-approved BSD License (the "License");
# see accompanying file Copyright.txt for details.
#
# This software is distributed WITHOUT ANY WARRANTY; without even the
# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
# See the License for more information.
#=============================================================================
# (To distribute this file outside of CMake, substitute the full
# License text for the above reference.)
#
# This module will try to find the newest Lua version down to 5.4
# Always search for non-versioned lua first (recommended)
SET(_POSSIBLE_LUA_INCLUDE include include/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)
SET(_POSSIBLE_SUFFIXES "54" "5.4" "-5.4" "53" "5.3" "-5.3" "52" "5.2" "-5.2" "")
# Set up possible search names and locations
FOREACH(_SUFFIX IN LISTS _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)
# Find the lua executable
FIND_PROGRAM(LUA_EXECUTABLE
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
$ENV{LUA_DIR}
PATH_SUFFIXES ${_POSSIBLE_LUA_INCLUDE}
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw # Fink
/opt/local # DarwinPorts
/opt/csw # Blastwave
/opt
)
# Find the lua library
FIND_LIBRARY(LUA_LIBRARY
NAMES ${_POSSIBLE_LUA_LIBRARY}
HINTS
$ENV{LUA_DIR}
PATH_SUFFIXES lib64 lib
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/sw
/opt/local
/opt/csw
/opt
)
IF(LUA_LIBRARY)
# include the math library for Unix
IF(UNIX AND NOT APPLE)
FIND_LIBRARY(LUA_MATH_LIBRARY m)
SET( LUA_LIBRARIES "${LUA_LIBRARY};${LUA_MATH_LIBRARY}" CACHE STRING "Lua Libraries")
# For Windows and Mac, don't need to explicitly include the math library
ELSE(UNIX AND NOT APPLE)
SET( LUA_LIBRARIES "${LUA_LIBRARY}" CACHE STRING "Lua Libraries")
ENDIF(UNIX AND NOT APPLE)
ENDIF(LUA_LIBRARY)
# Determine Lua version
IF(LUA_INCLUDE_DIR AND EXISTS "${LUA_INCLUDE_DIR}/lua.h")
FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_major_str REGEX "^#define[ \t]+LUA_VERSION_MAJOR[ \t]+\".+\"")
FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_minor_str REGEX "^#define[ \t]+LUA_VERSION_MINOR[ \t]+\".+\"")
FILE(STRINGS "${LUA_INCLUDE_DIR}/lua.h" lua_version_release_str REGEX "^#define[ \t]+LUA_VERSION_RELEASE[ \t]+\".+\"")
STRING(REGEX REPLACE "^#define[ \t]+LUA_VERSION_MAJOR[ \t]+\"([^\"]+)\".*" "\\1" LUA_VERSION_MAJOR "${lua_version_major_str}")
STRING(REGEX REPLACE "^#define[ \t]+LUA_VERSION_MINOR[ \t]+\"([^\"]+)\".*" "\\1" LUA_VERSION_MINOR "${lua_version_minor_str}")
STRING(REGEX REPLACE "^#define[ \t]+LUA_VERSION_RELEASE[ \t]+\"([^\"]+)\".*" "\\1" LUA_VERSION_RELEASE "${lua_version_release_str}")
STRING(CONCAT LUA_VERSION_STRING ${LUA_VERSION_MAJOR} "." ${LUA_VERSION_MINOR} "." ${LUA_VERSION_RELEASE})
UNSET(lua_version_major_str)
UNSET(lua_version_minor_str)
UNSET(lua_version_release_str)
ENDIF()
INCLUDE(FindPackageHandleStandardArgs)
# 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 LUA_COMPILER)

9
config.h.in Normal file
View File

@ -0,0 +1,9 @@
/* Lsyncd Version */
#define PACKAGE_VERSION "@LSYNCD_VERSION@"
/* File event notification mechanims available */
#cmakedefine WITH_INOTIFY 1
#cmakedefine WITH_FSEVENTS 1
/* OS */
#cmakedefine LSYNCD_TARGET_APPLE 1

View File

@ -1,139 +0,0 @@
# -*- Autoconf -*-
# Process this file with autoconf to produce a configure script.
#AC_PREREQ(2.60)
AC_INIT(lsyncd, 2.1.0, axkibe@gmail.com)
AM_INIT_AUTOMAKE([foreign])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR([lsyncd.c])
AC_CONFIG_HEADER([config.h])
###
# Checks for programs.
AC_PROG_CC
AC_PROG_INSTALL
AC_PROG_MAKE_SET
AC_PATH_PROG([A2X], [a2x], [no])
if test x${A2X} = xno ; then
AC_MSG_ERROR([Program 'a2x' (package asciidoc) is required])
fi
###
# Checks for Lua
# Try versioned Lua 5.2 first
PKG_CHECK_MODULES([LUA52], [lua5.2],,[
PKG_CHECK_MODULES([LUA52], [lua52],,[
PKG_CHECK_MODULES([LUA52], [lua-5.2],,[:])
])
])
AC_PATH_PROGS([LUA52], [lua5.2 lua52], [no])
AC_PATH_PROGS([LUAC52], [luac5.2 luac52], [no])
if test -z "${LUA52_PKG_ERRORS}" -a "${LUA52}" != no -a "${LUAC52}" != no ; then
LUA_VERSION="5.2"
LUA_CFLAGS="${LUA52_CFLAGS}"
LUA_LIBS="${LUA52_LIBS}"
LUA="${LUA52}"
LUAC="${LUAC52}"
else
# Fall back to versioned Lua 5.1
PKG_CHECK_MODULES([LUA51], [lua5.1 >= 5.1.3],,[
PKG_CHECK_MODULES([LUA51], [lua51 >= 5.1.3],,[
PKG_CHECK_MODULES([LUA51], [lua-5.1 >= 5.1.3],,[:])
])
])
AC_PATH_PROGS([LUA51], [lua5.1 lua51], [no])
AC_PATH_PROGS([LUAC51], [luac5.1 luac51], [no])
if test -z "${LUA51_PKG_ERRORS}" -a "${LUA51}" != no -a "${LUAC51}" != no ; then
LUA_VERSION="5.1"
LUA_CFLAGS="${LUA51_CFLAGS}"
LUA_LIBS="${LUA51_LIBS}"
LUA="${LUA51}"
LUAC="${LUAC51}"
else
# Try any Lua now
PKG_CHECK_MODULES([LUA], [lua >= 5.1.3],,[:])
AC_PATH_PROG([LUA], [lua], [no])
AC_PATH_PROG([LUAC], [luac], [no])
if test -z "${LUA_PKG_ERRORS}" -a "${LUA}" != no -a "${LUAC}" != no ; then
LUA_VERSION="(unknown version)"
else
AC_MSG_ERROR([Need a Lua toolchain with matching versions ('lua' library and 'lua' and 'luac' programs)])
fi
fi
fi
_LIBS="${LIBS}"
_CFLAGS="${CFLAGS}"
_CPPFLAGS="${CPPFLAGS}"
LIBS="${LIBS} ${LUA_LIBS}"
CFLAGS="${CFLAGS} ${LUA_CFLAGS}"
CPPFLAGS="${CPPFLAGS} ${LUA_CFLAGS}"
AC_MSG_CHECKING([whether Lua library was compiled with compat support])
AC_LINK_IFELSE(
[AC_LANG_PROGRAM([
#define LUA_COMPAT_ALL
#include <lauxlib.h>
],[luaL_register(0,0,0);])],
[lua_compat_support=yes],
[lua_compat_support=no]
)
AC_MSG_RESULT([${lua_compat_support}])
if test "x${lua_compat_support}" = xno ; then
AC_MSG_ERROR([Lua library needs to be compiled with compat support])
fi
LIBS="${_LIBS}"
CFLAGS="${_CFLAGS}"
CPPFLAGS="${_CPPFLAGS}"
unset _LIBS _CFLAGS _CPPFLAGS
AX_SUBST_L([LUA_CFLAGS], [LUA_LIBS], [LUA], [LUAC])
###
# Checks for header files.
AC_CHECK_HEADERS([sys/inotify.h])
###
# --without-inotify option
AC_ARG_WITH([inotify],
[ --without-inotify Do not use Linux inotify event interface. On by default.],
[],[with_inotify=yes])
if test "x${with_inotify}" == xyes; then
echo "compiling with inotify"
AC_DEFINE(LSYNCD_WITH_INOTIFY,,"descr")
else
echo "compiling without inotify"
fi
AM_CONDITIONAL([INOTIFY], [test x${with_inotify} != xno])
###
# --with-fsevents
# disabled per default, experimental, works only with OS X 10.5/10.6
AC_ARG_WITH([fsevents],
[ --with-fsevents Uses MacOS (10.5) /dev/fsevents. EXPERIMENTAL!
Off by default.])
if test "x${with_fsevents}" == xyes; then
echo "compiling with fsevents. WARNING experimental!"
AC_DEFINE(LSYNCD_WITH_FSEVENTS,,"descr")
fi
AM_CONDITIONAL([FSEVENTS],
[test x${with_fsevents} != x -a xno${with_fsevents} != xno])
# Checks for typedefs, structures, and compiler characteristics.
# Checks for library functions.
AC_CONFIG_FILES([Makefile])
AC_OUTPUT
AC_MSG_NOTICE([
Summary:
Using Lua ${LUA_VERSION}
])

View File

@ -1,7 +1,7 @@
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- default-direct.lua
--
-- Keeps two directories with /bin/cp, /bin/rm and /bin/mv in sync.
-- Keeps two directories with cp, rm and mv in sync.
-- Startup still uses rsync tough.
--
-- A (Layer 1) configuration.
@ -61,14 +61,17 @@ direct.action = function(inlet)
if event.isdir then
spawn(
event,
'/bin/mkdir',
'mkdir',
'--',
event.targetPath
)
else
-- 'cp -t', not supported on OSX
spawn(
event,
'/bin/cp',
'cp',
'-p',
'--',
event.sourcePath,
event.targetPathdir
)
@ -78,7 +81,9 @@ direct.action = function(inlet)
error("Do not know how to handle 'Modify' on dirs")
end
spawn(event,
'/bin/cp',
'cp',
'-p',
'--',
event.sourcePath,
event.targetPathdir
)
@ -99,7 +104,7 @@ direct.action = function(inlet)
error('Refusing to erase your harddisk!')
end
spawn(event, '/bin/rm', '-rf', tp)
spawn(event, 'rm', '-rf', '--', tp)
elseif event.etype == 'Move' then
local tp = event.targetPath
@ -109,13 +114,13 @@ direct.action = function(inlet)
error('Refusing to erase your harddisk!')
end
local command = '/bin/mv $1 $2 || /bin/rm -rf $1'
local command = 'mv -- "$1" "$2" || rm -rf -- "$1"'
if
config.delete ~= true and
config.delete ~= 'running'
then
command = '/bin/mv $1 $2'
command = 'mv -- "$1" "$2"'
end
spawnShell(
@ -124,7 +129,16 @@ direct.action = function(inlet)
event.targetPath,
event2.targetPath
)
elseif event.etype == 'Full' then
local tp = event.targetPath
-- extra security check
if tp == '' or tp == '/' or not tp then
error('Refusing to erase your harddisk!')
end
-- trigger full sync function
direct.full(event)
else
log('Warn', 'ignored an event of type "',event.etype, '"')
inlet.discardEvent(event)
@ -142,8 +156,10 @@ direct.collect = function(agent, exitcode)
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 "',
@ -170,6 +186,12 @@ end
--
direct.init = default.rsync.init
--
-- Spawns the recursive startup sync
-- (currently) identical to default rsync.
--
direct.full = default.rsync.full
--
-- Checks the configuration.
--

View File

@ -16,17 +16,13 @@
--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
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 = { }
default.rsync = rsync
-- uses default collect
@ -37,79 +33,95 @@ default.rsync = rsync
rsync.checkgauge = {
-- unsets default user action handlers
onCreate = false,
onModify = false,
onDelete = false,
onStartup = false,
onMove = false,
onCreate = false,
onModify = false,
onDelete = false,
onStartup = false,
onMove = false,
delete = true,
exclude = true,
target = true,
delete = true,
exclude = true,
excludeFrom = true,
filter = true,
filterFrom = true,
target = true,
batchSizeLimit = true,
rsync = {
-- rsync binary
binary = true,
-- rsync shortflags
verbose = true,
quiet = true,
checksum = true,
update = true,
links = true,
copy_links = true,
hard_links = true,
perms = true,
executability = true,
acls = true,
xattrs = true,
owner = true,
group = true,
times = true,
sparse = true,
dry_run = true,
whole_file = true,
one_file_system = true,
prune_empty_dirs = true,
ignore_times = 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,
copy_unsafe_links = true,
cvs_exclude = true,
protect_args = true,
delete_excluded = true,
dry_run = true,
executability = true,
existing = true,
group = true,
groupmap = true,
hard_links = true,
ignore_times = true,
inplace = true,
ipv4 = true,
ipv6 = true,
-- further rsync options
keep_dirlinks = true,
links = true,
one_file_system = true,
omit_dir_times = true,
omit_link_times = true,
owner = true,
password_file = true,
perms = true,
protect_args = true,
prune_empty_dirs = true,
quiet = true,
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,
_extra = true,
},
}
--
-- Spawns rsync for a list of events
--
-- Exlcusions are already handled by not having
-- events for them.
--
rsync.action = function( inlet )
--
-- gets all events ready for syncing
--
local elist = inlet.getEvents(
function(event)
return event.etype ~= 'Init' and event.etype ~= 'Blanket'
end
-- internal function to actually do the transfer
local run_action = function
(
inlet,
elist
)
local config = inlet.getConfig( )
local substitudes = inlet.getSubstitutionData(elist, {})
local target = substitudeCommands(config.target, substitudes)
--
-- 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( '%?', '\\?' ):
@ -124,8 +136,14 @@ rsync.action = function( inlet )
-- 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 )
@ -133,74 +151,65 @@ rsync.action = function( inlet )
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,
@ -209,40 +218,246 @@ rsync.action = function( inlet )
'--include-from=-',
'--exclude=*',
config.source,
config.target
target
)
end
--
-- Spawns the recursive startup sync
-- Returns true for non Init and Blanket events.
--
rsync.init = function(event)
local eventNotInitBlank =
function
(
event
)
return event.etype ~= 'Init' and event.etype ~= 'Blanket'
end
--
-- Returns size or true if the event is for batch processing
--
local getBatchSize =
function
(
event
)
-- print("getBatchSize", event, event.status, event.etype, event.pathname)
if event.status == 'active' then
return false
end
if event.etype == 'Init' or event.etype == 'Blanket' or event.etype == 'Full' then
return false
end
-- moves and deletes go always into batch
if event.etype == 'Move' or event.etype == 'Delete' then
return true
end
return lsyncd.get_file_size(event.sourcePath)
end
--
-- Spawns rsync for a list of events
--
-- Exclusions are already handled by not having
-- events for them.
--
rsync.action = function
(
inlet
)
local sizeLimit = inlet.getConfig().batchSizeLimit
if sizeLimit == nil then
-- gets all events ready for syncing
return run_action(inlet, inlet.getEvents(eventNotInitBlank))
else
-- spawn all files under the size limit/deletes/moves in batch mode
local eventInBatch = function(event)
if event.etype == "Full" then
return false
end
local size = getBatchSize(event)
if type(size) == "boolean" then
return size
elseif size == nil then
return true
end
if size <= sizeLimit then
return true
end
return false
end
-- indicator for grabbing one element of the queue
local single_returned = false
-- grab all events for seperate transfers
local eventNoBatch = function(event)
if event.etype == "Full" then
return false
end
local size = getBatchSize(event)
if type(size) ~= "number" or size == nil then
return false
end
if single_returned then
return 'break'
end
if size > sizeLimit then
single_returned = true
return true
end
return false
end
local extralist = inlet.getEvents(eventInBatch)
-- get all batched events
if extralist.size() > 0 then
run_action(inlet, extralist)
end
while true do
local cnt, maxcnt = lsyncd.get_process_info()
if inlet.getSync().processes:size( ) >= inlet.getConfig().maxProcesses then
log('Normal',
'Maximum processes for sync reached. Delaying large transfer for sync: '..inlet.getConfig().name)
break
elseif maxcnt and cnt >= maxcnt then
log('Normal',
'Maximum process count reached. Delaying large transfer for sync: '..inlet.getConfig().name)
break
end
local extralist = inlet.getEvents(eventNoBatch)
-- no more single size events
if extralist.size() == 0 then break end
run_action(inlet, extralist)
-- get next result
single_returned = false
end
end
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
--
-- log(
-- 'Normal',
-- 'Calling rsync with filter-list of new/modified files/dirs\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.target
-- )
--end
--
-- Spawns the recursive startup sync.
--
rsync.init = function
(
event
)
return rsync.full(event)
end
--
-- Triggers a full sync event
--
rsync.full = function
(
event
)
local config = event.config
local inlet = event.inlet
local excludes = inlet.getExcludes( )
local delete = nil
local filters = inlet.hasFilters( ) and inlet.getFilters( )
local delete = {}
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
local substitudes = inlet.getSubstitutionData(event, {})
target = substitudeCommands(target, substitudes)
if config.delete == true
or config.delete == 'startup'
then
delete = { '--delete', '--ignore-errors' }
end
if #excludes == 0 then
-- start rsync without any excludes
if config.rsync.delete_excluded == true
then
table.insert( delete, '--delete-excluded' )
end
if not filters and #excludes == 0
then
-- starts rsync without any filters or excludes
log(
'Normal',
'recursive startup rsync: ',
'recursive full rsync: ',
config.source,
' -> ',
target
@ -258,14 +473,15 @@ rsync.init = function(event)
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' )
log(
'Normal',
'recursive startup rsync: ',
'recursive full rsync: ',
config.source,
' -> ',
target,
@ -284,6 +500,32 @@ rsync.init = function(event)
config.source,
target
)
else
-- starts rsync providing a filter list
-- on stdin
local fS = table.concat( filters, '\n' )
log(
'Normal',
'recursive full rsync: ',
config.source,
' -> ',
target,
' filtering\n',
fS
)
spawn(
event,
config.rsync.binary,
'<', fS,
'--filter=. -',
delete,
config.rsync._computed,
'-r',
config.source,
target
)
end
end
@ -291,85 +533,37 @@ end
--
-- Prepares and checks a syncs configuration on startup.
--
rsync.prepare = function(
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 )
if not skipTarget and not config.target then
if not skipTarget and not config.target
then
error(
'default.rsync needs "target" configured',
level
)
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
if config.rsync._computed
then
error(
'please do not use the internal rsync._computed parameter',
level
)
end
--
-- computes the rsync arguments into one list
--
local crsync = config.rsync;
--
-- everything implied by archive = true
--
local archiveFlags = {
recursive = true,
links = true,
@ -384,108 +578,214 @@ rsync.prepare = function(
xattrs = false,
}
--
-- if archive given the implications are filled in
--
if crsync.archive then
for k, v in pairs( archiveFlags ) do
if crsync[ k ] == nil then
-- if archive is given the implications are filled in
if crsync.archive
then
for k, v in pairs( archiveFlags )
do
if crsync[ k ] == nil
then
crsync[ k ] = v
end
end
end
crsync._computed = { true }
--- @type any
local computed = crsync._computed
local computedN = 1
local computedN = 2
local shortFlags = {
verbose = 'v',
quiet = 'q',
checksum = 'c',
update = 'u',
links = 'l',
copy_links = 'L',
hard_links = 'H',
perms = 'p',
executability = 'E',
acls = 'A',
xattrs = 'X',
owner = 'o',
group = 'g',
times = 't',
sparse = 'S',
dry_run = 'n',
whole_file = 'W',
one_file_system = 'x',
prune_empty_dirs = 'm',
ignore_times = 'I',
backup = 'b',
checksum = 'c',
compress = 'z',
copy_dirlinks = 'k',
copy_links = 'L',
cvs_exclude = 'C',
protect_args = 's',
dry_run = 'n',
executability = 'E',
group = 'g',
hard_links = 'H',
ignore_times = 'I',
ipv4 = '4',
ipv6 = '6'
ipv6 = '6',
keep_dirlinks = 'K',
links = 'l',
one_file_system = 'x',
omit_dir_times = 'O',
omit_link_times = 'J',
owner = 'o',
perms = 'p',
protect_args = 's',
prune_empty_dirs = 'm',
quiet = 'q',
sparse = 'S',
times = 't',
update = 'u',
verbose = 'v',
whole_file = 'W',
xattrs = 'X',
}
local shorts = { '-' }
local shortsN = 2
if crsync._extra then
for k, v in ipairs( crsync._extra ) do
if crsync._extra
then
for k, v in ipairs( crsync._extra )
do
computed[ computedN ] = v
computedN = computedN + 1
end
end
for k, flag in pairs( shortFlags ) do
if crsync[ k ] then
for k, flag in pairs( shortFlags )
do
if crsync[ k ]
then
shorts[ shortsN ] = flag
shortsN = shortsN + 1
end
end
if crsync.devices and crsync.specials then
if crsync.devices and crsync.specials
then
shorts[ shortsN ] = 'D'
shortsN = shortsN + 1
else
if crsync.devices then
if crsync.devices
then
computed[ computedN ] = '--devices'
computedN = computedN + 1
end
if crsync.specials then
if crsync.specials
then
computed[ computedN ] = '--specials'
computedN = computedN + 1
end
end
if crsync.rsh then
computed[ computedN ] = '--rsh=' + crsync.rsh
if crsync.append
then
computed[ computedN ] = '--append'
computedN = computedN + 1
end
if crsync.rsync_path then
computed[ computedN ] = '--rsync-path=' + crsync.rsync_path
if crsync.append_verify
then
computed[ computedN ] = '--append-verify'
computedN = computedN + 1
end
if crsync.temp_dir then
computed[ computedN ] = '--temp-dir=' + crsync.temp_dir
if crsync.backup_dir
then
computed[ computedN ] = '--backup-dir=' .. crsync.backup_dir
computedN = computedN + 1
end
if shortsN ~= 2 then
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.copy_unsafe_links
then
computed[ computedN ] = '--copy-unsafe-links'
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'
computedN = computedN + 1
end
if crsync.password_file
then
computed[ computedN ] = '--password-file=' .. crsync.password_file
computedN = computedN + 1
end
if crsync.rsh
then
computed[ computedN ] = '--rsh=' .. crsync.rsh
computedN = computedN + 1
end
if crsync.rsync_path
then
computed[ computedN ] = '--rsync-path=' .. crsync.rsync_path
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
computedN = computedN + 1
end
if crsync.timeout
then
computed[ computedN ] = '--timeout=' .. crsync.timeout
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, '' )
else
computed[ 1 ] = { }
end
-- appends a / to target if not present
if not skipTarget and string.sub(config.target, -1) ~= '/' then
-- 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
@ -502,9 +802,10 @@ rsync.exitcodes = default.rsyncExitCodes
--
-- Calls rsync with this default options
--
rsync.rsync = {
rsync.rsync =
{
-- The rsync binary to be called.
binary = '/usr/bin/rsync',
binary = 'rsync',
links = true,
times = true,
protect_args = true

View File

@ -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 @@ end
-- rsyncssh extends default.rsync
--
local rsyncssh = { default.rsync }
default.rsyncssh = rsyncssh
--
@ -50,31 +54,118 @@ rsyncssh.checkgauge = {
-- ssh settings
ssh = {
binary = true,
port = true,
_extra = true
binary = true,
identityFile = true,
options = true,
port = true,
_extra = true
},
-- xargs settings
xargs = {
binary = true,
delimiter = true,
_extra = true
}
}
--
-- Returns true for non Init, Blanket and Move events.
--
local eventNotInitBlankMove =
function
(
event
)
-- TODO use a table
if event.etype == 'Move'
or event.etype == 'Init'
or event.etype == 'Blanket'
then
return 'break'
else
return true
end
end
--
-- Replaces what rsync would consider filter rules by literals.
--
local replaceRsyncFilter =
function
(
path
)
if not path then return end
return(
path
:gsub( '%?', '\\?' )
:gsub( '%*', '\\*' )
:gsub( '%[', '\\[' )
)
end
---
--- Check if _extra arguments are of correct form
---
local checkSSH = function (args)
local SINGLES = "46AaCfGgKkMNnqsTtVvXxYy"
local needs_more = true
local is_data = false
local rv = true
for index, value in ipairs(args) do
if is_data == false then
if string.sub(value, 1, 1) ~= "-" then
log('Warn', "_extra argument does not start with -")
rv = false
end
needs_more = true
end
for i = 1, #SINGLES do
if string.sub(SINGLES, i, i) == string.sub(value, 2, 2) then
needs_more = false
break
end
end
if needs_more == false then
is_data = false
else
is_data = true
end
end
if needs_more == true then
log('Warn', "passend argument requires more arguments")
rv = false
end
return rv
end
--
-- Spawns rsync for a list of events
--
rsyncssh.action = function( inlet )
rsyncssh.action = function
(
inlet
)
local config = inlet.getConfig( )
local event, event2 = inlet.getEvent()
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
log('Normal', 'Moving ',event.path,' -> ',event2.path)
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,
@ -82,173 +173,292 @@ rsyncssh.action = function( inlet )
config.ssh._computed,
config.host,
'mv',
'\"' .. config.targetdir .. event.path .. '\"',
'\"' .. config.targetdir .. event2.path .. '\"',
path1,
path2,
'||', 'rm', '-rf',
'\"' .. config.targetdir .. event.path .. '\"')
return
end
-- uses ssh to delete files on remote host
-- instead of constructing rsync filters
if event.etype == 'Delete' then
if
config.delete ~= true and
config.delete ~= 'running'
then
inlet.discardEvent(event)
return
end
-- gets all other deletes ready to be
-- executed
local elist = inlet.getEvents(
function( e )
return e.etype == 'Delete'
end
)
-- returns the paths of the delete list
local paths = elist.getPaths(
function( etype, path1, path2 )
if path2 then
return config.targetdir..path1, config.targetdir..path2
else
return config.targetdir..path1
end
end
)
-- ensures none of the paths is '/'
for _, v in pairs( paths ) do
if string.match(v, '^%s*/+%s*$') then
log('Error', 'refusing to `rm -rf /` the target!')
terminate(-1) -- ERRNO
end
end
log(
'Normal',
'Deleting list\n',
table.concat( paths, '\n' )
)
local params = { }
spawn(
elist,
config.ssh.binary,
'<', table.concat(paths, config.xargs.delimiter),
params,
config.ssh._computed,
config.host,
config.xargs.binary,
config.xargs._extra
path1
)
return
end
--
-- for everything else a rsync is spawned
--
local elist = inlet.getEvents(
function(e)
-- TODO use a table
return e.etype ~= 'Move' and
e.etype ~= 'Delete' and
e.etype ~= 'Init' and
e.etype ~= 'Blanket'
end
)
-- 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.
-- Replaces what rsync would consider filter rules by literals
--
for k, v in ipairs( paths ) do
if string.byte(v, -1) == 47 then
paths[k] = string.sub(v, 1, -2)
local function sub( p )
if not p then return end
return p:
gsub( '%?', '\\?' ):
gsub( '%*', '\\*' ):
gsub( '%[', '\\[' ):
gsub( '%]', '\\]' )
end
--
-- 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
)
-- stores all filters by integer index
local filterI = { }
-- stores all filters with path index
local filterP = { }
-- 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.
--
-- 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 pp = string.match( path, '^(.*/)[^/]+/?' )
while pp
do
addToFilter(pp)
pp = string.match( pp, '^(.*/)[^/]+/?' )
end
end
end
local sPaths = table.concat(paths, '\n')
local zPaths = table.concat(paths, '\000')
log(
'Normal',
'Calling rsync with filter-list of new/modified files/dirs\n',
table.concat( filterI, '\n' )
)
log('Normal', 'Rsyncing list\n', sPaths)
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
-----
----
---- 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.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 )
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 uSettings.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
@ -260,54 +470,146 @@ end
--
-- 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.rsync;
if config.maxProcesses ~= 1
then
error( 'default.rsyncssh must have maxProcesses set to 1.', level )
end
local cssh = config.ssh;
cssh._computed = { }
local computed = cssh._computed
local computedN = 1
if cssh._extra then
for k, v in ipairs( cssh._extra ) do
computed[ computedN ] = v
computedN = computedN + 1
local rsyncc = config.rsync._computed
if cssh.identityFile
then
computed[ computedN ] = '-i'
computed[ computedN + 1 ] = cssh.identityFile
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 ] ..
' -i ' ..
cssh.identityFile
end
if cssh.options
then
for k, v in pairs( cssh.options )
do
computed[ computedN ] = '-o'
computed[ computedN + 1 ] = k .. '=' .. v
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 ] =
table.concat(
{
rsyncc[ config.rsync._rshIndex ],
' -o ',
k,
'=',
v
},
''
)
end
end
if cssh.port then
computed[ computedN ] = '-p'
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
rsyncc[ config.rsync._rshIndex ] = '--rsh=ssh'
end
rsyncc[ config.rsync._rshIndex ] =
rsyncc[ config.rsync._rshIndex ] .. ' -p ' .. cssh.port
end
if cssh._extra
then
if checkSSH(cssh._extra) == false then
log( 'Warn', 'The ssh._extra parameter is a list of arguments, ensure it is written correctly')
end
for k, v in ipairs( cssh._extra )
do
computed[ computedN ] = v
computedN = computedN + 1
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 ] .. ' ' .. v
end
end
-- appends a slash to the targetdir if missing
if string.sub(config.targetdir, -1) ~= '/' then
-- and is not ':' for home dir
if string.sub( config.targetdir, -1 ) ~= '/'
and string.sub( config.targetdir, -1 ) ~= ':'
then
config.targetdir = config.targetdir .. '/'
end
@ -344,27 +646,6 @@ rsyncssh.rsyncExitCodes = default.rsyncExitCodes
--
rsyncssh.sshExitCodes = default.sshExitCodes
--
-- xargs calls configuration
--
-- xargs is used to delete multiple remote files, when ssh access is
-- available this is simpler than to build filters for rsync for this.
--
rsyncssh.xargs = {
--
-- the binary called (on target host)
binary = '/usr/bin/xargs',
--
-- delimiter, uses null by default, you might want to override this for older
-- by for example '\n'
delimiter = '\000',
--
-- extra parameters
_extra = { '-0', 'rm -rf' }
}
--
-- ssh calls configuration
@ -376,7 +657,17 @@ rsyncssh.ssh = {
--
-- the binary called
--
binary = '/usr/bin/ssh',
binary = 'ssh',
--
-- if set adds this key to ssh
--
identityFile = nil,
--
-- if set adds this special options to ssh
--
options = nil,
--
-- if set connect to this port

View File

@ -8,10 +8,12 @@
-- Authors: Axel Kittenberger <axkibe@gmail.com>
--============================================================================
if default then
if default
then
error( 'default already loaded' )
end
--- @diagnostic disable-next-line: lowercase-global
default = { }
@ -34,42 +36,52 @@ default._merge = {
-- used to ensure there aren't typos in the keys
--
default.checkgauge = {
action = true,
checkgauge = true,
collect = true,
delay = true,
exitcodes = true,
init = true,
maxDelays = true,
maxProcesses = true,
onCreate = true,
onModify = true,
onDelete = true,
onStartup = true,
onMove = true,
prepare = true,
source = true,
target = true,
action = true,
checkgauge = true,
collect = true,
crontab = true,
delay = true,
exitcodes = true,
init = true,
full = true,
maxDelays = true,
maxProcesses = true,
onAttrib = true,
onCreate = true,
onModify = true,
onDelete = true,
onStartup = true,
onMove = true,
onFull = true,
prepare = true,
source = true,
target = true,
tunnel = true,
}
--
-- 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
@ -81,31 +93,58 @@ end
--
-- 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.'
)
if settings('onepass')
then
log(
'Normal',
'onepass config set, exiting'
)
terminate( 0 )
end
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
)
@ -113,19 +152,24 @@ default.collect = function( agent, exitcode )
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 )
@ -134,31 +178,37 @@ default.collect = function( agent, exitcode )
'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
@ -170,15 +220,52 @@ default.collect = function( agent, exitcode )
rc = 'die'
end
else
if rc == 'ok' then
log('Normal', 'Retrying ',agent.etype,' on ',agent.sourcePath,' = ',exitcode)
elseif rc == 'again' then
log('Normal', 'Finished ',agent.etype,' on ',agent.sourcePath,' = ',exitcode)
elseif rc == 'die' then
log('Error', 'Failure with ',agent.etype,' on ',agent.sourcePath,' = ',exitcode)
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
@ -191,29 +278,35 @@ end
-- 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
@ -272,8 +365,8 @@ default.rsyncExitCodes = {
--
-- 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
@ -290,46 +383,40 @@ default.sshExitCodes = {
--
-- 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(
@ -338,18 +425,20 @@ local function check(
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

3
distclean.sh Executable file
View File

@ -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

1
docs/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
_site/

4
docs/_config.yml Normal file
View File

@ -0,0 +1,4 @@
name: Lsyncd
markdown: kramdown
url : /lsyncd
baseurl : /lsyncd

19
docs/_data/docs.yml Normal file
View File

@ -0,0 +1,19 @@
- title: Lsyncd
docs:
- /
- /download
- /help
- title: Manual
docs:
- /manual/building
- /manual/invoking
- /manual/config/file
- /manual/config/layer4
- /manual/config/layer3
- /manual/config/layer2
- /manual/config/layer1
- /manual/examples
- title: Appendices
docs:
- /faq
- /appendices/devdocs

View File

@ -0,0 +1,93 @@
{% if jekyll.environment == "local"
%}{% assign suffix = 'index.html'
%}{% else
%}{% assign suffix = ''
%}{% endif
%}{% assign base = ''
%}{% assign depth = page.url | split: '/' | size
%}{% if depth <= 1
%}{% assign base = '.'
%}{% elsif depth == 2
%}{% assign base = '..'
%}{% elsif depth == 3
%}{% assign base = '../..'
%}{% elsif depth == 4
%}{% assign base = '../../..'
%}{% endif
%}<!DOCTYPE html>
<html>
<head>
{% if page.url != "/" %}
<title>Lsyncd - {{ page.title }}</title>
{% else %}
<title>{{ page.title }}</title>
{% endif %}
<!-- link to main stylesheet -->
<link rel="stylesheet" type="text/css" href="{{ base }}/css/main.css">
<link rel="stylesheet" type="text/css" href="{{ base }}/css/syntax.css">
</head>
<body>
<div id="pillar">
<nav>
<div id="menu">
{% for section in site.data.docs %}
<h4>{{section.title}}</h4>
<ul>
{% for doc in section.docs %}
{% assign doc_url = doc %}
{% if doc_url != "/" %}
{% assign doc_url = doc | append: "/" %}
{% endif %}
{% assign p = site.pages | where:"url", doc_url | first %}
{% if page.tab %}
{% assign tab = page.tab | prepend:"/" | append:"/" %}
{% endif %}
{% if p.short %}
{% assign title = p.short %}
{% else %}
{% assign title = p.title %}
{% endif %}
<li><a {% if doc_url == page.url or doc_url == tab %}class="current"{% endif %} href="{{ base| append: p.url | append: suffix }}">{{ title }}</a></li>
{% endfor %}
</ul>
{% endfor %}
<div id="menufooter"></div>
</div>
<div id="ribbonwrap"><a href="https://github.com/lsyncd/lsyncd" id="ribbon"></a></div>
</nav>
<div id="container">
<a href="https://github.com/lsyncd/lsyncd/edit/master/docs{{ page.url }}index.md" id="improvethis"></a>
<h1>{{ page.title }}</h1>
{{ content }}
{% assign docs = site.data.docs | map: 'docs' | join: ',' | split: ',' %}
{% for doc in docs %}
{% assign doc_url = doc %}
{% if doc_url != "/" %}
{% assign doc_url = doc_url | append: "/" %}
{% endif %}
{% if doc_url == page.url %}
<div id="footnav" class="prenext">
{% if forloop.first %}
{% else %}
{% assign previous = forloop.index0 | minus: 1 %}
{% assign previous_page = docs[previous] %}
<a href="{{ site.url | append: previous_page }}" class="prev"></a>
{% endif %}
{% if forloop.last %}
{% else %}
{% assign next = forloop.index0 | plus: 1 %}
{% assign next_page = docs[next] %}
<a href="{{ site.url | append: next_page }}" class="next"></a>
{% endif %}
</div>
<div class="clear"></div>
{% break %}
{% endif %}
{% endfor %}
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,78 @@
<!DOCTYPE html>
<html>
<head>
{% if page.url != "/" %}
<title>Lsyncd - {{ page.title }}</title>
{% else %}
<title>{{ page.title }}</title>
{% endif %}
<!-- link to main stylesheet -->
<link rel="stylesheet" type="text/css" href="{{ site.url }}/css/main.css">
<link rel="stylesheet" type="text/css" href="{{ site.url }}/css/syntax.css">
</head>
<body>
<div id="note3"><b>Note, this manual is for upcoming Lsyncd 3 and currently in development.</b></div>
<div id="pillar">
<nav>
<div id="menu">
{% for section in site.data.docs %}
<h4>{{section.title}}</h4>
<ul>
{% for doc in section.docs %}
{% assign doc_url = doc %}
{% if doc_url != "/" %}
{% assign doc_url = doc | append: "/" %}
{% endif %}
{% assign p = site.pages | where:"url", doc_url | first %}
{% if page.tab %}
{% assign tab = page.tab | prepend:"/" | append:"/" %}
{% endif %}
{% if p.short %}
{% assign title = p.short %}
{% else %}
{% assign title = p.title %}
{% endif %}
<li><a {% if doc_url == page.url or doc_url == tab %}class="current"{% endif %} href="{{ site.url | append: p.url }}">{{ title }}</a></li>
{% endfor %}
</ul>
{% endfor %}
<div id="menufooter"></div>
</div>
<div id="ribbonwrap"><a href="https://github.com/axkibe/lsyncd" id="ribbon"></a></div>
</nav>
<div id="container">
<a href="https://github.com/axkibe/lsyncd/edit/gh-pages{{ page.url }}index.md" id="improvethis"></a>
<h1>{{ page.title }}</h1>
{{ content }}
{% assign docs = site.data.docs | map: 'docs' | join: ',' | split: ',' %}
{% for doc in docs %}
{% assign doc_url = doc %}
{% if doc_url != "/" %}
{% assign doc_url = doc_url | append: "/" %}
{% endif %}
{% if doc_url == page.url %}
<div id="footnav" class="prenext">
{% if forloop.first %}
{% else %}
{% assign previous = forloop.index0 | minus: 1 %}
{% assign previous_page = docs[previous] %}
<a href="{{ site.url | append: previous_page }}" class="prev"></a>
{% endif %}
{% if forloop.last %}
{% else %}
{% assign next = forloop.index0 | plus: 1 %}
{% assign next_page = docs[next] %}
<a href="{{ site.url | append: next_page }}" class="next"></a>
{% endif %}
</div>
<div class="clear"></div>
{% break %}
{% endif %}
{% endfor %}
</div>
</div>
</body>
</html>

View File

@ -0,0 +1,46 @@
---
layout: default
title: Developer Docs
short: Developer Docs
---
Code structure / Style guide
----------------------------
At large, Lsyncd's documentation contained in source code comments is well developed - at least compared to many other software projects. Here thus only a few outlining words.
Lsyncd 2 is partially written in C, partially written in Lua. For some code pieces it is a free decision in which it could be coded, but for most one of the two was more appropriate. Generally the goal was if there was no good reason to code something in C do it in Lua. The C core thus does all the stuff close to the operating system:
* inotify interface
* process management, zombie collection
* signals
* pipes
* logging (also in the core, so it can log itself comfortably)
* alarms (Lsyncd uses kernel jiffies as alarms, thus it should be year Y2K38 safe (2038 is the year in which an often used time measurement - seconds since 1970 - will overflow.)
Everything else is done in lsyncd.lua.
While Lua is not an object oriented language itself, it supplies a set of features which can emulate its features quite nicely. Shortly spoken, it supports prototyping. Lsyncd makes use of local function scoping to create blocks of code which are loosely connected with each other. A major feature of object oriented language.
A typical "class" in Lsyncd looks like this.
```Lua
MyClass = (function()
local private = "only visible here"
local function new()
return {foo="bar"}
end
-- public interface
return { new = new }
end)()
```
In plain interpretation this creates a global variable called "MyClass". It defines an unnamed function with internal variables and subfunctions. This functions returns a table of all functions that should be visible from the outside. Then the unnamed function is immediately called on the global definition of "MyClass" and the public interface table stored in "MyClass". `MyClass.new()` is then used to create objects of this class. Or if it is a "Singelton", it has no new function, but other functions, that would be considered "static" in an object oriented language.
Lua has developed a culture for personal adaption - "power patches". To not throw a stick into any package maintainer Lsyncd uses standard from the stock Lua (5.1).
One note about the style of sync{} configs. As you can see in Layer 4 configuration the configuration is selected by merely adding the configuration table as parameter. In the implementation it does nothing else than to take any value with a number as key and which has a table as value and copy all of its key/value entries that are not there already. This even works recursively if that config again imports another config.
Why Lua and not ...?
--------------------
Lua has been chosen for Lsyncd 2 much out of the same reasons why C was chosen for Lsyncd 1.x. Lsyncd should be small and fast and should come with as few as possible dependencies.
The move from a pure C implementation as in Lsyncd 1.x to using Lua was motivated by enhancing the configuration file format. XML was starting to get clunky for the requirements of Lsyncd. When used Lua bindings already for config file parsing, there was little reason not to move large parts of Lsyncd internal logic into Lua as well. With its garbage collector, memory management became much easier, and with its highly optimized tables as dictionaries, some things even become faster, as Lsyncd 1.x linearly traversed a lot of lists, while Lsyncd 2 uses Lua tables as hash tables everywhere.
Lua was chosen over other script languages not because I think Lua is the greatest thing on earth - like many coders think about their favorite language, but because it is considered to be a appropriate tool for this task. Many coders like [X] so much, they want to do everything with it, also because after a while they know [X] very well, and are of course faster in solving an issue than learning [Y]. I on the other hand didn't know Lua before Lsyncd 2 at all, and decided explicitly for it out of pragmatic reasons. First it is said have originated out of the need for configuration files for C applications. Much the use case Lsyncd needs. So the C interface is handy and fast. Since Lsyncd has its parts close to the system still coded in C this interfacing makes things easier. Lua is very fast. Without affronting other script languages one can say it is at least in the top tier regarding speed. It may be the fastest scripting language there is, but this statement might raise debates. But not without reason it got quite some attention from the gaming industry. It gets even faster with LuaJIT and it gets into the range of JavaJIT (which is a compiled and not a script language). Lua has also a very slim standard library, while this is a stopper for people just wanting to quickly write some script this plays into the hands of Lsyncd. It comes with little requirements on the machine to be installed and Lsyncd comes with its own custom core functionally anyway. And although the Lua syntax looks a bit dirty at first - like implicit on the fly creation of globals - with a little configuration this can be banned. Like Lsyncd does in normal operation modes.

257
docs/css/main.css Normal file
View File

@ -0,0 +1,257 @@
html {
height: 100%;
overflow-y: scroll;
}
body {
background-color: rgb( 245, 245, 245 );
font: 13px/1.4 Helvetica, arial, nimbussansl, liberationsans, freesans, clean, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol";
height: 100%;
margin: 0;
}
#pillar {
position: relative;
margin: 0 auto;
max-width: 70em;
min-height: 100%;
overflow: auto;
background-color: white;
padding: 0;
}
a:link, a:visited {
color: blue;
text-decoration: inherit;
}
a:hover {
text-decoration: underline;
}
nav {
background-color: rgb( 200, 200, 200 );
position : absolute;
right : 0;
padding: 0;
width: 11.1em;
bottom: 0;
top: 0;
margin: 0;
}
#menu {
background-color: rgba( 200, 200, 200, 0.95 );
z-index: 20;
position: relative;
margin: 0;
padding: 0;
width: 11.1em;
}
#menufooter {
position: absolute;
bottom: -2em;
height: 2em;
width: 11.1em;
background-color: transparent;
background-image: linear-gradient(to bottom, rgba(200, 200, 200, 0.95), rgba(200, 200, 200, 0));
pointer-events: none;
}
nav h4 {
border-bottom: 1px solid black;
padding-left: 1em;
margin-right: 1em;
font-size: 15px;
}
nav a {
display : block;
position : relative;
margin-left: 1em;
padding: 0.2em 0 0.2em 0;
font-size: 14px;
}
nav a.current {
background-color: rgb( 220, 220, 220 );
padding-left: 1em;
margin-left: 0;
margin-right: 1em;
border-top-right-radius: 0.8em;
border-bottom-right-radius: 0.8em;
}
nav a.current::before {
content : "";
border-color : white;
border-width : 1em;
border-style : solid;
border-radius : 1em;
position : absolute;
left: -1.5em;
height : 0;
top : -0.12em;
width: 0;
z-index: 100;
}
nav ul {
list-style-type: none;
padding : 0;
}
#container {
margin: 0 13em 0 2em;
}
table {
padding : 0;
margin : 0;
border-spacing : 0;
}
tr:nth-child( even ) {
background-color: rgb( 240, 240, 240 );
}
tr:first-child td {
border-top : 1px solid rgb( 220, 220, 220 )
}
tr td:first-child, tr th:first-child {
border-left : 1px solid rgb( 220, 220, 220 )
}
thead::after {
line-height: 0.4em;
content: "\200C";
display:block;
}
th {
background-color: rgb( 230, 230, 230 );
border-top : 1px solid rgb( 210, 210, 210 );
border-bottom : 1px solid rgb( 210, 210, 210 );
border-right : 1px solid rgb( 210, 210, 210 );
padding : 0.8em 1em;
margin : 0;
font-weight: bold;
}
th:first-child {
border-top-left-radius: 1em;
border-bottom-left-radius: 1em;
}
th:last-child {
border-top-right-radius: 1em;
border-bottom-right-radius: 1em;
}
td {
border-bottom : 1px solid rgb( 220, 220, 220 );
border-right : 1px solid rgb( 220, 220, 220 );
padding : 1em;
margin : 0;
}
tr:first-child td:first-child { border-top-left-radius: 1em; }
tr:first-child td:last-child { border-top-right-radius: 1em; }
tr:last-child td:first-child { border-bottom-left-radius: 1em; }
tr:last-child td:last-child { border-bottom-right-radius: 1em; }
#ribbonwrap {
position: absolute;
right: 14.1em;
z-index: 10;
}
#ribbon {
position: fixed;
bottom: 0;
width: 12.1em;
height: 12.1em;
overflow: hidden;
}
#ribbon::before, #ribbon::after {
display: block;
position: absolute;
transform: rotate(-45deg);
top: auto;
bottom: 3.15em;
height: 1.54em;
width: 15.38em;
right: -3.23em;
}
#ribbon::before {
content: "";
background-color: #666;
background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0), rgba(0, 0, 0, 0.15));
padding: 0.38em 0;
box-shadow: 0 .15em .23em 0 rgba(0, 0, 0, 0.5);
}
#ribbon::after {
content: "Fork me on Github";
color: white;
font: 700 1em "Helvetica Neue", Helvetica, Arial, sans-serif;
line-height: 1.54em;
padding: 0.15em 0;
margin: 0.15em 0;
text-indent: 0;
text-align: center;
border-color: rgba(255, 255, 255, 0.7);
border-style: dotted;
border-width: 0.08em 0;
}
#footnav {
}
.prenext {
}
.prenext a:hover::before {
text-decoration: underline;
}
.prenext .prev::before, .prenext .next:before {
background-color: rgb( 240, 240, 240 );
padding: 0.2em 0.8em;
border-radius: 0.8em;
margin: 1em 0 1em 0;
}
.prenext .prev::before {
content : "\2039\2039\00a0prev";
float: left;
}
.prenext .next::before {
content : "next\00a0\203A\203A";
float: right;
}
#improvethis {
float: right;
color: #bbb;
margin: 1em 0 0 0;
}
#improvethis::before {
content : "\270E\00a0Improve\00a0this\00a0page";
}
.highlighter-rouge {
background-color: #f8f8f8;
border-radius: 0.4em;
padding: 0.1em 0.2em;
}

212
docs/css/syntax.css Normal file
View File

@ -0,0 +1,212 @@
.highlight table td { padding: 5px; }
.highlight table pre { margin: 0; }
.highlight .cm {
color: #999988;
font-style: italic;
}
.highlight .cp {
color: #999999;
font-weight: bold;
}
.highlight .c1 {
color: #999988;
font-style: italic;
}
.highlight .cs {
color: #999999;
font-weight: bold;
font-style: italic;
}
.highlight .c, .highlight .cd {
color: #999988;
font-style: italic;
}
.highlight .err {
color: #a61717;
background-color: #e3d2d2;
}
.highlight .gd {
color: #000000;
background-color: #ffdddd;
}
.highlight .ge {
color: #000000;
font-style: italic;
}
.highlight .gr {
color: #aa0000;
}
.highlight .gh {
color: #999999;
}
.highlight .gi {
color: #000000;
background-color: #ddffdd;
}
.highlight .go {
color: #888888;
}
.highlight .gp {
color: #555555;
}
.highlight .gs {
font-weight: bold;
}
.highlight .gu {
color: #aaaaaa;
}
.highlight .gt {
color: #aa0000;
}
.highlight .kc {
color: #000000;
font-weight: bold;
}
.highlight .kd {
color: #000000;
font-weight: bold;
}
.highlight .kn {
color: #000000;
font-weight: bold;
}
.highlight .kp {
color: #000000;
font-weight: bold;
}
.highlight .kr {
color: #000000;
font-weight: bold;
}
.highlight .kt {
color: #445588;
font-weight: bold;
}
.highlight .k, .highlight .kv {
color: #000000;
font-weight: bold;
}
.highlight .mf {
color: #009999;
}
.highlight .mh {
color: #009999;
}
.highlight .il {
color: #009999;
}
.highlight .mi {
color: #009999;
}
.highlight .mo {
color: #009999;
}
.highlight .m, .highlight .mb, .highlight .mx {
color: #009999;
}
.highlight .sb {
color: #d14;
}
.highlight .sc {
color: #d14;
}
.highlight .sd {
color: #d14;
}
.highlight .s2 {
color: #d14;
}
.highlight .se {
color: #d14;
}
.highlight .sh {
color: #d14;
}
.highlight .si {
color: #d14;
}
.highlight .sx {
color: #d14;
}
.highlight .sr {
color: #009926;
}
.highlight .s1 {
color: #d14;
}
.highlight .ss {
color: #990073;
}
.highlight .s {
color: #d14;
}
.highlight .na {
color: #008080;
}
.highlight .bp {
color: #999999;
}
.highlight .nb {
color: #0086B3;
}
.highlight .nc {
color: #445588;
font-weight: bold;
}
.highlight .no {
color: #008080;
}
.highlight .nd {
color: #3c5d5d;
font-weight: bold;
}
.highlight .ni {
color: #800080;
}
.highlight .ne {
color: #990000;
font-weight: bold;
}
.highlight .nf {
color: #990000;
font-weight: bold;
}
.highlight .nl {
color: #990000;
font-weight: bold;
}
.highlight .nn {
color: #555555;
}
.highlight .nt {
color: #000080;
}
.highlight .vc {
color: #008080;
}
.highlight .vg {
color: #008080;
}
.highlight .vi {
color: #008080;
}
.highlight .nv {
color: #008080;
}
.highlight .ow {
color: #000000;
font-weight: bold;
}
.highlight .o {
color: #000000;
font-weight: bold;
}
.highlight .w {
color: #bbbbbb;
}
.highlight {
background-color: #f8f8f8;
border-radius: 1em;
padding: 0.1em 1em;
margin: 0;
}

13
docs/download/index.md Normal file
View File

@ -0,0 +1,13 @@
---
layout: default
title: "Download"
---
Current Release
=======================
All releases are on the [releases page](https://github.com/lsyncd/lsyncd/releases)
HEAD Development
================
{% highlight shell %}
git clone https://github.com/lsyncd/lsyncd.git
{% endhighlight %}

8
docs/faq/index.md Normal file
View File

@ -0,0 +1,8 @@
---
layout: default
title: Frequently Asked Questions
short: FAQ
---
* [How can I call a script after each rsync operation?](postscript)
* [How can I sync from one source to multiple targets?](multiple-targets)
* [The startup sync works but after that Lsyncd doesn't do anything!](nothing-after-startup)

View File

@ -0,0 +1,32 @@
---
layout: default
title: "FAQ: How can I sync from one source to multiple targets?"
---
If you got multiple targets, you simple specify the sync command multiple times.
{% highlight lua %}
sync{ default.rsync, source='/sourcedir', target='targethost1:/targetdir' }
sync{ default.rsync, source='/sourcedir', target='targethost2:/targetdir' }
sync{ default.rsync, source='/sourcedir', target='targethost3:/targetdir' }
{% endhighlight %}
Lsyncd will notice multiple uses of the same source directory or the use of a subdirectory of an already used source directory and creates only watch per subdirectoy watched in any sync.
To remedy the multiplication of the same configuration you can even use a loop to configure multiple targets.
This is the same configuration as before using a loop:
{% highlight lua %}
targets = {
'targethost1:/targetdir',
'targethost2:/targetdir',
'targethost3:/targetdir',
}
for _, target in ipairs( targets )
do
sync{ default.rsync, source='/sourcedir', target=target }
end
{% endhighlight %}
## [ back to FAQ index](../)

View File

@ -0,0 +1,11 @@
---
layout: default
title: "FAQ: The startup sync works but after that Lsyncd doesn't do anything!"
---
This almost always caused by the fact you specified a network mounted directory as source.
Lsyncd requires the kernels inotify or fsevents interface to get noted of file changes. No known network filesystem (known to Lsyncd authors) supports forwarding file notifcations events.
Thus Lsyncd needs to run on the system where the files are located physically.
## [ back to FAQ index](../)

View File

@ -0,0 +1,42 @@
---
layout: default
title: "FAQ: How can I call a script before or after each rsync operation?"
---
The issue with this quite frequent request is, by itself it complicates error handling a lot. What should Lsyncd do, when the script fails that it ought to run after each rsync call? If it should recall the post script it would require a new state for each rsync event which would complicate Lsyncd code quite a bit.
The easiest way to get around this, is by replacing the rsync binary Lsyncd calls by a script from you, that calls rsync and does whatever you want to do, when rsync completes. The only thing to take care is that Lsyncd communicates with rsync using stdin/out/err-pipes and thus better not interfere with these.
Also take care the script properly forwards the exit code rsync returned.
This is an example bash script to wrap around rsync:
{% highlight shell %}
#!/bin/bash
/usr/bin/rsync "$@"
result=$?
(
if [ $result -eq 0 ]; then
echo "my commands";
fi
) >/dev/null 2>/dev/null </dev/null
exit $result
{% endhighlight %}
It does not do error handling for post commands. If you need this, you'll have to code them here fitting your requirements.
Above script can be used as rsync wrapper replacement like this:
{% highlight lua %}
sync {
default.rsync,
source = "/path/to/source",
target = "targethost::targetdir",
rsync = {
binary = "/path/to/bash/handler.sh"
}
}
{% endhighlight %}
## [ back to FAQ index](../)

22
docs/help/index.md Normal file
View File

@ -0,0 +1,22 @@
---
layout: default
title: "Getting and Giving Help"
short: "Help"
---
Discussion Group
----------------
There is a [discussion group to all things related Lsyncd](https://github.com/lsyncd/lsyncd/discussions)
Issues
------
For cases Lsyncd is missbehaving there are [Issues](https://github.com/lsyncd/lsyncd/issues) on Github.
Please check:
* This isn't a usage error.
* Your running the latest Lsyncd version.
* You include all things needed to reproduce the error, like complete configuration scripts.
Source & Improvement
--------------------
[Fork Lsyncd at Github](https://github.com/lsyncd/lsyncd)

47
docs/index.md Normal file
View File

@ -0,0 +1,47 @@
---
layout: default
title: "Lsyncd - Live Syncing (Mirror) Daemon"
short: "Welcome"
---
Description
-----------
Lsyncd uses a filesystem event interface (inotify or fsevents) to watch for changes to local files and directories. Lsyncd collates these events for several seconds and then spawns one or more processes to synchronize the changes to a remote filesystem. The default synchronization method is [rsync](http://rsync.samba.org/). Thus, Lsyncd is a light-weight live mirror solution. Lsyncd is comparatively easy to install and does not require new filesystems or block devices. Lysncd does not hamper local filesystem performance.
As an alternative to rsync, Lsyncd can also push changes via rsync+ssh. Rsync+ssh allows for much more efficient synchronization when a file or direcotry is renamed or moved to a new location in the local tree. (In contrast, plain rsync performs a move by deleting the old file and then retransmitting the whole file.)
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/). Thus, simple, powerful and flexible configurations are possible.
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.
When to use
-----------
Lsyncd is designed to synchronize a slowly changing local directory tree to a remote mirror. Lsyncd is especially useful to sync data from a secure area to a not-so-secure area.
Other synchronization tools
------------------------
[DRBD](http://www.drbd.org) operates on block device level. This makes it useful for synchronizing systems that are under heavy load. Lsyncd on the other hand does not require you to change block devices and/or mount points, allows you to change uid/gid of the transferred files, separates the receiver through the one-way nature of rsync. DRBD is likely the better option if you are syncing databases.
[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
---------------------
{% highlight shell %}
lsyncd -rsync /home remotehost.org::share/
{% endhighlight %}
This watches and rsyncs the local directory /home with all sub-directories and
transfers them to 'remotehost' using the rsync-share 'share'.
{% highlight shell %}
lsyncd -rsyncssh /home remotehost.org backup-home/
{% endhighlight %}
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.
Disclaimer
----------
Besides the usual disclaimer in the license, we want to specifically emphasize that neither the authors, nor any organization associated with the authors, can or will be held responsible for data-loss caused by possible malfunctions of Lsyncd.

161
docs/manpage/lsyncd.1 Normal file
View File

@ -0,0 +1,161 @@
'\" t
.\" Title: lsyncd
.\" Author: [see the "AUTHOR" section]
.\" Generator: DocBook XSL Stylesheets vsnapshot <http://docbook.sf.net/>
.\" 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://lsyncd\&.github\&.io/lsyncd/
.SH "VERSION"
.sp
This man page is for lsyncd(1) version 2\&.2\&.0
.SH "AUTHOR"
.sp
Axel Kittenberger, <axkibe@gmail\&.com> 2010\-2017 Daniel Poelzleithner, <poelzleithner@b1\-systems\&.de> 2021\-2023
.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)\&.

View File

@ -3,8 +3,8 @@ lsyncd(1)
:doctype: manpage
:man source: Lsyncd
:man manual: Lsyncd
:man version: 2.0.7
:date: April 2012
:man version: 2.2.1
:date: January 2017
NAME
----
@ -31,17 +31,17 @@ Lsyncd(1) watches local directory trees through an event monitor interface
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
blockdevices and does not hamper local filesystem performance.
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 retransmitting the move
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 http://code.google.com/p/lsyncd/wiki/Lsyncd20Manual
CONFIG-FILE https://lsyncd.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.
@ -55,7 +55,7 @@ OPTIONS
Show a help message.
*-insist*::
Continues startup even if a startup rsync cannot connect.
Continues start up even if rsync cannot connect.
*-log* 'LEVEL'::
Controls which kind of events are logged. By default Lsyncd logs 'Normal'
@ -69,6 +69,9 @@ OPTIONS
*-nodaemon*::
Lsyncd will not detach from the invoker and log as well to stdout/err.
*-onepass*::
Sync once and exit
*-pidfile* 'FILE'::
Lsyncd will write its process ID in 'FILE'.
@ -80,27 +83,29 @@ OPTIONS
EXIT STATUS
-----------
*0*::
Terminated on a TERM signal(7)
*(128+SIGNUM)*::
Terminated by Signal (143 by TERM)
*-1*::
Failure (syntax, unrecoverable error condition, internal failure)
SEE ALSO
--------
Online Manual: http://code.google.com/p/lsyncd/wiki/Lsyncd2
Online Manual: https://lsyncd.github.io/lsyncd/
VERSION
------
This man page is for lsyncd(1) version 2.0.7
This man page is for lsyncd(1) version 2.2.0
AUTHOR
------
Axel Kittenberger, <axkibe@gmail.com> 2010-2012
Axel Kittenberger, <axkibe@gmail.com> 2010-2017
Daniel Poelzleithner, <poelzleithner@b1-systems.de> 2021-2023
COPYING
-------
Copyright \(C) 2010-2012 Axel Kittenberger. Free use of this software is granted
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).

View File

@ -0,0 +1,26 @@
---
layout: default
title: Building
---
## Requirements
### Lua >= 5.2
Lsyncd depends on Lua 5.2 or greater; that is 5.2 or 5.3. For most distributions you need to install the liblua??, the liblua??-dev and the lua?? package, with ?? being the respective Lua version.
### cmake >= 2.8
To configure Lsyncd to your system, cmake >= 2.8 is required
### rsync >= 3.1
During runtime Lsyncd needs rsync > 3.1 installed both on source and target systems.
## Compiling
With these requirements fulfilled building Lsyncd should be a straight forward process. Unpack the downloaded tar.gz file and run:
{% highlight shell %}
cmake .
make
sudo make install
{% endhighlight %}

View File

@ -0,0 +1,99 @@
---
layout: default
title: "The Configuration File"
short: "Config File"
---
Lsyncd configuration files are valid [Lua syntax](http://www.lua.org/). It is designed to be simple yet potent. While rich configuration and simplicity are not opposites by themselves, some trade-offs are inevitable. To achieve both goals as far as possible, Lsyncd configuration can be done at different layers. Lower layers add adaptability while the interface becomes more engaging.
Settings
--------
For scripts of all layers, the ```settings``` call can be used to alter daemon-wide configurations.
For example, the following code will instruct Lsyncd to log into ```/tmp/lsyncd.log```, periodically update the file ```/tmp/lsyncd.status``` with its status and to not detach as a daemon.
{% highlight lua %}
settings {
logfile = "/tmp/lsyncd.log",
statusFile = "/tmp/lsyncd.status",
nodaemon = true,
}
{% endhighlight %}
**Caution**
If you are upgrading from 2.0.x, please notice that `settings` became a function from a variable, so you **MUST** delete the equal sign '=' between `settings` and the `{`.
Valid keys for settings are:
<table>
<tr><td> logfile
</td><td> =
</td><td> FILENAME
</td><td> logs into this file
</td></tr>
<tr><td> pidfile
</td><td> =
</td><td> FILENAME
</td><td> logs PID into this file
</td></tr>
<tr><td> nodaemon
</td><td> =
</td><td> BOOL
</td><td> does not detach
</td></tr>
<tr><td> statusFile
</td><td> =
</td><td> FILENAME
</td><td> periodically writes a status report to this file
</td></tr>
<tr><td> statusInterval
</td><td> =
</td><td> NUMBER
</td><td> writes the status file at shortest after this number of seconds has passed (default: 10)
</td></tr>
<tr><td> logfacility
</td><td> =
</td><td> STRING
</td><td> syslog facility, default "user"
</td></tr>
<tr><td> logident
</td><td> =
</td><td> STRING
</td><td> syslog identification (tag), default "lsyncd"
</td></tr>
<tr><td> insist
</td><td> =
</td><td> BOOL
</td><td> keep running at startup although one or more targets failed due to not being reachable.
</td></tr>
<tr><td> inotifyMode
</td><td> =
</td><td> STRING
</td><td> Specifies on inotify systems what kind of changes to listen to. Can be "Modify", "CloseWrite" (default) or "CloseWrite or Modify".
</td></tr>
<tr><td> maxProcesses
</td><td> =
</td><td> NUMBER
</td><td> Lysncd will not spawn more than these number of processes. This adds across all sync{}s.
</td></tr>
</table>
Additionally some parameters can be configured, which are inherited by all _Syncs_ (see Layer 3)
<table>
<tr><td> maxDelays
</td><td> =
</td><td> NUMBER
</td><td> When this amount of delayed events is queued, actions will be spawned, even below the delay timer.
</td></tr>
</table>

View File

@ -0,0 +1,182 @@
---
layout: default
title: "Config Layer 1: Inlets"
short: "Config Layer 1"
---
Layer 2 allows you to create one process per one event. However, as with default rsync behavior you might want to call one process for several events. This can be done with inlets. When any event becomes ready Lsyncd calls the ```action``` entry with ```inlet``` as parameter. The ```inlet``` can be used to grab ready single events or event lists.
For example this is the action used by default.rsync:
{% highlight lua %}
action = function( inlet )
local elist = inlet.getEvents( )
local config = inlet.getConfig( )
local paths = elist.getPaths( )
log( "Normal", "rsyncing list\n", table.concat( paths, '\n' ) )
spawn(elist, '/usr/bin/rsync',
'<', table.concat( paths, '\000' ),
'--delete',
config.rsync._computed,
'--from0',
'--include-from=-',
'--exclude=*',
config.source,
config.target
)
end
{% endhighlight %}
Inlet functions are:
| Function | Description |
|:---------|:------------|
| inlet.getEvent() | Retrieves the next `event` as in Layer 2 configuration. Multiple calls to getEvent() will return the same event unless it has spawn{}ed an action. |
| inlet.getEvents(test) | Returns a list of all events that are ready. `test` is optional for a function that will be called for every event to test if it should be included in the list. It has one parameter the `event` and returns true if an event should be included. If nil every ready event will be included in the list |
| inlet.discardEvent() | Discards an event. The next call to getEvent will thus receive another event, even if no action has been spawned for this event |
| inlet.getConfig() | returns the same as `event.config`. The configuration of the sync{} |
| inlet.addExclude() | adds an exclusion pattern to this sync (see Exclusions) |
| inlet.rmExclude() | removes an exclusion pattern from this sync |
| inlet.createBlanketEvent() | puts an `event` on the top of the Delay FIFO that blocks all events and is blocked by all events. This is used for onStartup. |
The list returned by getEvents can be handed to spawn{} as _agent_ just as well as singular events.
Lists have following functions
| Function | Description |
|:----------|:------------|
| elist.getPaths(delimiter) | returns a string of the paths (as in `event.path` separated by `delimiter`. By default \n is used as delimiter. |
| elist.getSourcePaths(delimiter) | returns a string of the sourcePaths (as in `event.sourcePath` separated by `delimiter`. By default \n is used as delimiter. |
Take care calling getEvents() and its function since depending on the amount of events, they will cause quite some CPU load.
Layer 2 functions is nothing else than following layer 1 action loaded by the default if the user script did not provide one itself.
{% highlight lua %}
-----
-- Default action calls user scripts on**** functions.
--
action = function( inlet )
-- in case of moves getEvent returns the origin and destination of the move
local event, event2 = inlet.getEvent( )
local config = inlet.getConfig( )
local func = config[ 'on'.. event.etype ]
if func
then
func( event, event2 )
end
-- if function didnt change the wait status its not interested
-- in this event -> drop it.
if event.status == "wait"
then
inlet.discardEvent( event )
end
end,
{% endhighlight %}
Lsyncd will automatically split Move events into Create and Delete events if no "onMove" field is found in the config. When handling moves in layer 1 `action` function, simply set "onMove" to be "true".
Other than `action` Lsyncd calls `init` for each sync{} on initialization. This is the default init function which is loaded if the user script does not have one. It provides the onStartup() functionality for layer 2 and 3.
{% highlight lua %}
-----
-- called on (re)initalizing of lsyncd.
--
init = function( inlet )
local config = inlet.getConfig( )
-- calls a startup if provided by user script.
if type( config.onStartup ) == "function"
then
local event = inlet.createBlanketEvent( )
config.onStartup( event )
if event.status == 'wait'
then
-- user script did not spawn anything
-- thus the blanket event is deleted again.
inlet.discardEvent( event )
end
end
end,
{% endhighlight %}
As another example this is the init of `default.rsync`. As specialty it changes the configuration in that it adds a slash to target if not there already.
{% highlight lua %}
-----
-- Spawns the recursive startup sync
--
init = function( inlet )
local config = inlet.getConfig( )
local event = inlet.createBlanketEvent( )
if string.sub(config.target, -1) ~= "/"
then
config.target = config.target .. "/"
end
log("Normal", "recursive startup rsync: ", config.source,
" -> ", config.target)
spawn(event,
"/usr/bin/rsync",
"--delete",
config.rsync._computed .. "r",
config.source,
config.target
)
end,
{% endhighlight %}
When child processes are finished and their zombie processes are collected, Lsyncd calls the function of the `collect` entry. When collect return "again" the status of the agent (an event or an event list) will be set on "wait" again, and will become ready in `delay` seconds (or 1 second if smaller).
The default collect function looks in the exitcodes[] table for an entry of the exit code. Otherwise most of the unfortunately longer code below does nothing but making nice log message.
{% highlight lua %}
-----
-- Called when collecting a finished child process
--
collect = function(agent, exitcode)
local config = agent.config
if not agent.isList and agent.etype == "Blanket" then
if exitcode == 0
then
log("Normal", "Startup of '",agent.source,"' finished.")
elseif config.exitcodes and
config.exitcodes[exitcode] == "again"
then
log("Normal",
"Retrying startup of '",agent.source,"'.")
return "again"
else
log("Error", "Failure on startup of '",agent.source,"'.")
terminate(-1) -- ERRNO
end
return
end
local rc = config.exitcodes and config.exitcodes[exitcode]
if rc == "die"
then
return rc
end
if agent.isList
then
if rc == "again"
then
log("Normal", "Retrying a list on exitcode = ",exitcode)
else
log("Normal", "Finished a list = ",exitcode)
end
else
if rc == "again"
then
log("Normal", "Retrying ",agent.etype,
" on ",agent.sourcePath," = ",exitcode)
else
log("Normal", "Finished ",agent.etype,
" on ",agent.sourcePath," = ",exitcode)
end
end
return rc
end,
{% endhighlight %}

View File

@ -0,0 +1,94 @@
---
layout: default
title: "Config Layer 2: Advanced onAction"
short: "Config Layer 2"
---
While Layer 4 and 3 feel like normal configuration files, Layer 2 and 1 enter the realm of coding. It is thus supposed you have some coding knowledge when using Layer 2 or 1.
Instead of designating actions as strings as in Layer 3 Lua functions can used to do some small scripts right within Lsyncd.
This example will convert any file with the suffix ".ps" created in a directory into a PDF.
{% highlight lua %}
autopdf = {
onCreate = function(event)
log("Normal", "got an onCreate Event")
if string.ends(event.pathname, ".ps") then
spawn(event, "/usr/bin/ps2pdf", event.sourcePath)
end
end
}
{% endhighlight %}
The function can take any valid Lua code.
Lsyncd provides you a set of functions to be used in user scripts.
log(Category, ...)
------------------
Logs a message into file/stdout/syslog. The first parameter is the logging category all others are strings to be logged. A logging category must start with a capital letter. "Normal" and "Error" are standard categories for log messages. All others are categories for debugging.
spawn(Event, Binary, ...)
--------------------------
Spawns a new process associated with the event (or event list, see below) as first parameter. The second parameter specifies a binary to call. All others are arguments for the binary.
If the third parameter is "<", then along with fourth parameter they will not be passed as arguments to the binary. The fourth parameter is a string that will piped through stdin to the binary.
Do not use Lua's ```os.execute``` as opposed to Lsyncd's ```spawn()``` it will block and thus block the whole Lsyncd daemon until the command is completed. Lsyncd's ```spawn``` on the other hand returns immediately while the child process runs.
spawnShell(Event, Command, ... )
--------------------------------
The same as spawn(), only it will invoke a shell. Any parameters are referred as $1, $2, $3 and so on in the command.
By the way, this is the simple implementation of spawnShell:
{% highlight lua %}
function spawnShell(agent, command, ...)
return spawn(agent, "/bin/sh", "-c", command, "/bin/sh", ...)
end
{% endhighlight %}
terminate(exitcode)
-------------------
Lets Lsyncd terminate with ```exitcode```.
event
-----
Variables of the actions are given by the _event_ field. It has following fields.
|Field|Meaning|
|:----|:----|
| event.config | the configuration as called with sync{} |
| event.inlet | see [layer 1](../layer1/) about inlets |
| event.etype | the event type. Can be 'ATTRIB', 'CREATE', 'MODIFY', 'DELETE', 'MOVE' |
| event.status | the status of the event. 'wait' when it is ready to be spawned and 'active' if there is a process running associated with this event |
| event.isdir | true if the event relates to a directory |
| event.name | the filename, directories end with a slash |
| event.basename | the filename, directories do not end with a slash |
| event.path | see ^path of [Layer 3](../layer3/#all-possible-variables) |
| event.pathname | see ^pathname of [Layer 3](../layer3/#all-possible-variables) |
| event.source | see ^source of [Layer 3](../layer3/#all-possible-variables) |
| event.sourcePath | see ^sourcePath of [Layer 3](../layer3/#all-possible-variables) |
| event.sourcePathname | see ^sourcePathname of [Layer 3](../layer3/#all-possible-variables) |
| event.target | see ^target of [Layer 3](../layer3/#all-possible-variables) |
| event.targetPath | see ^targetPath of [Layer 3](../layer3/#all-possible-variables) |
| event.targetPathname | see ^targetPathname of [Layer 3](../layer3/#all-possible-variables) |
onMove actions have two events as parameter, the origin and the destination of the move.
This example will tattle about all moves within the observed directory tree.
{% highlight lua %}
tattleMove = {
onMove = function(oEvent, dEvent)
log("Normal", "A moved happened from ",
oEvent.pathname, " to ", dEvent.pathname)
end,
}
{% endhighlight %}
Action functions have to be short and fast. They are running right within Lsyncd's one and only main thread. If you have to do any more time consuming calculations _spawn{}_ a child process instead.
There can only be one child process associated to a event.
Layer 3 is nothing else than Lsyncd automatically write Layer 2 functions for you on initialization. Start Lsyncd with ```-log FWrite``` on a Layer 3 configuration to see what functions it dynamically writes and loads for you. Thus Layer 3 and 2 can also be be mixed at will.

View File

@ -0,0 +1,175 @@
---
layout: default
title: "Config Layer 3: Simple onAction"
short: "Config Layer 3"
---
Simple onAction
---------------
In this layer, custom configurations can be created. This example will use bash commands to keep a local directory in sync.
{% highlight lua %}
bash = {
delay = 5,
maxProcesses = 3,
onCreate = "cp -r ^sourcePathname ^targetPathname",
onModify = "cp -r ^sourcePathname ^targetPathname",
onDelete = "rm -rf ^targetPathname",
onMove = "mv ^o.targetPathname ^d.targetPathname",
onStartup = '[[ if [ "$(ls -A ^source)" ]; then cp -r ^source* ^target; fi]]',
}
{% endhighlight %}
The example explained step by step. Technically, any Lsyncd configuration is a Lua table with a set of keys filled out. Thus it starts by creating a variable called ```bash``` and assigns it a table with = { ... }.
{% highlight lua %}
bash = {
...
}
{% endhighlight %}
Now the table is filled with entries. Every entry having a key left of the equal sign and its value right of it. If no delay is specified, this means immediate actions for Lsyncd. This example wants to aggregate changes for 5 seconds thus the next entry is:
{% highlight lua %}
delay = 5,
{% endhighlight %}
And a comma is needed since to mark the end of an entry.
Actions
-------
Actions are specified by the 6 keys:
<table>
<tr><td> onAttrib
</td><td> called when only attributes changed
</td></tr>
<tr><td> onCreate
</td><td> called on a new file or directory
</td></tr>
<tr><td> onModify
</td><td> called when a file has changed
</td></tr>
<tr><td> onDelete
</td><td> called when a file or directory has been deleted
</td></tr>
<tr><td> onMove
</td><td> called when a file or directory has been moved within the observed directory tree
</td></tr>
<tr><td> onStartup
</td><td> called on the start of Lsyncd
</td></tr>
</table>
When there is no ```onMove``` or the move goes into or out of the observed directory tree, it is split into an ```onDelete``` of the move origin and an ```onCreate``` of the move destination. That is if either is within the observed directory tree. ```onStartup``` will always block all other actions for this _Sync_ until completed.
The action to be taken is specified as a Lua string. Thus actions can be delimited with anything Lua allows, these are 'TEXT', "TEXT", or '[[TEXT]] as used in ```onStartup``` in the example above.
Any action starting with a "/" instructs Lsyncd to directly call the binary file at the beginning instead of spawning an additional shell. For example
{% highlight lua %}
onCreate = "/usr/bin/zip /usr/var/all.zip ^sourcePath"
onModify = "/usr/bin/zip /usr/var/all.zip ^sourcePath"
{% endhighlight %}
will add any newly created and modified files to /usr/var/all.zip using absolute path names. Any action not starting with a "/" will result in Lsyncd spawning a shell to execute the action as command.
Variables
---------
Variable arguments are specified with the caret symbol ^. It has been chosen over $ or other symbols to be less conflicting with standard shell conventions.
Note that variables will always be implicitly quoted in double quotes, so if you want them to be a part of another double-quoted string, you will have to go one layer deeper, e.g.
{% highlight lua %}
onCreate = '[[ su user -c "/usr/bin/zip /usr/var/all.zip ^o.sourcePath " ]],
{% endhighlight %}
will expand to ```su user -c "/usr/bin/zip /usr/var/all.zip "source""``` which is incorrect and will break. You have to rewrite the above statement one layer deeper as
{% highlight lua %}
onCreate = function(event)
spawnShell('[[ su user -c "/usr/bin/zip /usr/var/all.zip \"$1\"" ]], event.sourcePath)
end
{% endhighlight %}
All possible variables
----------------------
<table>
<tr><td> ^source
</td><td> the absolute path of the observed source directory
</td></tr>
<tr><td> ^target
</td><td> the "target" attribute of the config
</td></tr>
<tr><td> ^path
</td><td> the relative path of the file or directory to the observed directory; directories have a slash at the end.
</td></tr>
<tr><td> ^pathname
</td><td> the relative path of the file or directory to the observed directory; directories have no slash at the end.
</td></tr>
<tr><td> ^sourcePath
</td><td> the absolute path of the observed source directory and the relative path of the file or directory; this equals the absolute local path of the file or directory. Directories have a slash at the end.
</td></tr>
<tr><td> ^sourcePathname
</td><td> same as ^sourcePath, but directories have no slash at the end.
</td></tr>
<tr><td> ^targetPath
</td><td> The "target" attributed of the config appended by the relative path of the file or directory. Directories have a slash at the end.
</td></tr>
<tr><td> ^targetPathname
</td><td> same as ^targetPath, but directories have no slash at the end.
</td></tr>
</table>
For ```onMoves``` a _o._ and or _d._ can be prepended to path, pathname, sourcePath sourcePathname, targetPath and targetPathname to specify the move origin or destination. Without neither the variables refers to the move origin.
From the example above, it moves the file or directory in the target directory.
{% highlight lua %}
onMove = "mv ^o.targetPathname ^d.targetPathname",
{% endhighlight %}
Execution control (exit codes)
------------------------------
A few words on the startup of the example. It looks a little more complicated, but it is just some bash scripting, nothing Lsyncd specific. It simply does a recursive copy of the source to the target, but first tests if there is anything in the source file. Otherwise the command returns a non-zero error code.
{% highlight lua %}
onStartup = '[[if [ "$(ls -A ^source)" ]; then cp -r ^source* ^target; fi]],
{% endhighlight %}
By default Lsyncd ignores all exit codes except onStartup which must return 0 for it to continue. You can change this behavior by adding a ```exitcodes``` table.
{% highlight lua %}
exitcodes = {[0] = "ok", [1] = "again", [2] = "die"}
{% endhighlight %}
The keys specify for the exit code the string of the desired action.
<table>
<tr><td> again
</td><td> respawns the action after {{delay}} seconds, or 1 second if delay is immediate
</td></tr>
<tr><td> die
</td><td> lets Lsyncd terminate.
</td></tr>
</table>
All other values let Lsyncd continue normally.

View File

@ -0,0 +1,735 @@
---
layout: default
title: "Config Layer 4: Default Config"
short: "Config Layer 4"
---
You can simply choose from a set of three default implementations which are: __rsync__, __rsyncssh__ and __direct__.
To sync a local directory using the default rsync behavior, just add this to a config file:
{% highlight lua %}
sync {
default.rsync,
source = "DIRNAME",
target = "DIRNAME"
}
{% endhighlight %}
The order of the arguments is of no importance. If target is a local directory, take care that it is an absolute pathname. You can add multiple syncs that way. The source directories may be identical or differ without problems. ```source``` is an universal parameter that must be given for every sync. All other ```sync``` parameters can differ depending on the behavior selected. Optionally you can override the default or settings values ```maxDelays``` or ```maxProcesses``` per _Sync_.
One can also skip the initial rsync process by setting the default ```init``` function to false:
{% highlight lua %}
sync {
default.rsync,
source = "DIRNAME",
target = "DIRNAME",
init = false
}
{% endhighlight %}
This is an optimization which can be dangerous; so, please use it only if you are sure that source and target are synchronized when Lsyncd is started.
The default behaviors you can select from are following:
Shared Settings
---------------
Following settings are shared between all defaults:
| Name | Description |
|-----------------|------------------|
| source | Source directory |
| crontab | See section `Periodic Full-Sync`|
default.rsync
-------------
The default rsync configuration will aggregate events up to ```delay``` seconds or 1000 separate uncollapsible events, which ever happens first. Then it will spawn one Rsync with a filter of all files that changed. The filter list is transmitted to Rsync trough a pipe. A call from Lsyncd to Rsync will thus look like this:
{% highlight shell %}
/usr/bin/rsync -ltsd --delete --include-from=- --exclude=* SOURCE TARGET
{% endhighlight %}
You can change the options Rsync is called and the Rsync binary that is call with the ```rsync``` parameter.
Example:
{% highlight lua %}
sync {
default.rsync,
source = "/home/user/src/",
target = "foohost.com:~/trg/",
delay = 15,
rsync = {
binary = "/usr/local/bin/rsync",
archive = true,
compress = true
}
}
{% endhighlight %}
Additional settings:
| Name | Description |
|-----------------|-------------|
| batchSizeLimit | Files larger then this limit should not be batched into on transfer. Only makes sense with processes > 1 which prevents rsyncssh |
Below is a table of options for the ```rsync``` parameter. Please have a look at the Rsync documentation for an in depth explanation.
<table>
<tr><td> <b>parameter</b>
</td><td> <b>=</b>
</td><td> <b>TYPE</b>
</td><td> <b>default value</b>
</td><td> <b>comment</b>
</td></tr>
<tr><td> acls
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> append
</td><td> =
</td><td> BOOL
</td><td> false
</td><td> (Lsyncd >= 2.2.0)
</td></tr>
<tr><td> append-verify
</td><td> =
</td><td> BOOL
</td><td> false
</td><td> (Lsyncd >= 2.2.0)
</td></tr>
<tr><td> archive
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> backup
</td><td> =
</td><td> BOOL
</td><td> false
</td><td> (Lsyncd >= 2.2.0)
</td></tr>
<tr><td> backup_dir
</td><td> =
</td><td> DIR
</td><td> false
</td><td> (Lsyncd >= 2.2.0)
</td></tr>
<tr><td> binary
</td><td> =
</td><td> FILENAME
</td><td> "/usr/bin/rsync"
</td><td> Lsyncd calls this binary as rsync
</td></tr>
<tr><td> checksum
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> chmod
</td><td> =
</td><td> STRING
</td><td>
</td><td> (Lsyncd >= 2.2.0)
</td></tr>
<tr><td> chown
</td><td> =
</td><td> USER:GROUP
</td><td>
</td><td> (Lsyncd >= 2.2.0)
</td></tr>
<tr><td> compress
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> copy_dirlinks
</td><td> =
</td><td> BOOL
</td><td> false
</td><td> (Lsyncd >= 2.2.0)
</td></tr>
<tr><td> copy_links
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> cvs_exclude
</td><td> =
</td><td> BOOL
</td><td>
</td></tr>
<tr><td> dry_run
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> exclude
</td><td> =
</td><td> PATTERN
</td><td>
</td><td> TABLE of PATTERNs also allowed
</td></tr>
<tr><td> excludeFrom
</td><td> =
</td><td> FILENAME
</td><td>
</td><td>
</td></tr>
<tr><td> executability
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> existing
</td><td> =
</td><td> BOOL
</td><td> false
</td><td> (Lsyncd >= 2.2.0)
</td></tr>
<tr><td> filter
</td><td> =
</td><td> TABLE of STRINGS
</td><td>
</td><td> (Lsyncd >= 2.2.3)
</td></tr>
<tr><td> group
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> groupmap
</td><td> =
</td><td> STRING
</td><td>
</td><td> (Lsyncd >= 2.2.0)
</td></tr>
<tr><td> hard_links
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> ignore_times
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> inplace
</td><td> =
</td><td> BOOL
</td><td> false
</td><td> (Lsyncd >= 2.1.6)
</td></tr>
<tr><td> ipv4
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> ipv6
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> links
</td><td> =
</td><td> BOOL
</td><td> true
</td><td>
</td></tr>
<tr><td> one_file_system
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> owner
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> password_file
</td><td> =
</td><td> FILENAME
</td><td>
</td><td> (Lsyncd >= 2.1.2)
</td></tr>
<tr><td> perms
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> protect_args
</td><td> =
</td><td> BOOL
</td><td> true
</td><td>
</td></tr>
<tr><td> prune_empty_dirs
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> quiet
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> rsh
</td><td> =
</td><td> COMMAND
</td><td>
</td><td>
</td></tr>
<tr><td> rsync_path
</td><td> =
</td><td> PATH
</td><td>
</td><td> (path to rsync on remote host)
</td></tr>
<tr><td> sparse
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> suffix
</td><td> =
</td><td> SUFFIX
</td><td>
</td><td> (Lsyncd >= 2.2.0)
</td></tr>
<tr><td> temp_dir
</td><td> =
</td><td> DIR
</td><td>
</td><td>
</td></tr>
<tr><td> times
</td><td> =
</td><td> BOOL
</td><td> true
</td><td>
</td></tr>
<tr><td> update
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> usermap
</td><td> =
</td><td> STRING
</td><td>
</td><td> (Lsyncd >= 2.2.0)
</td></tr>
<tr><td> verbose
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> whole_file
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> xattrs
</td><td> =
</td><td> BOOL
</td><td> false
</td><td>
</td></tr>
<tr><td> _extra
</td><td> =
</td><td> TABLE of STRINGS.
</td><td>
</td><td> If absolutely needed, additional arguments can be specified as a TABLE of STRINGS(example: <tt>{ "--omit-dir-times", "--omit-link-times" }</tt>). Note that the underscore highlights this as workaround. If you need something that is not covered by the above options, please request it via a feature request on the project website. Most notably, do not add -r for recursive or -a which implies recursive, since Lsyncd will handle that by itself. Additionally do not add -R for relative, which will ruin Lsyncd &lt;-&gt; Rsync communication.
</td></tr>
</table>
default.rsyncssh
----------------
This configuration differs from the standard rsync configuration in that it uses ssh commands to move files or directories locally at the target host instead of deleting and transferring again. This configuration does spawn Rsync processes like default.rsync but additionally will spawn ```/usr/bin/ssh HOST mv ORIGIN DESTINATION``` commands.
Different to default.rsync it does not take an uniform ```target``` parameter, but needs ```host``` and ```targetdir``` separated.
Rsync's options can be changed with the ```rsync``` parameter like in default.rsync described above.
Additional to that ssh can be configured via the ```ssh``` parameter.
<table>
<tr><td> binary
</td><td> =
</td><td> FILENAME
</td><td> Lsyncd calls this binary as ssh (default: /usr/bin/ssh)
</td></tr>
<tr><td> identityFile
</td><td> =
</td><td> FILE
</td><td> Uses this file to identify for public key authentication.
</td></tr>
<tr><td> options
</td><td> =
</td><td> TABLE
</td><td> A table of addition extended options to pass to ssh's -o option.
</td></tr>
<tr><td> port
</td><td> =
</td><td> PORT
</td><td> Adds --port=PORT to the ssh call.
</td></tr>
<tr><td> _extra
</td><td> =
</td><td> STRING TABLE
</td><td> Similar to rsync._extra this can be used as quick workaround if absolutely needed.
</td></tr>
</table>
Example:
{% highlight lua %}
settings {
logfile = "/var/log/lsyncd.log",
statusFile = "/var/log/lsyncd-status.log",
statusInterval = 20
}
sync {
default.rsyncssh,
source="/srcdir",
host="remotehost",
excludeFrom="/etc/lsyncd.exclude",
targetdir="/dstdir",
rsync = {
archive = true,
compress = false,
whole_file = false
},
ssh = {
port = 1234
}
}
{% endhighlight %}
Please note the comma between the ```rsync``` parameter set and the ```ssh``` parameter set.
__Caution__
If you are upgrading from 2.0.x, please notice that `settings` became a function from a variable, so you __MUST__ delete the equal sign '=' between `settings` and the `{`.
Lsyncd will call ```xargs``` on the remote host to handle multiple tasks in a single connection. Xargs options can be specified by the xargs parameter.
<table>
<tr><td> binary
</td><td> =
</td><td> FILENAME
</td><td> Lsyncd calls this binary as xargs on the remote host (default: /usr/bin/xargs)
</td></tr>
<tr><td> delimiter
</td><td> =
</td><td> DELIMITER
</td><td> delimiting character to separate filenames. By default the 0 character is used. Very old holds may need newline instead.
</td></tr>
<tr><td> _extra
</td><td> =
</td><td> STRING TABLE
</td><td> By default { '-0', 'rm -rf' }. Remove the -0 if you chose newline delimiter instead. Otherwise leave it as is.
</td></tr>
</table>
Example:
{% highlight lua %}
sync {
default.rsyncssh,
source = "/home/user/src/",
host = "foohost.com",
targetdir = "~/trg/",
}
{% endhighlight %}
default.direct
-------------
Default.direct can be used to keep two local directories in sync with better performance than using default.rsync. Default.direct uses (just like default.rsync) rsync on startup to initially synchronize the target directory with the source directory. However, during normal operation default.direct uses /bin/cp, /bin/rm and /bin/mv to keep the synchronization. All parameters are just like default.rsync.
Example:
{% highlight lua %}
sync {
default.direct,
source = "/home/user/src/",
target = "/home/user/trg/"
}
{% endhighlight %}
Exclusions
----------
Two additional parameters can be specified to sync{}:
<table>
<tr><td> excludeFrom
</td><td> =
</td><td> FILENAME
</td><td> loads exclusion rules from this file, on rule per line
</td></tr>
<tr><td> exclude
</td><td> =
</td><td> LIST
</td><td> loads exclusion rules from this list of strings
</td></tr>
</table>
Exclusion rules are modeled after rsync's exclusion patterns but are a bit simpler. Lsyncd supports these features:
* Generally if any segment of the pathname (see below Layer 3) of an event matches the text, it is excluded. E.g. the file "/bin/foo/bar" matches the rule "foo".
* If the rule starts with a slash, it will only be matched at the beginning of the pathname
* If the rule ends with a slash, it will only be matched at the end of a pathname
* ? matches any character that is not a slash.
* ```*``` matches zero or more characters that are not a slash
* ```**``` matches zero or more characters, this can be slashes.
Example:
{% highlight lua %}
sync {
default.rsync,
source = "/home/user/src/",
targetdir = "/home/user/dst/",
exclude = { '_.bak' , '_.tmp' }
}
{% endhighlight %}
Deletions
---------
By default Lsyncd will delete files on the target that are not present at the source since this is a fundamental part of the idea of keeping the target in sync with the source. However, many users requested exceptions for this, for various reasons, so all default implementations take ```delete``` as an additional parameter.
Valid values for ```delete``` are:
<table>
<tr><td> delete
</td><td> =
</td><td> true
</td><td> Default. Lsyncd will delete on the target whatever is not in the source. At startup and what's being deleted during normal operation.
</td></tr>
<tr><td> delete
</td><td> =
</td><td> false
</td><td> Lsyncd will not delete any files on the target. Not on startup nor on normal operation. (Overwrites are possible though)
</td></tr>
<tr><td> delete
</td><td> =
</td><td> 'startup'
</td><td> Lsyncd will delete files on the target when it starts up but not on normal operation.
</td></tr>
<tr><td> delete
</td><td> =
</td><td> 'running'
</td><td> Lsyncd will not delete files on the target when it starts up but will delete those that are removed during normal operation.
</td></tr>
</table>
Tunnels
-------
*New in: 2.3.0*
Lsyncd is able to start and manage external programs to provide a tunnel for data transfer.
Additionally it can spawn multiple connections and load-balance connections among them. A tunnel is created through the `tunnel` function.
```
{% highlight lua %}
sync {
default.rsync,
tunnel = tunnel {
command = {"ssh", "-N", "-L", "localhost:5432:localhost:873", "tunnel@testmachine"},
}
target = "rsync://localhost:5432/projects",
source = "/home/user/src/",
}
{% endhighlight %}
```
You can then set the shell for the tunnel user to `/bin/false` and configure the rsyncd server side appropriately.
Valid arguments for `tunnel` are:
| Argument | Description | Default | Valid values |
|------------|-------------------------------------------------------|-----------|-------------------------------|
| mode | Mode in which the tunnel is run | command | command, pool |
| command | _Required_ Command to run | nil | Table of arguments |
| parallel | How many connections to run. Only for pool mode | 1 | 1+ |
| retryDelay | Seconds to wait until tunnel is restarted | 10 | Number |
| readyDelay | Seconds after program start to consider tunnel up | 5 | Number |
| localhost | Name of the local host variable | localhost | String |
## Pool Mode
In pool mode, lsyncd allocates a new local port which is then passed as variable to the host command.
All variables can be substituded by `^variable` syntax.
See [../../../../examples/lrsyncssh-tunnel.lua](lrsyncssh-tunnel.lua) for a extended configuration.
### List of variables
| Name | Description |
|------------|-----------------------------------------------|
| localport | Port allocated for the selected connection |
| localhost | Local hostname used. Default localhost |
### Example
Run 2 tunnel ssh processes and 4 rsync processes at the same time. Use extra transfers for all files larger 30 MB.
```lua
sync {
default.rsync,
tunnel = tunnel {
command = {"ssh", "-N", "-L", "localhost:^localport:localhost:873", "user@testmachine"},
mode = "pool",
parallel = 2,
},
source = "/data/projects",
target = "rsync://localhost:^localport/projects",
delay = 5,
batchSizeLimit = 1024 * 1024 * 30,
maxProcesses = 4
}
```
This example will open 2 ssh connections for port forwarding and load balance 4 parallel rsync
processes in a round roubin fashion.
### Workflow in Poolmodel
When a sync with a tunnel parameter is started, all events are queued until the tunnel reaches the
`UP` state, which is when one successful tunnel process exists for at least `readyDelay` seconds.
Dead tunnel processes are automatically restarted. When the tunnel process count drops to 0, tunnel falls back to the
`CONNECTING` state. There is a `retryDelay` seconds delay between each attempt to restart the tunnel.
Once the tunnel is UP, a full transfer is initiated. Subsequent transfers are then load balanced over multiple connections.
### Notes on Poolmode
* Pool mode only works with `rsync` backend, since there is no way to prevent multiple transfers to the same file in a relieable way, the rsync backend only supports `maxProcesses = 1` which renders pool mode useless. Since the remote side rsync daemon can prevent file trashing, the rsync backend is safe.
## Periodic Full-Sync
*New in: 2.3.0*
It is possible to trigger a full sync command from within lsync with the crontab feature. This requires that [lua-crontab](https://github.com/logiceditor-com/lua-crontab) is installed on the system. The crontab configuration accepts a list of `crontab` patterns to which a full sync will be triggered.
```lua
sync {
...
crontab = {
-- does a full sync once a day at 3:00:01
"1 0 3 * * *",
},
...
}
```
### Field destination
Each field is seperated by `" "` and can contain multiple values seperated by `,`.
| FIELD | VALUES | SPECIAL CHARACTERS |
|--------------|-----------------|--------------------|
| Seconds | 0-59 | , - * |
| Minutes | 0-59 | , - * |
| Hours | 0-23 | , - * |
| Day of month | 1-31 | , - * |
| Month | 1-12 or JAN-DEC | , - * |
| Day of week | 0-6 or SUN-SAT | , - * |

View File

@ -0,0 +1,283 @@
---
layout: default
title: "Example: Auto-Image-Magic"
tab: "manual/examples"
---
This [example](..) is a layer 1 script to make a special "magic" directory in which image files will be converted automatically therein.
The full script:
{% highlight lua %}
local formats = { jpg = true, gif = true, png = true }
convert = {
delay = 0,
maxProcesses = 99,
action = function(inlet)
local event = inlet.getEvent()
if event.isdir then
-- ignores events on dirs
inlet.discardEvent(event)
return
end
-- extract extension and basefilename
local p = event.pathname
local ext = string.match(p, ".*%.([^.]+)$")
local base = string.match(p, "(.*)%.[^.]+$")
if not formats[ext] then
-- an unknown extenion
log("Normal", "not doing something on ."..ext)
inlet.discardEvent(event)
return
end
-- autoconvert on create and modify
if event.etype == "Create" or event.etype == "Modify" then
-- builds one bash command
local cmd = ""
-- do for all other extensions
for k, _ in pairs(formats) do
if k ~= ext then
-- excludes files to be created, so no
-- followup actions will occur
inlet.addExclude(base..'.'..k)
if cmd ~= "" then
cmd = cmd .. " && "
end
cmd = cmd..
'/usr/bin/convert "'..
event.source..p..'" "'..
event.source..base..'.'..k..
'" || /bin/true'
end
end
log("Normal", "Converting "..p)
spawnShell(event, cmd)
return
end
-- deletes all formats if you delete one
if event.etype == "Delete" then
-- builds one bash command
local cmd = ""
-- do for all other extensions
for k, _ in pairs(formats) do
if k ~= ext then
-- excludes files to be deleted, so no
-- followup actions will occur
inlet.addExclude(base..'.'..k)
if cmd ~= "" then
cmd = cmd .. " && "
end
cmd = cmd..
'rm "'..event.source..base..'.'..k..
'" || /bin/true'
end
end
log("Normal", "Deleting all "..p)
spawnShell(event, cmd)
return
end
-- ignores other events.
inlet.discardEvent(event)
end,
-----
-- Removes excludes when convertions are finished
--
collect = function(event, exitcode)
local p = event.pathname
local ext = string.match(p, ".*%.([^.]+)$")
local base = string.match(p, "(.*)%.[^.]+$")
local inlet = event.inlet
if event.etype == "Create" or
event.etype == "Modify" or
event.etype == "Delete"
then
for k, _ in pairs(formats) do
inlet.rmExclude(base..'.'..k)
end
end
end,
}
sync{convert, source="magicdir", recursive=false}
{% endhighlight %}
This creates a local table of all supported file formats. The file formats are used as keys.
{% highlight lua %}
local formats = { jpg=true, gif=true, png=true, }
{% endhighlight %}
Configures actions to be instant and there is unlimits the amount the conversion to be done at once. Well not unlimited but set the limit pretty high.
{% highlight lua %}
convert = {
delay = 0,
maxProcesses = 99,
{% endhighlight %}
This script uses the _layer 1_ inlet interface altough it greps only single events and not lists. It does this instead of _layer 2_ as it needs to do common operations for all kind of events.
{% highlight lua %}
action = function(inlet)
local event = inlet.getEvent()
{% endhighlight %}
Ignores directories. As using _layer 1_ it has to explicitly discard events it does not spawn actions for.
{% highlight lua %}
if event.isdir then
-- ignores events on dirs
inlet.discardEvent(event)
return
end
{% endhighlight %}
Uses Lua string patterns to extract the file extension from the rest - here called base.
{% highlight lua %}
-- extract extension and basefilename
local p = event.pathname
local ext = string.match(p, ".*%.([^.]+)$")
local base = string.match(p, "(.*)%.[^.]+$")
{% endhighlight %}
Looks the extension up in the formats table. This can be done, since formats are keys in that table. If not an image format it bails out.
{% highlight lua %}
if not formats[ext] then
-- an unknown extenion
log("Normal", "not doing something on ."..ext)
inlet.discardEvent(event)
return
end
{% endhighlight %}
Following actions will done on "Create" and "Modify" events.
{% highlight lua %}
-- autoconvert on create and modify
if event.etype == "Create" or event.etype == "Modify" then
{% endhighlight %}
This script builds a bash command using a string.
{% highlight lua %}
-- builds one bash command
local cmd = ""
{% endhighlight %}
It iterates for all image formats and excludes the one which is the source image.
{% highlight lua %}
-- do for all other extensions
for k, _ in pairs(formats) do
if k ~= ext then
{% endhighlight %}
This is a little trick. It creates Exclusions for the converted images. As this images are not placed in a target directory but right next to the source image in the source directory they would otherwise trigger Create actions as well.
{% highlight lua %}
-- excludes files to be created, so no
-- followup actions will occur
inlet.addExclude(base..'.'..k)
{% endhighlight %}
And for every image to be converted adds the calls to the arguments. It uses ```" || /bin/true "``` to let the shell continue if one conversion fails. In that it chains the conversion with '&&' they will be called sequentially.
{% highlight lua %}
if cmd ~= "" then
cmd = cmd .. " && "
end
cmd = cmd..
'/usr/bin/convert "'..
event.source..p..'" "'..
event.source..base..'.'..k..
'" || /bin/true'
{% endhighlight %}
And eventually it spawns the shell doing the conversions and is finished.
{% highlight lua %}
end
end
log("Normal", "Converting "..p)
spawnShell(event, cmd)
return
end
{% endhighlight %}
For deletions it does technically something similar, but it deletes all other file formats of the image.
{% highlight lua %}
-- deletes all formats if you delete one
if event.etype == "Delete" then
-- builds one bash command
local cmd = ""
-- do for all other extensions
for k, _ in pairs(formats) do
if k ~= ext then
-- excludes files to be deleted, so no
-- followup actions will occur
inlet.addExclude(base..'.'..k)
if cmd ~= "" then
cmd = cmd .. " && "
end
cmd = cmd..
'rm "'..event.source..base..'.'..k..
'" || /bin/true'
end
end
log("Normal", "Deleting all "..p)
spawnShell(event, cmd)
return
end
{% endhighlight %}
and not to forget to nicely discard all other events.
{% highlight lua %}
-- ignores other events.
inlet.discardEvent(event)
end,
{% endhighlight %}
collect is called when the conversions finished. It will remove the temporary excludes again.
{% highlight lua %}
-----
-- Removes excludes when convertions are finished
--
collect = function(event, exitcode)
local p = event.pathname
local ext = string.match(p, ".*%.([^.]+)$")
local base = string.match(p, "(.*)%.[^.]+$")
local inlet = event.inlet
if event.etype == "Create" or
event.etype == "Modify" or
event.etype == "Delete"
then
for k, _ in pairs(formats) do
inlet.rmExclude(base..'.'..k)
end
end
end,
{% endhighlight %}
And finally use the configuration to watch "magicdir".
{% highlight lua %}
sync{convert, source="magicdir", recursive=false}
{% endhighlight %}

View File

@ -0,0 +1,90 @@
---
layout: default
title: "Example: GForce"
tab: "manual/examples"
---
A Layer 3 [example](..) that forces a directory tree to be read/writeable by a group.
{% highlight lua %}
-----
-- User configuration file for lsyncd.
--
-- This example refers to a common problem in unix.
--
-- You have a shared directory for a set of users and you want
-- to ensure all users have read and write permissions on all
-- files in there. Unfortunally sometimes users mess with their
-- umask, and create files in there that are not read/write/deleteable
-- by others. Usually this involves frequent handfixes by a sysadmin,
-- or a cron job that recursively chmods/chowns the whole directory.
--
-- This is another approach to use lsyncd to continously fix permissions.
--
-- One second after a file is created/modified it checks for its permissions
-- and forces group permissions on it.
--
-- This example regards more the handcraft of bash scripting than lsyncd.
-- An alternative to this would be to load a Lua-Posix library and do the
-- permission changes right within the onAction handlers.
----
-- forces this group.
--
fgroup = "staff"
-----
-- script for all changes.
--
command =
-- checks if the group is the one enforced and sets them if not
'[[
perm=`stat -c %A ^sourcePathname`
if test `stat -c %G ^sourcePathname` != ]]..fgroup..'[[; then
/bin/chgrp ]]..fgroup..'[[ ^sourcePathname || /bin/true;
fi
]] ..
-- checks if the group permissions are rw and sets them
'[[
if test `expr match $perm "....rw"` = 0; then
/bin/chmod g+rw ^sourcePathname || /bin/true;
fi
]] ..
-- and forces the executable bit for directories.
'[[
if test -d ^sourcePathname; then
if test `expr match $perm "......x"` -eq 0; then
/bin/chmod g+x ^^sourcePathname || /bin/true;
fi
fi
]]
-- on startup recursively sets all group ownerships
-- all group permissions are set to 'rw'
-- and to executable flag for directories
--
-- the hash in the first line is important, otherwise due to the starting
-- slash, Lsyncd would think it is a call to the binary /bin/chgrp only
-- and would optimize the shell call away.
--
startup =
'[[#
/bin/chgrp -R ]]..fgroup..'[[ ^source || /bin/true &&
/bin/chmod -R g+rw ^source || /bin/true &&
/usr/bin/find ^source -type d | xargs chmod g+x
]]
gforce = {
maxProcesses = 99,
delay = 1,
onStartup = startup,
onAttrib = command,
onCreate = command,
onModify = command,
-- does nothing on moves, they won't change permissions
onMove = true,
}
sync{gforce, source="/path/to/share"}
{% endhighlight %}

View File

@ -0,0 +1,39 @@
---
layout: default
title: "Examples"
---
Layer 4 Examples
----------------
bash sync:
<pre>
sync{bash, source="/home/lonewolf/teste1", target="/home/lonewolf/teste2"}
</pre>
rsyncssh option:
<pre>
sync{default.rsyncssh,
source="/var/www/live_site_resources",
host="192.168.129.90",
targetdir="/var/www/live_site_resources",
delete="running",
exclude={ ".*", "*.tmp" },
rsync = {
compress = false,
checksums = false,
_extra = {"--bwlimit=50000"},
}
}
</pre>
Layer 3 Examples
----------------
* [GForce](gforce): forces a local directory tree to be read/writable by a group.
Layer 2 Examples
----------------
Layer 1 Examples
----------------
* [Auto Image Magic](auto-image-magic): creates a "magic" directory in which all images placed into will be converted to other file formats

View File

@ -0,0 +1,90 @@
---
layout: default
title: Invoking
---
As most Unix tools, Lsyncd will print a synopsis of its command line options when called with --help.
```console
lsyncd --help
lsyncd -help
```
The two hyphens are redundant for Lsyncd. It has no short one letter options and one hyphen will always result into the same as specifying two.
Also like most Unix tools, ```--version``` or ```-version``` will let Lsyncd print its version number.
```console
lsyncd -version
```
Lsyncd 2.1 is designed to be predominantly configured through a config file (see below). The config file can thus be the only command line option.
```console
lsyncd CONFIGFILE
```
Although for standard use or quick testing it can be cursorily configured by command line options. The following will keep a local source and destination directory in sync using rsync:
```console
lsyncd -rsync /home/USER/src /home/USER/dst
```
The target can here be anything that Rsync recognizes.
```console
lsyncd -rsync /home/USER/src remotehost:dst
```
Two (or more) targets are configured by calling -rsync twice (or several times).
```console
lsyncd -rsync /home/USER/src remotehost1:dst -rsync /home/USER/src remotehost2:dst
```
A disadvantage with Rsync synchronization is that normally directory and file moves result in a deletion of the move origin and a retransfer of the move destination of the wire. However, Lsyncd 2 can use ssh commands to move the directories and files locally on the target. To use this use ```-rsyncssh``` followed by the local source directory, the remote host and the target directory there. The REMOTEHOST can include a user like ```me@remotehost.com```.
```console
lsyncd -rsyncssh /home/USER/src REMOTEHOST TARGETDIR
```
When testing Lsyncd configurations ```-nodaemon``` is a pretty handy flag. With this option, Lsyncd will not detach and will not become a daemon. All log messages are additionally to the configured logging facilities printed on the console (_stdout_ and _stderr_).
```console
lsyncd -nodaemon CONFIGFILE
```
There is a difference in behaviour when running with -nodaemon. Lsyncd will not change its working directory to `/`, as it does when becoming a daemon. Thus relative targets like `./target` will work with `-nodaemon` but must be specified to absolute paths to work in daemon mode. The source directories will also be turned into absolute paths by Lsyncd. The reason targets are not resolved to absolute paths while sources are is because Lsyncd itself does not care about the format of the target specifier which can also be remote hosts, rsyncd modules, etc. It is opaquely handed to rsync. It cares about the observed directories though.
*New in 2.3.0*
To only trigger the initial full sync and not monitor the file system, you can use the `-onepass` option.
```console
lsyncd -onepass CONFIGFILE
```
## Logging
All log messages are sorted in categories. By default Lsyncd is scarce with log messages. You can turn Lsyncd into a motormouth by specifying ```-log all```.
```console
lsyncd -log all CONFIGFILE
```
This might easily become too much. A particularly useful category is "Exec" which will log the command lines of all processes Lsyncd spawns.
```console
lsyncd -log Exec CONFIGFILE
```
When the initial startup sync fails by default Lsyncd will terminate with an error message. It has been designed this way, so configuration failures are visibly reported to a possibly beginning user. However, in production a remote target might be done, but you want Lsyncd to start nevertheless and keep trying to sync to the remote target until it is up.
```console
lsyncd -insist -rsync /home/USER/src remotehost:dst
```
In production mode it is recommended to have insist on. It can also be specified in the settings{} command in a config file.

View File

@ -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,
@ -15,12 +15,12 @@ settings = {
-- for testing purposes. prefix can be used to slow commands down.
-- prefix = "sleep 5 && "
--
prefix = ""
local prefix = ""
-----
-- for testing purposes. uses bash command to hold local dirs in sync.
--
bash = {
local bash = {
delay = 0,
maxProcesses = 1,

View File

@ -1,14 +1,13 @@
-----
-- User configuration file for lsyncd.
--
-- This example uses local bash commands to keep two local
-- directory trees in sync.
-- This example uses just echos the operations
--
-----
-- for testing purposes. just echos what is happening.
--
echo = {
local echo = {
maxProcesses = 1,
delay = 1,
onStartup = "/bin/echo telling about ^source",

View File

@ -22,12 +22,12 @@
----
-- forces this group.
--
fgroup = "staff"
local fgroup = "staff"
-----
-- script for all changes.
--
command =
local command =
-- checks if the group is the one enforced and sets them if not
[[
perm=`stat -c %A ^sourcePathname`
@ -59,13 +59,13 @@ fi
-- the carret as first char tells Lsycnd to call a shell altough it
-- starts with a slash otherwisw
--
startup =
local startup =
[[^/bin/chgrp -R ]]..fgroup..[[ ^source || /bin/true &&
/bin/chmod -R g+rw ^source || /bin/true &&
/usr/bin/find ^source -type d | xargs chmod g+x
]]
gforce = {
local gforce = {
maxProcesses = 99,
delay = 1,
onStartup = startup,

View File

@ -12,7 +12,7 @@
--
local formats = { jpg=true, gif=true, png=true, }
convert = {
local convert = {
delay = 0,
maxProcesses = 99,

View File

@ -11,29 +11,43 @@ local rsyncpostcmd = {
-- based on default rsync.
default.rsync,
checkgauge = {
default.rsync.checkgauge,
host = true,
targetdir = true,
target = true,
postcmd = true,
},
-- for this config it is important to keep maxProcesses at 1, so
-- the postcmds will only be spawned after the rsync completed
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 event.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
@ -45,43 +59,60 @@ local rsyncpostcmd = {
-- 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")
end
error( 'this should never be reached' )
end,
-- called before anything else
-- builds the target from host and targetdir
prepare = function(config)
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)
return default.rsync.prepare(config, level, skipTarget)
end
}
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',
}

View File

@ -3,7 +3,7 @@
--
-- Simple example for default rsync.
--
settings = {
settings {
statusFile = "/tmp/lsyncd.stat",
statusInterval = 1,
}

View File

@ -0,0 +1,35 @@
settings {
logfile = "/home/lsync/job1/lsyncd.log",
statusFile = "/home/lsync/job1/lsyncd.status",
insist = true
}
sync {
default.rsyncssh,
source = "/data/projects",
host = "offsitehost",
targetdir = "/data/projects",
excludeFrom = "/home/lsync/job1/lsyncd.exclude",
delay = 5,
rsync = {
verbose = true,
inplace = true,
_extra = {
"--info=progress2"
}
},
ssh = {
identityFile = "/home/lsync/.ssh/id_rsa_new",
options = {
User = "poelzi",
StrictHostKeyChecking = "no",
Compression = "no",
Cipher = "aes256-gcm@openssh.com"
},
_extra = {
"-T",
"-c",
"aes256-gcm@openssh.com"
}
}
}

View File

@ -0,0 +1,33 @@
-- This is an advanced tunnel config that uses the tunnel, load balancing
-- and extra large file transfers
sync {
default.rsync,
tunnel = tunnel {
command = {"ssh", "-N", "-L", "localhost:${localport}:localhost:873", "user@testmachine"},
mode = "pool",
parallel = 2,
},
crontab = {
-- does a full sync once a day at 3:00:01
"1 0 3 * * *"
},
source = "/data/projects",
target = "rsync://localhost:${localport}/projects",
delay = 5,
batchSizeLimit = 1024 * 1024 * 30,
maxProcesses = 4,
rsync = {
inplace = true,
}
}
-- On your target machine configure rsyncd.conf like this:
-- [projects]
-- uid = myuser
-- gid = mygroup
-- path = /srv/projects
-- read only = false
-- If you restrict the ssh key or server to allow only port forwarding and no shell
-- this is a very secure setup

222
examples/ls3.lua Normal file
View File

@ -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 <danielkza2@gmail.com>
--
local 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
}
}

View File

@ -21,15 +21,16 @@ require("socket")
-- For demo reasons, do not detach
settings.nodaemon = true
hostname = "irc.freenode.org"
local hostname = "irc.freenode.org"
--hostname = "127.0.0.1"
port = 6667
nick = "lbot01"
chan = "##lfile01"
local port = 6667
local nick = "lbot01"
local chan = "##lfile01"
-- this blocks until the connection is established
-- for once lets say this ok since Lsyncd didnt yet actually
-- start.
--- @diagnostic disable-next-line: undefined-global
local ircSocket, err = socket.connect(hostname, port)
if not ircSocket then
log("Error", "Cannot connect to IRC: ", err)

43
flake.lock Normal file
View File

@ -0,0 +1,43 @@
{
"nodes": {
"flake-utils": {
"locked": {
"lastModified": 1659877975,
"narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"nixpkgs": {
"locked": {
"lastModified": 1666940097,
"narHash": "sha256-spcDvKqQU9iSMAjh5uj9gfu8Gu7vIFFkOKH2CCWvOVY=",
"owner": "nixos",
"repo": "nixpkgs",
"rev": "7269939a5d5610f2eec933607dc4d646394b29b8",
"type": "github"
},
"original": {
"owner": "nixos",
"ref": "release-22.05",
"repo": "nixpkgs",
"type": "github"
}
},
"root": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": "nixpkgs"
}
}
},
"root": "root",
"version": 7
}

174
flake.nix Normal file
View File

@ -0,0 +1,174 @@
{
description = "Lsyncd (Live Syncing Daemon)";
inputs.nixpkgs.url = "github:nixos/nixpkgs/release-22.05";
inputs.flake-utils.url = "github:numtide/flake-utils";
outputs = { self, nixpkgs, flake-utils }:
flake-utils.lib.eachDefaultSystem
(system:
let
pkgs = (import nixpkgs {
inherit system;
# Makes the config pure as well. See <nixpkgs>/top-level/impure.nix:
config = {
allowBroken = true;
};}); #.legacyPackages.${system};
defaultDeps = with pkgs; [
glib
rsync
openssh
];
nativeDeps = with pkgs; [
curl
asciidoc
jekyll
gcc
cmake
gnumake
];
version = builtins.elemAt
(builtins.match ''.*lsyncd_version = '([0-9\.]*)'.*''
(builtins.substring 0 1200
(builtins.readFile ./lsyncd.lua))) 0;
# mylua5_4 = pkgs.lua5_4.override({
# packageOverrides = luaself: luaprev: {
# luarocks = luaprev.luarocks-3_7;
# };
# });
luaposix35 = mylua: mylua.pkgs.buildLuarocksPackage {
pname = "luaposix";
lua = mylua;
version = "35.1-1";
knownRockspec = (pkgs.fetchurl {
url = "https://luarocks.org/luaposix-35.1-1.rockspec";
sha256 = "1n6c7qyabj2y95jmbhf8fxbrp9i73kphmwalsam07f9w9h995xh1";
}).outPath;
src = pkgs.fetchurl {
url = "http://github.com/luaposix/luaposix/archive/v35.1.zip";
sha256 = "1c03chkzwr2p1wd0hs1bafl2890fqbrfc3qk0wxbd202gc6128zi";
};
#
propagatedBuildInputs = [ mylua ];
meta = {
homepage = "http://github.com/luaposix/luaposix/";
description = "Lua bindings for POSIX";
license.fullName = "MIT/X11";
};
};
buildExtensions = luapkgs: (
let
nucleo = luapkgs.buildLuarocksPackage {
pname = "lua-nucleo";
version = "1.1.0-1";
knownRockspec = (pkgs.fetchurl {
url = "https://luarocks.org/lua-nucleo-1.1.0-1.rockspec";
sha256 = "02ly51wav1pxiahf6lflr4vks550bisdq4ir9cy1lxn9v2zmcbim";
}).outPath;
src = pkgs.fetchgit ( removeAttrs (builtins.fromJSON ''{
"url": "https://github.com/lua-nucleo/lua-nucleo.git",
"rev": "76835968ff30f182367abd58637560402990e0b1",
"date": "2021-04-26T11:51:34+03:00",
"path": "/nix/store/3ycmrh0j64qxm4f04yxmn3y42imc8bv5-lua-nucleo",
"sha256": "15kydmj64jhxv5ksayfgkwzmgzd7raj7xp636x8a7c3ybiirs90n",
"fetchSubmodules": true,
"deepClone": false,
"leaveDotGit": false
}
'') ["date" "path"]) ;
disabled = with luapkgs; (luaOlder "5.1");
meta = {
homepage = "http://github.com/lua-nucleo/lua-nucleo";
description = "A random collection of core and utility level Lua libraries";
license.fullName = "MIT/X11";
};
};
in
luapkgs.buildLuarocksPackage {
pname = "lua-crontab";
version = "1.0.0-1";
knownRockspec = (pkgs.fetchurl {
url = "https://luarocks.org/lua-crontab-1.0.0-1.rockspec";
sha256 = "1aynwxq488sxd2lyng4wnswfkqna5n07sfmdainlqlhcb6jan161";
}).outPath;
src = pkgs.fetchgit ( removeAttrs (builtins.fromJSON ''{
"url": "https://github.com/logiceditor-com/lua-crontab.git",
"rev": "e3929a572e8164f968da4dcbdf1c4464a2870699",
"date": "2021-07-29T14:12:08+03:00",
"path": "/nix/store/rsc49m4f1mjqbffaq7axcf31rgxxfjb3-lua-crontab",
"sha256": "0zkqslw3vg495k8g010cz931vlzfyynq4kcwi1jbbppia521z6rx",
"fetchSubmodules": true,
"deepClone": false,
"leaveDotGit": false
}
'') ["date" "path"]) ;
propagatedBuildInputs = [ nucleo ];
meta = {
homepage = "http://github.com/logiceditor-com/lua-crontab";
description = "Stores crontab-like rules for events and calculates timestamps for their occurrences";
license.fullName = "MIT/X11";
};
}
);
buildTypes = {
lua5_1 = [pkgs.lua5_1 pkgs.lua51Packages.luaposix (buildExtensions pkgs.lua51Packages)];
lua5_2 = [pkgs.lua5_2 pkgs.lua52Packages.luaposix (buildExtensions pkgs.lua52Packages)];
lua5_3 = [pkgs.lua5_3 pkgs.lua53Packages.luaposix (buildExtensions pkgs.lua53Packages)];
# lua5_4 = [mylua5_4pkgs.lua5_3 (luaposix35 mylua5_4)];
lua5_4 = [pkgs.lua5_4 (luaposix35 pkgs.lua5_4) (buildExtensions pkgs.lua5_4.pkgs)];
};
# buildTypes = {
# lua5_1 = [(pkgs.lua5_1.withPackages (ps: [ps.luaposix (buildExtensions pkgs.lua51Packages)]))];
# lua5_2 = [(pkgs.lua5_2.withPackages (ps: [ps.luaposix (buildExtensions pkgs.lua52Packages)]))];
# lua5_3 = [(pkgs.lua5_3.withPackages (ps: [ps.luaposix (buildExtensions pkgs.lua53Packages)]))];
# lua5_4 = [(pkgs.lua5_4.withPackages (ps: [ps.luaposix (buildExtensions pkgs.lua54Packages)]))];
# };
in
let
mkLsync = luaPackages: pkgs.stdenv.mkDerivation ({
inherit version;
name = "lsyncd";
src = ./.;
buildPhase = ''
make all manpage docs-html
'';
buildInputs = defaultDeps ++ luaPackages;
nativeBuildInputs = nativeDeps;
});
mkDev = extras: pkgs.mkShell {
propagatedBuildInputs = defaultDeps ++ extras;
};
in
{
packages = {
lsyncd = mkLsync buildTypes.lua5_3;
lsyncd_lua5_1 = mkLsync buildTypes.lua5_1;
lsyncd_lua5_2 = mkLsync buildTypes.lua5_2;
lsyncd_lua5_3 = mkLsync buildTypes.lua5_3;
lsyncd_lua5_4 = mkLsync buildTypes.lua5_4;
};
devShells = {
lsyncd = mkDev buildTypes.lua5_3;
lsyncd_lua5_1 = mkDev buildTypes.lua5_1;
lsyncd_lua5_2 = mkDev buildTypes.lua5_2;
lsyncd_lua5_3 = mkDev buildTypes.lua5_3;
lsyncd_lua5_4 = mkDev buildTypes.lua5_4;
};
defaultPackage = self.packages.${system}.lsyncd;
}
);
}

View File

@ -49,9 +49,11 @@
#define DEV_FSEVENTS "/dev/fsevents"
/* buffer for reading from the device */
#define FSEVENT_BUFSIZ 131072
#define FSEVENT_BUFSIZ 131072
/* limited by MAX_KFS_EVENTS */
#define EVENT_QUEUE_SIZE 4096
#define EVENT_QUEUE_SIZE 4096
#define KFS_NUM_ARGS FSE_MAX_ARGS
/* OS 10.5 structuce */
@ -151,6 +153,10 @@ handle_event(lua_State *L, struct kfs_event *event, ssize_t mlen)
const char *trg = NULL;
const char *etype = NULL;
int isdir = -1;
#ifdef LSYNCD_TARGET_APPLE \
// Since macOS 10.15, fsevent paths are prefixed with this string.
const char *const INOTIFY_PREFIX = "/System/Volumes/Data";
#endif
if (event->type == FSE_EVENTS_DROPPED) {
logstring("Fsevents", "Events dropped!");
@ -175,8 +181,13 @@ handle_event(lua_State *L, struct kfs_event *event, ssize_t mlen)
logstring("Fsevents", "contains dropped events");
}*/
} else {
printlogf(L, "Error", "unknown event(%d) in fsevents.",
atype);
printlogf(
L,
"Error",
"unknown event(%d) in fsevents.",
atype
);
exit(-1); // ERRNO
}
@ -258,6 +269,11 @@ handle_event(lua_State *L, struct kfs_event *event, ssize_t mlen)
lua_pushstring(L, etype);
lua_pushboolean(L, isdir);
l_now(L);
#ifdef LSYNCD_TARGET_APPLE
if (!strncmp(path, INOTIFY_PREFIX, strlen(INOTIFY_PREFIX ))) {
path += strlen(INOTIFY_PREFIX);
}
#endif
lua_pushstring(L, path);
if (trg) {
lua_pushstring(L, trg);

View File

@ -12,10 +12,6 @@
#include "lsyncd.h"
#ifndef HAVE_SYS_INOTIFY_H
# error Missing <sys/inotify.h>; supply kernel-headers and rerun configure.
#endif
#include <sys/stat.h>
#include <sys/times.h>
#include <sys/types.h>
@ -128,9 +124,9 @@ l_addwatch( lua_State *L )
// 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",
@ -143,7 +139,7 @@ l_addwatch( lua_State *L )
printlogf(
L, "Inotify",
"addwatch( %s )-> % d; err= %d : %s",
"addwatch( %s )-> %d; err= %d : %s",
path, wd, errno, strerror( errno )
);
}
@ -158,18 +154,18 @@ l_addwatch( lua_State *L )
/*
* 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;
}
@ -177,7 +173,8 @@ l_rmwatch(lua_State *L)
/*
| Lsyncd's core's inotify functions.
*/
static const luaL_Reg linotfylib[] = {
static const luaL_Reg linotfylib[ ] =
{
{ "addwatch", l_addwatch },
{ "rmwatch", l_rmwatch },
{ NULL, NULL}
@ -219,17 +216,18 @@ handle_event(
// 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
load_runner_func( L, "overflow" );
if( lua_pcall( L, 0, 0, -2 ) )
{
exit( -1 );
}
if( lua_pcall( L, 0, 0, -2 ) ) exit( -1 );
lua_pop( L, 1 );
hup = 1;
return;
}
@ -255,10 +253,10 @@ handle_event(
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
)
)
{
@ -267,18 +265,21 @@ handle_event(
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";
@ -294,10 +295,14 @@ handle_event(
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;
}
@ -350,6 +355,7 @@ handle_event(
}
lua_pushstring( L, event_type );
if( event_type != MOVE )
{
lua_pushnumber( L, event->wd );
@ -375,10 +381,7 @@ handle_event(
lua_pushnil( L );
}
if( lua_pcall( L, 7, 0, -9 ) )
{
exit( -1 );
}
if( lua_pcall( L, 7, 0, -9 ) ) exit( -1 );
lua_pop( L, 1 );
@ -394,6 +397,7 @@ handle_event(
| buffer to read inotify events into
*/
static size_t readbuf_size = 2048;
static char * readbuf = NULL;
@ -411,10 +415,7 @@ inotify_ready(
// sanity check
if( obs->fd != inotify_fd )
{
logstring(
"Error",
"internal failure, inotify_fd != ob->fd"
);
logstring( "Error", "internal failure, inotify_fd != obs->fd" );
exit( -1 );
}
@ -499,7 +500,7 @@ inotify_ready(
extern void
register_inotify( lua_State *L )
{
luaL_register( L, LSYNCD_INOTIFYLIBNAME, linotfylib );
lua_compat_register( L, LSYNCD_INOTIFYLIBNAME, linotfylib );
}
@ -515,11 +516,14 @@ inotify_tidy( struct observance *obs )
"Error",
"internal failure: inotify_fd != ob->fd"
);
exit( -1 );
}
close( inotify_fd );
free( readbuf );
readbuf = NULL;
}
@ -561,13 +565,6 @@ open_inotify( lua_State *L )
close_exec_fd( inotify_fd );
non_block_fd( inotify_fd );
observe_fd(
inotify_fd,
inotify_ready,
NULL,
inotify_tidy,
NULL
);
observe_fd( inotify_fd, inotify_ready, NULL, inotify_tidy, NULL );
}

689
lsyncd.c

File diff suppressed because it is too large Load Diff

View File

@ -12,11 +12,13 @@
#define LSYNCD_H
// some older machines need this to see pselect
#define _DEFAULT_SOURCE 1
#define _BSD_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 +33,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
*/
@ -41,6 +57,7 @@ extern struct settings {
int log_facility; // The syslog facility
int log_level; // -1 logs everything, 0 normal mode, LOG_ERROR errors only.
bool nodaemon; // True if Lsyncd shall not daemonize.
bool onepass; // True if Lsyncd should exit after first sync pass
char * pidfile; // If not NULL Lsyncd writes its pid into this file.
} settings;
@ -141,26 +158,18 @@ extern void observe_fd(
// stops the core to observe a file descriptor
extern void nonobserve_fd(int fd);
/**
/*
* inotify
*/
#ifdef LSYNCD_WITH_INOTIFY
#ifdef WITH_INOTIFY
extern void register_inotify(lua_State *L);
extern void open_inotify(lua_State *L);
#endif
/**
* fanotify
*/
#ifdef LSYNCD_WITH_FANOTIFY
extern void register_fanotify(lua_State *L);
extern void open_fanotify(lua_State *L);
#endif
/**
/*
* /dev/fsevents
*/
#ifdef LSYNCD_WITH_FSEVENTS
#ifdef WITH_FSEVENTS
extern void open_fsevents(lua_State *L);
#endif

6382
lsyncd.lua

File diff suppressed because it is too large Load Diff

54
tests/churn-direct.lua Executable file → Normal file
View File

@ -1,18 +1,18 @@
#!/usr/bin/lua
-- a heavy duty test.
-- makes thousends of random changes to the source tree
require('posix')
dofile('tests/testlib.lua')
require( 'posix' )
cwriteln('****************************************************************')
cwriteln(' Testing default.direct with random data activity ')
cwriteln('****************************************************************')
dofile( 'tests/testlib.lua' )
local tdir, srcdir, trgdir = mktemps()
cwriteln( '****************************************************************' )
cwriteln( ' Testing default.direct with random data activity ' )
cwriteln( '****************************************************************' )
local tdir, srcdir, trgdir = mktemps( )
-- makes some startup data
churn(srcdir, 10)
churn( srcdir, 10, false )
local logs = { }
--local logs = {'-log', 'Exec', '-log', 'Delay' }
@ -20,24 +20,36 @@ local pid = spawn(
'./lsyncd',
'-nodaemon',
'-direct', srcdir, trgdir,
unpack(logs)
table.unpack( logs )
)
cwriteln('waiting for Lsyncd to startup')
posix.sleep(1)
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)
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)
cwriteln('Exitcode of Lsyncd = ',exitmsg,' ',lexitcode)
cwriteln( 'killing the Lsyncd daemon' )
posix.kill( pid )
exitcode = os.execute('diff -r '..srcdir..' '..trgdir)
cwriteln('Exitcode of diff = "', exitcode, '"')
local _, exitmsg, lexitcode = posix.wait( pid )
cwriteln( 'Exitcode of Lsyncd = ', exitmsg, ' ', lexitcode )
if exitcode ~= 0 then os.exit(1) else os.exit(0) end
local result, code = execute( 'diff -r ' .. srcdir .. ' ' .. trgdir )
if result == 'exit'
then
cwriteln( 'Exitcode of diff = ', code )
else
cwriteln( 'Signal terminating diff = ', code )
end
if code ~= 0
then
os.exit( 1 )
else
os.exit( 0 )
end

55
tests/churn-rsync.lua Executable file → Normal file
View File

@ -1,50 +1,57 @@
#!/usr/bin/lua
-- a heavy duty test.
-- makes thousends of random changes to the source tree
require( "posix" )
dofile( "tests/testlib.lua" )
cwriteln( "****************************************************************" )
cwriteln( " Testing default.rsync with random data activity" )
cwriteln( "****************************************************************" )
require( 'posix' )
dofile( 'tests/testlib.lua' )
cwriteln( '****************************************************************' )
cwriteln( ' Testing default.rsync with random data activity' )
cwriteln( '****************************************************************' )
local tdir, srcdir, trgdir = mktemps( )
-- makes some startup data
churn( srcdir, 100 )
churn( srcdir, 100, true )
local logs = { }
-- logs = { "-log", "Delay", "-log", "Fsevents" }
local pid = spawn(
"./lsyncd",
"-nodaemon",
"-delay", "5",
"-rsync", srcdir, trgdir,
unpack( logs )
'./lsyncd',
'-nodaemon',
'-delay', '5',
'-rsync', srcdir, trgdir,
table.unpack( logs )
)
cwriteln( "waiting for Lsyncd to startup" )
cwriteln( 'waiting for Lsyncd to startup' )
posix.sleep( 1 )
churn( srcdir, 500 )
churn( srcdir, 500, false )
cwriteln( "waiting for Lsyncd to finish its jobs." )
cwriteln( 'waiting for Lsyncd to finish its jobs.' )
posix.sleep( 10 )
cwriteln( "killing the Lsyncd daemon" )
cwriteln( 'killing the Lsyncd daemon' )
posix.kill(pid)
local _, exitmsg, lexitcode = posix.wait( lpid )
cwriteln("Exitcode of Lsyncd = ", exitmsg, " ", lexitcode)
posix.kill( pid )
local _, exitmsg, lexitcode = posix.wait( pid )
cwriteln( 'Exitcode of Lsyncd = ', exitmsg, ' ', lexitcode )
exitcode = os.execute( "diff -r "..srcdir.." "..trgdir )
cwriteln( "Exitcode of diff = '", exitcode, "'" )
local result, code = execute( 'diff -r ' .. srcdir .. ' ' .. trgdir )
if exitcode ~= 0 then
os.exit(1)
if result == 'exit'
then
cwriteln( 'Exitcode of diff = ', code )
else
os.exit(0)
cwriteln( 'Signal terminating diff = ', code )
end
if code ~= 0
then
os.exit( 1 )
else
os.exit( 0 )
end

70
tests/churn-rsyncssh.lua Executable file → Normal file
View File

@ -1,47 +1,65 @@
#!/usr/bin/lua
-- a heavy duty test.
-- makes thousends of random changes to the source tree
require("posix")
dofile("tests/testlib.lua")
cwriteln("****************************************************************");
cwriteln(" Testing default.rsyncssh with random data activity");
cwriteln("****************************************************************");
cwriteln("( this test needs passwordless ssh localhost access )");
cwriteln("( for current user )");
require( 'posix' )
dofile( 'tests/testlib.lua' )
cwriteln( '****************************************************************' )
cwriteln( ' Testing default.rsyncssh with random data activity ' )
cwriteln( '****************************************************************' )
local tdir, srcdir, trgdir = mktemps()
-- makes some startup data
churn(srcdir, 100)
churn( srcdir, 5, true )
local logs = {}
logs = {"-log", "Delay" }
logs = { '-log', 'Delay' }
local pid = spawn("./lsyncd", "-nodaemon", "-delay", "5",
"-rsyncssh", srcdir, "localhost", trgdir,
unpack(logs))
local pid = spawn(
'./lsyncd',
'-nodaemon',
'-delay',
'5',
'-sshopts',
'-i tests/ssh/id_rsa -p 2468 -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null',
'-rsyncssh',
srcdir,
'localhost',
trgdir,
table.unpack(logs)
)
cwriteln("waiting for Lsyncd to startup")
posix.sleep(1)
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( 'waiting for Lsyncd to finish its jobs.' )
posix.sleep( 10 )
cwriteln( 'killing the Lsyncd daemon' )
cwriteln("killing the Lsyncd daemon")
posix.kill(pid)
local _, exitmsg, lexitcode = posix.wait(lpid)
cwriteln("Exitcode of Lsyncd = ", exitmsg, " ", lexitcode)
exitcode = os.execute("diff -r "..srcdir.." "..trgdir)
cwriteln("Exitcode of diff = '", exitcode, "'")
local _, exitmsg, lexitcode = posix.wait( pid )
if exitcode ~= 0 then
os.exit(1)
cwriteln( 'Exitcode of Lsyncd = ', exitmsg, ' ', lexitcode )
local result, code = execute( 'diff -r ' .. srcdir .. ' ' .. trgdir )
if result == 'exit'
then
cwriteln( 'Exitcode of diff = ', code )
else
os.exit(0)
cwriteln( 'Signal terminating diff = ', code )
end
if code ~= 0
then
os.exit( 1 )
else
os.exit( 0 )
end

21
tests/ci-run.sh Executable file
View File

@ -0,0 +1,21 @@
#!/usr/bin/env bash
set -ex
function cleanup() {
echo "** abort. cleanup ssh server"
cd `dirname $BASH_SOURCE`/..
# CLEANUP=`dirname $BASH_SOURCE`/../teardown.lua
lua tests/teardown.lua
}
trap cleanup INT EXIT
SRC=`pwd`
BUILD_FOLDER=`mktemp -d`
echo "Build folder: $BUILD_FOLDER"
cd $BUILD_FOLDER
cmake $SRC
make VERBOSE=1
make run-tests
rm -rf $BUILD_FOLDER

149
tests/cron-rsync.lua Normal file
View File

@ -0,0 +1,149 @@
require( 'posix' )
dofile( 'tests/testlib.lua' )
cwriteln( '****************************************************************' )
cwriteln( ' Testing crontab (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,
crontab = {
-- trigger full sync every 1 minute
"*/10 * * * * *",
},
source = "]]..srcdir..[[",
action = function (inlet)
local e = inlet.getEvent( );
print("inhibit action ".. e.path.. " = " .. e.etype);
if e.etype ~= "Full" then
inlet.discardEvent(e);
return;
end
return default.rsync.action(inlet);
end,
target = "]]..trgdir..[[",
delay = 2,
delete = true,
rsync = {
verbose = true,
inplace = true,
_extra = {
"-vv",
"--info=progress2"
}
},
filter = {
'- /xb**',
'+ /x**',
'- /**',
},
}
]])
-- writes all files
local function writefiles
( )
writefile( srcdir .. 'xbc', 'xbc' )
writefile( srcdir .. 'xcc', 'xcc' )
writefile( srcdir .. 'yaa', 'yaa' )
posix.mkdir( srcdir .. 'xbx' )
writefile( srcdir .. 'xbx/a', 'xbxa' )
posix.mkdir( srcdir .. 'xcx' )
writefile( srcdir .. 'xcx/x', 'xcxx' )
writefile( srcdir .. 'xda', 'xda', '700' )
writefile( srcdir .. 'xdb', 'xdb', '755' )
end
-- test all files
local function testfiles
( )
testfile( trgdir .. 'xbc', false )
testfile( trgdir .. 'xcc', true )
testfile( trgdir .. 'yaa', false )
testfile( trgdir .. 'xbx/a', false )
testfile( trgdir .. 'xcx/x', true )
testfile( trgdir .. 'xda', true )
testfile( trgdir .. 'xdb', true )
end
cwriteln( 'testing crontab' )
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( 20 )
local result, code = execute( 'diff -urN ' .. srcdir .. ' ' .. trgdir )
if result ~= 'exit' or code ~= 0
then
cwriteln( 'fail, target directory not empty!' )
posix.kill( pid )
os.exit( 1 )
end
cwriteln( 'writing files after startup' )
writefiles( )
cwriteln( 'waiting for Lsyncd to transmit changes' )
posix.sleep( 20 )
testfiles( )
cwriteln( 'killing started Lsyncd' )
posix.kill( pid )
local _, exitmsg, exitcode = posix.wait( pid )
cwriteln( 'Exitcode of Lsyncd = ', exitmsg, ' ', exitcode );
if exitcode == 143
then
cwriteln( 'OK' )
os.exit( 0 )
else
os.exit( 1 )
end
-- TODO remove temp

189
tests/exclude-rsync.lua Executable file → Normal file
View File

@ -1,115 +1,152 @@
#!/usr/bin/lua
require("posix")
dofile("tests/testlib.lua")
require( 'posix' )
dofile( 'tests/testlib.lua' )
cwriteln("****************************************************************")
cwriteln(" Testing excludes ")
cwriteln("****************************************************************")
cwriteln( '****************************************************************' )
cwriteln( ' Testing excludes (rsync)' )
cwriteln(' ****************************************************************' )
local tdir, srcdir, trgdir = mktemps()
local logfile = tdir .. "log"
local cfgfile = tdir .. "config.lua"
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 = {
logfile = "]]..logfile..[[",
nodaemon = true,
delay = 3,
settings {
logfile = "]]..logfile..[[",
nodaemon = true,
}
sync {
default.rsync,
default.rsync,
source = "]]..srcdir..[[",
target = "]]..trgdir..[[",
delay = 3,
exclude = {
"erf",
"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");
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);
--
-- 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("waiting for Lsyncd to start");
posix.sleep(3)
cwriteln("testing excludes after startup");
testfiles();
cwriteln("ok, removing sources");
cwriteln( 'testing startup excludes' )
if srcdir:sub(1,4) ~= "/tmp" then
writefiles( )
cwriteln( 'starting Lsyncd' )
local pid = spawn( './lsyncd', cfgfile, '-log', 'all' )
cwriteln( 'waiting for Lsyncd to start' )
posix.sleep( 3 )
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);
if os.execute("diff -urN "..srcdir.." "..trgdir) ~= 0 then
cwriteln("fail, target directory not empty!");
os.exit(1);
os.execute( 'rm -rf '..srcdir..'/*' )
cwriteln( 'waiting for Lsyncd to remove destination' )
posix.sleep( 5 )
local result, code = 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( 'writing files after startup' )
cwriteln( "killing started Lsyncd" );
posix.kill( pid );
local _, exitmsg, lexitcode = posix.wait( lpid );
cwriteln( "Exitcode of Lsyncd = ", exitmsg, " ", lexitcode );
writefiles( )
if lexitcode == 143 then
cwriteln( "OK" );
os.exit( 0 );
cwriteln( 'waiting for Lsyncd to transmit changes' )
posix.sleep( 5 )
testfiles( )
cwriteln( 'killing started Lsyncd' )
posix.kill( pid )
local _, exitmsg, exitcode = posix.wait( pid )
cwriteln( 'Exitcode of Lsyncd = ', exitmsg, ' ', exitcode );
if exitcode == 143
then
cwriteln( 'OK' )
os.exit( 0 )
else
os.exit( 1 );
os.exit( 1 )
end
-- TODO remove temp

171
tests/exclude-rsyncssh.lua Executable file → Normal file
View File

@ -1,15 +1,13 @@
#!/usr/bin/lua
require( 'posix' )
dofile( 'tests/testlib.lua' )
require('posix')
dofile('tests/testlib.lua')
cwriteln( '****************************************************************' );
cwriteln( ' Testing excludes (rsyncssh)' );
cwriteln( '****************************************************************' );
cwriteln( ' (this test needs passwordless ssh localhost access ' );
cwriteln( ' for current user)' );
cwriteln('****************************************************************');
cwriteln(' Testing excludes');
cwriteln('****************************************************************');
cwriteln(' (this test needs passwordless ssh localhost access ');
cwriteln(' for current user)');
local tdir, srcdir, trgdir = mktemps()
local tdir, srcdir, trgdir = mktemps( )
local logfile = tdir .. 'log'
local cfgfile = tdir .. 'config.lua'
local range = 5
@ -17,19 +15,27 @@ local log = {}
log = {'-log', 'all'}
writefile(cfgfile, [[
settings = {
logfile = ']]..logfile..[[',
nodaemon = true,
delay = 3,
settings {
logfile = ']]..logfile..[[',
nodaemon = true
}
sync {
default.rsyncssh,
default.rsyncssh,
delay = 3,
host = 'localhost',
ssh = {
port= 2468,
identityFile = "]] .. script_path() .. [[/ssh/id_rsa",
options = {
StrictHostKeyChecking="no",
UserKnownHostsFile="/dev/null",
},
},
source = ']]..srcdir..[[',
targetdir = ']]..trgdir..[[',
exclude = {
'erf',
'erf',
'/eaf',
'erd/',
'/ead/',
@ -37,79 +43,98 @@ sync {
}]]);
-- 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);
end
os.execute('rm -rf '..srcdir..'/*');
cwriteln('waiting for Lsyncd to remove destination');
posix.sleep(5);
if os.execute('diff -urN '..srcdir..' '..trgdir) ~= 0 then
cwriteln('fail, target directory not empty!');
os.exit(1);
cwriteln('exit before drama, srcdir is "', srcdir, '"')
os.exit( 1 )
end
cwriteln('writing files after startup');
writefiles();
cwriteln('waiting for Lsyncd to transmit changes');
posix.sleep(15);
testfiles();
os.execute( 'rm -rf ' .. srcdir .. '/*' )
cwriteln( 'waiting for Lsyncd to remove destination' )
posix.sleep( 5 )
cwriteln('killing started Lsyncd');
posix.kill(pid);
local _, exitmsg, lexitcode = posix.wait(lpid);
cwriteln('Exitcode of Lsyncd = ', exitmsg, ' ', lexitcode);
posix.sleep(1);
local result, code = execute( 'diff -urN '..srcdir..' '..trgdir )
if lexitcode == 143 then
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( 15 )
testfiles( )
cwriteln( 'killing started Lsyncd' )
posix.kill( pid )
local _, exitmsg, lexitcode = posix.wait( pid )
cwriteln( 'Exitcode of Lsyncd = ', exitmsg, ' ', lexitcode )
posix.sleep( 1 )
if lexitcode == 143
then
cwriteln( 'OK' );
os.exit( 0 );
else

146
tests/filter-rsync.lua Normal file
View File

@ -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 )
local result, code = 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( pid )
cwriteln( 'Exitcode of Lsyncd = ', exitmsg, ' ', exitcode );
if exitcode == 143
then
cwriteln( 'OK' )
os.exit( 0 )
else
os.exit( 1 )
end
-- TODO remove temp

91
tests/l4rsyncdata.lua Executable file → Normal file
View File

@ -1,34 +1,45 @@
#!/usr/bin/lua
require("posix")
dofile("tests/testlib.lua")
require( 'posix' )
dofile( 'tests/testlib.lua' )
cwriteln("****************************************************************")
cwriteln(" Testing layer 4 default rsync with simulated data activity ")
cwriteln("****************************************************************")
cwriteln( '****************************************************************' )
cwriteln( ' Testing layer 4 default rsync with simulated data activity ' )
cwriteln( '****************************************************************' )
local tdir, srcdir, trgdir = mktemps()
local logfile = tdir .. "log"
local logfile = tdir .. 'log'
local range = 5
local log = {"-log", "all"}
local log = { '-log', 'all' }
posix.mkdir(srcdir .. "d")
posix.mkdir(srcdir .. "d/e")
if not writefile(srcdir .. "d/e/f1", 'test') then
os.exit(1)
posix.mkdir( srcdir .. 'd' )
posix.mkdir( srcdir .. 'd/e' )
if not writefile( srcdir .. "d/e/f1", 'test' )
then
os.exit( 1 )
end
cwriteln("starting Lsyncd")
cwriteln( 'starting Lsyncd' )
logs = {}
local pid = spawn("./lsyncd", "-logfile", logfile, "-nodaemon", "-delay", "5",
"-rsync", srcdir, trgdir, unpack(logs))
cwriteln("waiting for lsyncd to start")
posix.sleep(2)
local logs = { }
local pid =
spawn(
'./lsyncd',
'-logfile', logfile,
'-nodaemon',
'-delay', '5',
"-rsync", srcdir, trgdir,
table.unpack( logs )
)
cwriteln("* making some data")
cwriteln("* creating d[x]/e/f2")
for i = 1, range do
cwriteln("[cp -r "..srcdir.."d "..srcdir.."d"..i.."]")
os.execute("cp -r "..srcdir.."d "..srcdir.."d"..i)
cwriteln( 'waiting for lsyncd to start' )
posix.sleep( 2 )
cwriteln( '* making some data' )
cwriteln( '* creating d[x]/e/f2' )
for i = 1, range
do
cwriteln( '[cp -r ' .. srcdir .. 'd ' .. srcdir .. 'd' .. i .. ']' )
os.execute( 'cp -r ' .. srcdir .. 'd ' .. srcdir .. 'd' .. i )
end
-- mkdir -p "$S"/m/n
@ -38,23 +49,31 @@ end
-- echo 'test4' > "$S"/m${i}/n/another
-- done
cwriteln("* waiting for Lsyncd to do its job.")
posix.sleep(10)
cwriteln( '* waiting for Lsyncd to do its job.' )
posix.sleep( 10 )
cwriteln("* killing Lsyncd")
cwriteln( '* killing Lsyncd' )
posix.kill(pid)
local _, exitmsg, lexitcode = posix.wait(lpid)
cwriteln("Exitcode of Lsyncd = ", exitmsg, " ", lexitcode)
posix.sleep(1)
posix.kill( pid )
local _, exitmsg, lexitcode = posix.wait(pid)
cwriteln( 'Exitcode of Lsyncd = ', exitmsg, ' ', lexitcode)
posix.sleep( 1 )
cwriteln("* differences:")
exitcode = os.execute("diff -urN "..srcdir.." "..trgdir)
cwriteln("Exitcode of diff = '", exitcode, "'")
if exitcode ~= 0 then
os.exit(1)
cwriteln( '* differences:' )
local result, code = execute( 'diff -urN ' .. srcdir .. ' ' .. trgdir )
if result == 'exit'
then
cwriteln( 'Exitcode of diff = "', code, '"')
else
os.exit(0)
cwriteln( 'Signal terminating diff = "', code, '"')
end
if result ~= 'exit' or code ~= 0
then
os.exit( 1 )
else
os.exit( 0 )
end
-- TODO remove temp

59
tests/schedule.lua Executable file → Normal file
View File

@ -1,10 +1,9 @@
#!/usr/bin/lua
require("posix")
dofile("tests/testlib.lua")
require( 'posix' )
dofile( 'tests/testlib.lua' )
cwriteln("****************************************************************")
cwriteln(" Testing Lsyncd scheduler ")
cwriteln("****************************************************************")
cwriteln( '****************************************************************' )
cwriteln( ' Testing Lsyncd scheduler ' )
cwriteln( '****************************************************************' )
local tdir, srcdir, trgdir = mktemps()
local logfile = tdir .. "log"
@ -12,10 +11,10 @@ local cfgfile = tdir .. "config.lua"
local logs = {"-log", "all" }
writefile(cfgfile, [[
settings = {
logfile = "]]..logfile..[[",
settings {
logfile = "]]..logfile..[[",
log = all,
nodaemon = true,
nodaemon = true,
maxProcesses = 1
}
@ -47,30 +46,36 @@ sync {ccircuit, source ="]]..srcdir..[[", target = "]]..trgdir..[["}
-- 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( pid )
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 )

9
tests/setup.lua Normal file
View File

@ -0,0 +1,9 @@
-- a heavy duty test.
-- makes thousends of random changes to the source tree
require( 'posix' )
dofile( 'tests/testlib.lua' )
cwriteln( ' Start Testsuite ' )
startSshd()

8
tests/teardown.lua Normal file
View File

@ -0,0 +1,8 @@
-- a heavy duty test.
-- makes thousends of random changes to the source tree
require( 'posix' )
dofile( 'tests/testlib.lua' )
stopSshd()

38
tests/test_actions.sh Executable file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env bash
if [ -z $1 ]; then
echo "usage: test_actions [directory]"
exit 1
fi
set -x
BLOBB1=$(realpath /tmp/src/../blobb1)
if [ ! -e $BLOBB1 ]; then
echo "create outside blobb $BLOBB1"
dd count=50 bs=1M if=/dev/urandom of=$BLOBB1
echo done
fi
while true; do
mkdir -p $1/testdir
sleep 3
touch $1/testfile
sleep 2
echo "blubb" >> $1/testfile
sleep 2
mv $1/testfile $1/testdir
sleep 2
ln $BLOBB1 $1/blubb1
ln $BLOBB1 $1/testdir/blubb2
ln $BLOBB1 $1/blubb3
sleep 30
rm $1/testdir/testfile
sleep 2
rm -rf $1/testdir
sleep 1
rm $1/blubb1
rm $1/blubb3
sleep 5
done

View File

@ -1,136 +1,333 @@
--- @diagnostic disable: lowercase-global, need-check-nil
-- common testing environment
require('posix')
posix = require( 'posix' )
string = require( 'string' )
local sys_stat = require "posix.sys.stat"
-- 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')
-- compatibility with 5.1
if table.unpack == nil then
--- @diagnostic disable-next-line: deprecated
table.unpack = unpack
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)
--
-- Writes colorized.
--
function cwriteln
(...)
io.write( c1, '++ ', ... )
io.write( c0, '\n' )
end
local lver = string.gmatch(_VERSION, "Lua (%d).(%d)")
_LUA_VERSION_MAJOR, _LUA_VERSION_MINOR = lver()
_LUA_VERSION_MAJOR = tonumber(_LUA_VERSION_MAJOR)
_LUA_VERSION_MINOR = tonumber(_LUA_VERSION_MINOR)
-- Compatibility execute function
function execute(...)
if _LUA_VERSION_MAJOR <= 5 and
_LUA_VERSION_MINOR < 2 then
local rv = os.execute(...)
return "exit", rv
else
local ok, why, code = os.execute(...)
return why, code
end
end
-----
-- creates a tmp directory
--
-- @returns the name of the directory
-- Initializes the pseudo random generator
--
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*$')
-- If environment variable 'SEED' is set,
-- that one is used seed.
--
local seed = tonumber(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' )
if f == nil then
return error("can't create testing directory")
end
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
-- Creates a tmp directory with the
-- typical lsyncd test architecture.
--
function mktemps()
local tdir = mktempd()..'/'
cwriteln('using ', tdir, ' as test root')
-- 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,
mode
)
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( )
if mode ~= nil then
posix.chmod(filename, mode)
end
return true
end
-----
-- spawns a subprocess.
function splitpath(P)
local i = #P
local ch = P:sub(i,i)
while i > 0 and ch ~= "/" do
i = i - 1
ch = P:sub(i,i)
end
if i == 0 then
return '',P
else
return P:sub(1,i-1), P:sub(i+1)
end
end
function isabs(p)
return string.sub(p, 1, 2) == "/"
end
function abspath(P,pwd)
P = P:gsub('[\\/]$','')
if not isabs(P) then
local rv = posix.unistd.getcwd() .. "/" .. P
return rv
end
return P
end
function script_path()
-- local str = debug.getinfo(2, "S").source:sub(2)
-- return str:match("(.*/)")
local dir, file = splitpath(abspath(debug.getinfo(1).short_src))
return dir
end
function which(exec)
local path = os.getenv("PATH")
for match in (path..':'):gmatch("(.-)"..':') do
local fname = match..'/'..exec
local s = sys_stat.stat(fname)
if s ~= nil then
return fname
end
end
end
--
-- @returns the processes pid
-- Starts test ssh server
--
function startSshd()
-- local f = io.open(script_path() .. "ssh/sshd.pid", 'r')
-- if f
-- then
-- return false
-- end
cwriteln(arg[0])
cwriteln(script_path() .. "ssh/sshd_config")
local sshdPath = script_path() .. "/ssh/"
if posix.stat( sshdPath ) == nil then
cwriteln("setup ssh server in " .. sshdPath)
posix.mkdir(sshdPath)
os.execute("ssh-keygen -t rsa -N '' -f" .. sshdPath .. "id_rsa")
os.execute("cp ".. sshdPath .. "id_rsa.pub ".. sshdPath .. "authorized_keys")
os.execute("ssh-keygen -t rsa -N '' -f ".. sshdPath .. "ssh_host_rsa_key")
cwriteln("done")
end
local f = io.open( sshdPath .. "sshd_config", 'w')
local cfg = [[
Port 2468
HostKey ]] .. sshdPath .. [[ssh_host_rsa_key
AuthorizedKeysFile ]] .. sshdPath .. [[authorized_keys
ChallengeResponseAuthentication no
UsePAM no
#Subsystem sftp /usr/lib/ssh/sftp-server
PidFile ]] .. sshdPath .. [[sshd.pid
]]
cwriteln("Use ssh config: "..cfg)
f:write(cfg)
f:close()
--local which = io.popen("which sshd")
exePath = which('sshd')
cwriteln("Using sshd: "..exePath)
local pid = spawn(exePath, "-D", "-e", "-f", sshdPath .. "sshd_config")
cwriteln( 'spawned sshd server: ' .. pid)
return true
end
function strip(s)
return s:match "^%s*(.-)%s*$"
end
--
-- Stop test ssh server
--
function stopSshd()
local f = io.open(script_path() .. "/ssh/sshd.pid", 'r')
if not f
then
return false
end
pid = strip(f:read("*a"))
posix.kill(tonumber(pid))
end
--
-- 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)
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,216 +335,371 @@ function churn(rootdir, n)
-- * 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( )
tdir[fn] = true
if not dirsWithFileD[tdir] then
dirsWithFileD[tdir] = true
table.insert(dirsWithFileI, tdir)
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
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
cwriteln('making random data')
local ndice = 0
for i, d in ipairs(dice) do
ndice = ndice + d[1]
d[1] = ndice
if init
then
dice =
{
{ 10, mkfile },
{ 10, mkdir },
}
else
dice =
{
{ 50, sleep },
{ 20, mkfile },
{ 20, mkdir },
{ 20, mvdir },
{ 20, rmfile },
}
end
for ai=1,n do
cwriteln( 'making random data' )
local ndice = 0
for i, d in ipairs( dice )
do
ndice = ndice + d[ 1 ]
d[ 1 ] = ndice
end
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
end
end
-- check if tables are equal
function isTableEqual(o1, o2, ignore_mt)
if o1 == o2 then return true end
local o1Type = type(o1)
local o2Type = type(o2)
if o1Type ~= o2Type then return false end
if o1Type ~= 'table' then return false end
if not ignore_mt then
local mt1 = getmetatable(o1)
if mt1 and mt1.__eq then
--compare using built in method
return o1 == o2
end
end
local keySet = {}
for key1, value1 in pairs(o1) do
local value2 = o2[key1]
if value2 == nil or isTableEqual(value1, value2, ignore_mt) == false then
return false
end
keySet[key1] = true
end
for key2, _ in pairs(o2) do
if not keySet[key2] then return false end
end
return true
end
--
-- Tests if the filename exists
-- fails if this is different to expect.
--
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

79
tests/utils_test.lua Normal file
View File

@ -0,0 +1,79 @@
dofile( 'tests/testlib.lua' )
cwriteln( '****************************************************************' )
cwriteln( ' Testing Utils Functions ' )
cwriteln( '****************************************************************' )
assert(isTableEqual(
lsyncd.splitQuotedString("-p 22 -i '/home/test/bla blu/id_rsa'"),
{"-p", "22", "-i", "/home/test/bla blu/id_rsa"}
))
-- test string replacement
local testData = {
localPort = 1234,
localHost = "localhorst"
}
assert(isTableEqual(
substitudeCommands({"-p^doesNotExist", "2^localHostA", "-i '^localPort'"}, testData),
{"-p^doesNotExist", "2localhorstA", "-i '1234'"}
))
assert(
substitudeCommands("-p^doesNotExist 2^localHostA -i '^localPort'", testData),
"-p^doesNotExist 2localhorstA -i '1234'"
)
assert(type(lsyncd.get_free_port()) == "number")
local function testQueue()
local q = Queue.new()
q:push(1)
q:push(2)
q:push(3)
q:push(4)
assert(q:size(), 4)
assert(q[1], 1)
assert(q[4], 4)
q:remove(4)
assert(q:size(), 3)
assert(q[3], 3)
assert(q[1], 1)
q:remove(1)
assert(q:size(), 2)
assert(q[3], 3)
assert(q[2], 2)
assert(q.first, 2)
assert(q.last, 3)
q:push(5)
assert(q:size(), 3)
assert(q.last, 4)
assert(q.first, 2)
assert(q[4], 5)
assert(q[3], 3)
assert(q[2], 2)
q:remove(3)
assert(q:size(), 2)
assert(q.last, 3)
assert(q.first, 2)
assert(q[2], 2)
assert(q[3], 5)
q:inject(23)
assert(q:size(), 3)
assert(q.last, 3)
assert(q.first, 1)
assert(q[1], 23)
assert(q[2], 2)
assert(q[3], 5)
end
testQueue()
os.exit(0)