mirror of
https://github.com/octoleo/lsyncd.git
synced 2024-12-04 19:03:17 +00:00
creating this thing
This commit is contained in:
parent
baa76c6eda
commit
e6f3d55321
15
_data/docs.yml
Normal file
15
_data/docs.yml
Normal file
@ -0,0 +1,15 @@
|
||||
- title: Manual
|
||||
dir: manual
|
||||
docs:
|
||||
- compiling
|
||||
- invoking
|
||||
- config/file
|
||||
- config/layer4
|
||||
- config/layer3
|
||||
- config/layer2
|
||||
- config/layer1
|
||||
- examples
|
||||
- title: Appendices
|
||||
dir: appendices
|
||||
docs:
|
||||
- devdocs
|
@ -6,21 +6,38 @@
|
||||
<link rel="stylesheet" type="text/css" href="/css/main.css">
|
||||
</head>
|
||||
<body>
|
||||
<div id="pillar">
|
||||
<nav>
|
||||
<ul>
|
||||
<li><a href="/">Home</a></li>
|
||||
<li><a href="/about">About</a></li>
|
||||
</ul>
|
||||
{% for section in site.data.docs %}
|
||||
<h4>{{section.title}}</h4>
|
||||
<ul>
|
||||
{% for doc in section.docs %}
|
||||
{% assign doc_url = doc | prepend:"/" | prepend: section.dir | prepend:"/" | append:"/" %}
|
||||
{% 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="{{ p.url }}">{{ title }}</a></li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
{% endfor %}
|
||||
</nav>
|
||||
<div class="container">
|
||||
|
||||
<div id="container">
|
||||
<h1>{{ page.title }}</h1>
|
||||
{{ content }}
|
||||
</div>
|
||||
|
||||
</div><!-- /.container -->
|
||||
<footer>
|
||||
</div>
|
||||
<!--footer>
|
||||
<ul>
|
||||
<li><a href="https://github.com/lsyncd">github.com/lsyncd</a></li>
|
||||
</ul>
|
||||
</footer>
|
||||
</footer-->
|
||||
</body>
|
||||
</html>
|
||||
|
46
appendices/devdocs/index.md
Normal file
46
appendices/devdocs/index.md
Normal 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.
|
134
css/main.css
Normal file
134
css/main.css
Normal file
@ -0,0 +1,134 @@
|
||||
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.8em auto;
|
||||
border-radius: 1em;
|
||||
max-width: 70em;
|
||||
height: auto;
|
||||
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: 11em;
|
||||
bottom: 0;
|
||||
top: 0;
|
||||
}
|
||||
|
||||
nav::before {
|
||||
content: "Lsyncd";
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
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 {
|
||||
border-left : 1px solid rgb( 220, 220, 220 )
|
||||
}
|
||||
|
||||
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; }
|
||||
|
||||
|
||||
pre {
|
||||
background-color: rgb( 245, 245, 245 );
|
||||
padding: 1em;
|
||||
border-radius: 1em;
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
---
|
||||
layout: default
|
||||
title: Lsyncd
|
||||
---
|
||||
<div class="blurb">
|
||||
<p>jekyll test</p>
|
||||
</div>
|
11
manual/compiling/index.md
Normal file
11
manual/compiling/index.md
Normal file
@ -0,0 +1,11 @@
|
||||
---
|
||||
layout: default
|
||||
title: Compiling
|
||||
---
|
||||
Building Lsyncd should be a straight forward process. Unpack the downloaded tar.gz file and run:
|
||||
|
||||
```shell
|
||||
cmake .
|
||||
make
|
||||
sudo make install
|
||||
```
|
93
manual/config/file/index.md
Normal file
93
manual/config/file/index.md
Normal file
@ -0,0 +1,93 @@
|
||||
---
|
||||
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.
|
||||
|
||||
```Lua
|
||||
settings {
|
||||
logfile = "/tmp/lsyncd.log",
|
||||
statusFile = "/tmp/lsyncd.status",
|
||||
nodaemon = true,
|
||||
}
|
||||
```
|
||||
|
||||
**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> true
|
||||
</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> 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>
|
164
manual/config/layer1/index.md
Normal file
164
manual/config/layer1/index.md
Normal file
@ -0,0 +1,164 @@
|
||||
---
|
||||
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:
|
||||
|
||||
```Lua
|
||||
action = function(inlet)
|
||||
local elist = inlet.getEvents()
|
||||
local config = inlet.getConfig()
|
||||
local paths = elist.getPaths()
|
||||
log("Normal", "rsyncing list\n", paths)
|
||||
spawn(elist, "/usr/bin/rsync",
|
||||
"<", paths,
|
||||
"--delete",
|
||||
config.rsyncOpts .. "d",
|
||||
"--include-from=-",
|
||||
"--exclude=*",
|
||||
config.source, config.target)
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```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,
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```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,
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```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.rsyncOpts .. "r",
|
||||
config.source,
|
||||
config.target)
|
||||
end,
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```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,
|
||||
```
|
94
manual/config/layer2/index.md
Normal file
94
manual/config/layer2/index.md
Normal 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.
|
||||
|
||||
```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
|
||||
}
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```Lua
|
||||
function spawnShell(agent, command, ...)
|
||||
return spawn(agent, "/bin/sh", "-c", command, "/bin/sh", ...)
|
||||
end
|
||||
```
|
||||
|
||||
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](Layer 1 Config) 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][l3-all-vars] |
|
||||
|event.pathname | see ^pathname of [Layer 3][l3-all-vars] |
|
||||
| event.source | see ^source of [Layer 3][l3-all-vars] |
|
||||
| event.sourcePath | see ^sourcePath of [Layer 3][l3-all-vars] |
|
||||
|event.sourcePathname | see ^sourcePathname of [Layer 3][l3-all-vars] |
|
||||
|event.target | see ^target of [Layer 3][l3-all-vars] |
|
||||
|event.targetPath | see ^targetPath of [Layer 3][l3-all-vars] |
|
||||
|event.targetPathname | see ^targetPathname of [Layer 3][l3-all-vars] |
|
||||
[l3-all-vars]:../wiki/Lsyncd-2.1.x-‖-Layer-3-Config-‖-Simple-onAction#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.
|
||||
|
||||
```Lua
|
||||
tattleMove = {
|
||||
onMove = function(oEvent, dEvent)
|
||||
log("Normal", "A moved happened from ",
|
||||
oEvent.pathname, " to ", dEvent.pathname)
|
||||
end,
|
||||
}
|
||||
```
|
||||
|
||||
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.
|
174
manual/config/layer3/index.md
Normal file
174
manual/config/layer3/index.md
Normal file
@ -0,0 +1,174 @@
|
||||
---
|
||||
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.
|
||||
|
||||
```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]]',
|
||||
}
|
||||
```
|
||||
|
||||
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 = { ... }.
|
||||
|
||||
```Lua
|
||||
bash = {
|
||||
...
|
||||
}
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```Lua
|
||||
delay = 5,
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
```Lua
|
||||
onCreate = "/usr/bin/zip /usr/var/all.zip ^sourceName"
|
||||
onModify = "/usr/bin/zip /usr/var/all.zip ^sourceName"
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```Lua
|
||||
onCreate = '[[ su user -c "/usr/bin/zip /usr/var/all.zip ^o.sourceName " ]],
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
```Lua
|
||||
onCreate = function(event)
|
||||
spawnShell('[[ su user -c "/usr/bin/zip /usr/var/all.zip \"$1\"" ]], event.sourceName)
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
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.
|
||||
```
|
||||
onMove = "mv ^o.targetPathname ^d.targetPathname",
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```Lua
|
||||
onStartup = '[[if [ "$(ls -A ^source)" ]; then cp -r ^source* ^target; fi]],
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```Lua
|
||||
exitcodes = {[0] = "ok", [1] = "again", [2] = "die"}
|
||||
```
|
||||
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.
|
450
manual/config/layer4/index.md
Normal file
450
manual/config/layer4/index.md
Normal file
@ -0,0 +1,450 @@
|
||||
---
|
||||
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:
|
||||
|
||||
```Lua
|
||||
sync {
|
||||
default.rsync,
|
||||
source = "DIRNAME",
|
||||
target = "DIRNAME"
|
||||
}
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```Lua
|
||||
sync {
|
||||
default.rsync,
|
||||
source = "DIRNAME",
|
||||
target = "DIRNAME",
|
||||
init = false
|
||||
}
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
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:
|
||||
|
||||
```shell
|
||||
/usr/bin/rsync -ltsd --delete --include-from=- --exclude=* SOURCE TARGET
|
||||
```
|
||||
|
||||
You can change the options Rsync is called and the Rsync binary that is call with the ```rsync``` parameter.
|
||||
|
||||
Example:
|
||||
|
||||
```Lua
|
||||
sync {
|
||||
default.rsync,
|
||||
source = "/home/user/src/",
|
||||
target = "foohost.com:~/trg/",
|
||||
delay = 15,
|
||||
rsync = {
|
||||
binary = "/usr/local/bin/rsync",
|
||||
archive = true,
|
||||
compress = true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
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> acls
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> binary
|
||||
</td><td> =
|
||||
</td><td> FILENAME
|
||||
</td><td> Lsyncd calls this binary as rsync (default: /usr/bin/rsync)
|
||||
</td></tr>
|
||||
|
||||
<tr><td> checksum
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> compress
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> copy_links
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> cvs_exclude
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> dry_run
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> exclude
|
||||
</td><td> =
|
||||
</td><td> PATTERN
|
||||
</td><td> TABLE of PATTERNs also allowed
|
||||
</td></tr>
|
||||
|
||||
<tr><td> excludeFrom
|
||||
</td><td> =
|
||||
</td><td> FILENAME
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> executability
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> hard_links
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> ignore_times
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> inplace
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td> (Lsyncd >= 2.1.6)
|
||||
</td></tr>
|
||||
|
||||
<tr><td> ipv4
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> ipv6
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
|
||||
<tr><td> links
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td> (set by Lsyncd by default)
|
||||
</td></tr>
|
||||
|
||||
<tr><td> one_file_system
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> owner
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> password_file
|
||||
</td><td> =
|
||||
</td><td> FILENAME
|
||||
</td><td> (Lsyncd >= 2.1.2)
|
||||
</td></tr>
|
||||
|
||||
<tr><td> perms
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> protect_args
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td> (set by Lsyncd by default)
|
||||
</td></tr>
|
||||
|
||||
<tr><td> prune_empty_dirs
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> quiet
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> rsh
|
||||
</td><td> =
|
||||
</td><td> COMMAND
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> rsync_path
|
||||
</td><td> =
|
||||
</td><td> PATH
|
||||
</td><td> (path to rsync on remote host)
|
||||
</td></tr>
|
||||
|
||||
<tr><td> sparse
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> temp_dir
|
||||
</td><td> =
|
||||
</td><td> DIR
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> times
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td> (set by Lsyncd by default)
|
||||
</td></tr>
|
||||
|
||||
<tr><td> update
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> verbose
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> whole_file
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> xattrs
|
||||
</td><td> =
|
||||
</td><td> true
|
||||
</td><td>
|
||||
</td></tr>
|
||||
|
||||
<tr><td> _extra
|
||||
</td><td> =
|
||||
</td><td> TABLE of STRINGS.
|
||||
</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 <-> 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> 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:
|
||||
```
|
||||
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
|
||||
}
|
||||
}
|
||||
```
|
||||
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, just guessed it, 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:
|
||||
|
||||
```Lua
|
||||
sync {
|
||||
default.rsyncssh,
|
||||
source = "/home/user/src/",
|
||||
host = "foohost.com",
|
||||
targetdir = "~/trg/",
|
||||
}
|
||||
```
|
||||
|
||||
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:
|
||||
|
||||
```Lua
|
||||
sync {
|
||||
default.direct,
|
||||
source = "/home/user/src/",
|
||||
target = "/home/user/trg/"
|
||||
}
|
||||
```
|
||||
|
||||
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 part 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:
|
||||
|
||||
```Lua
|
||||
sync {
|
||||
default.rsync,
|
||||
source = "/home/user/src/",
|
||||
targetdir = "/home/user/dst/",
|
||||
exclude = { '.bak' , '.tmp' }
|
||||
}
|
||||
```
|
||||
|
||||
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>
|
BIN
manual/examples/auto-image-magic/.index.md.swp
Normal file
BIN
manual/examples/auto-image-magic/.index.md.swp
Normal file
Binary file not shown.
283
manual/examples/auto-image-magic/index.md
Normal file
283
manual/examples/auto-image-magic/index.md
Normal 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:
|
||||
|
||||
```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}
|
||||
```
|
||||
|
||||
This creates a local table of all supported file formats. The file formats are used as keys.
|
||||
|
||||
```Lua
|
||||
local formats = { jpg=true, gif=true, png=true, }
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```Lua
|
||||
convert = {
|
||||
delay = 0,
|
||||
maxProcesses = 99,
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```Lua
|
||||
action = function(inlet)
|
||||
local event = inlet.getEvent()
|
||||
```
|
||||
|
||||
Ignores directories. As using _layer 1_ it has to explicitly discard events it does not spawn actions for.
|
||||
|
||||
```Lua
|
||||
if event.isdir then
|
||||
-- ignores events on dirs
|
||||
inlet.discardEvent(event)
|
||||
return
|
||||
end
|
||||
```
|
||||
|
||||
Uses Lua string patterns to extract the file extension from the rest - here called base.
|
||||
|
||||
```Lua
|
||||
-- extract extension and basefilename
|
||||
local p = event.pathname
|
||||
local ext = string.match(p, ".*%.([^.]+)$")
|
||||
local base = string.match(p, "(.*)%.[^.]+$")
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```Lua
|
||||
if not formats[ext] then
|
||||
-- an unknown extenion
|
||||
log("Normal", "not doing something on ."..ext)
|
||||
inlet.discardEvent(event)
|
||||
return
|
||||
end
|
||||
```
|
||||
|
||||
|
||||
Following actions will done on "Create" and "Modify" events.
|
||||
|
||||
```Lua
|
||||
-- autoconvert on create and modify
|
||||
if event.etype == "Create" or event.etype == "Modify" then
|
||||
```
|
||||
|
||||
This script builds a bash command using a string.
|
||||
|
||||
```Lua
|
||||
-- builds one bash command
|
||||
local cmd = ""
|
||||
```
|
||||
|
||||
It iterates for all image formats and excludes the one which is the source image.
|
||||
|
||||
```Lua
|
||||
-- do for all other extensions
|
||||
for k, _ in pairs(formats) do
|
||||
if k ~= ext then
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```Lua
|
||||
-- excludes files to be created, so no
|
||||
-- followup actions will occur
|
||||
inlet.addExclude(base..'.'..k)
|
||||
```
|
||||
|
||||
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.
|
||||
|
||||
```Lua
|
||||
if cmd ~= "" then
|
||||
cmd = cmd .. " && "
|
||||
end
|
||||
cmd = cmd..
|
||||
'/usr/bin/convert "'..
|
||||
event.source..p..'" "'..
|
||||
event.source..base..'.'..k..
|
||||
'" || /bin/true'
|
||||
```
|
||||
|
||||
And eventually it spawns the shell doing the conversions and is finished.
|
||||
|
||||
```Lua
|
||||
end
|
||||
end
|
||||
log("Normal", "Converting "..p)
|
||||
spawnShell(event, cmd)
|
||||
return
|
||||
end
|
||||
```
|
||||
|
||||
For deletions it does technically something similar, but it deletes all other file formats of the image.
|
||||
|
||||
```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
|
||||
```
|
||||
|
||||
and not to forget to nicely discard all other events.
|
||||
|
||||
```Lua
|
||||
-- ignores other events.
|
||||
inlet.discardEvent(event)
|
||||
end,
|
||||
```
|
||||
|
||||
collect is called when the conversions finished. It will remove the temporary excludes again.
|
||||
|
||||
```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,
|
||||
```
|
||||
|
||||
And finally use the configuration to watch "magicdir".
|
||||
|
||||
```Lua
|
||||
sync{convert, source="magicdir", recursive=false}
|
||||
```
|
90
manual/examples/gforce/index.md
Normal file
90
manual/examples/gforce/index.md
Normal 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.
|
||||
|
||||
```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"}
|
||||
```
|
39
manual/examples/index.md
Normal file
39
manual/examples/index.md
Normal 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
|
68
manual/invoking/index.md
Normal file
68
manual/invoking/index.md
Normal file
@ -0,0 +1,68 @@
|
||||
---
|
||||
layout: default
|
||||
title: Invoking
|
||||
---
|
||||
As most Unix tools, Lsyncd will print a synopsis of its command line options when called with --help.
|
||||
|
||||
```shell
|
||||
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.
|
||||
|
||||
```
|
||||
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.
|
||||
|
||||
```shell
|
||||
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:
|
||||
|
||||
```shell
|
||||
lsyncd -rsync /home/USER/src /home/USER/dst
|
||||
```
|
||||
|
||||
The target can here be anything that Rsync recognizes.
|
||||
|
||||
```shell
|
||||
lsyncd -rsync /home/USER/src remotehost:dst
|
||||
```
|
||||
|
||||
Two (or more) targets are configured by calling -rsync twice (or several times).
|
||||
|
||||
```shell
|
||||
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```.
|
||||
|
||||
```shell
|
||||
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_).
|
||||
|
||||
```shell
|
||||
lsyncd -nodaemon CONFIGFILE
|
||||
```
|
||||
|
||||
Note 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.
|
||||
|
||||
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```.
|
||||
|
||||
```shell
|
||||
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.
|
||||
|
||||
```shell
|
||||
lsyncd -log Exec CONFIGFILE
|
||||
```
|
Loading…
Reference in New Issue
Block a user