~ubuntu-branches/ubuntu/saucy/lsyncd/saucy-proposed

« back to all changes in this revision

Viewing changes to lsyncd.lua

  • Committer: Package Import Robot
  • Author(s): Jan Dittberner
  • Date: 2013-06-22 23:14:59 UTC
  • mfrom: (1.1.12)
  • Revision ID: package-import@ubuntu.com-20130622231459-ngiwwvd9u5xmt0iy
Tags: 2.1.5-1
* New upstream version (Closes: #707328).
* refresh debian/patches/dont_install_lua_as_docs.patch
* debian/control:
  - change Homepage to github page
  - switch to canonical Vcs-* URLs
  - bump Standards-Version to 3.9.4 (no changes)
* debian/rules:
  - add --add-missing to automake invocation to add missing test-driver and
    remove test-driver in clean target (Closes: #713793)
* debian/lsyncd.init:
  - don't source /lib/init/vars.sh and don't use $VERBOSE, don't use
    --make-pidfile
  - add --nicelevel to lsyncd invocation

Show diffs side-by-side

added added

removed removed

Lines of Context:
5
5
-- It works closely together with the Lsyncd core in lsyncd.c. This means it
6
6
-- cannot be runned directly from the standard lua interpreter.
7
7
--
 
8
-- This code assumes your editor is at least 100 chars wide.
 
9
--
8
10
-- License: GPLv2 (see COPYING) or any later version
9
11
-- Authors: Axel Kittenberger <axkibe@gmail.com>
10
12
--
13
15
-- require('profiler')
14
16
-- profiler.start()
15
17
 
16
 
-----
 
18
--
17
19
-- A security measurement.
 
20
-- The core will exit if version ids mismatch.
18
21
--
19
22
if lsyncd_version then
20
 
        -- checks if the runner is being loaded twice
21
 
        lsyncd.log('Error', 'You cannot use the lsyncd runner as configuration file!')
22
 
        lsyncd.terminate(-1) -- ERRNO
 
23
 
 
24
        -- ensures the runner is not being loaded twice
 
25
        lsyncd.log(
 
26
                'Error',
 
27
                'You cannot use the lsyncd runner as configuration file!'
 
28
        )
 
29
 
 
30
        lsyncd.terminate( -1 )
23
31
end
24
 
lsyncd_version = '2.0.7'
25
 
 
26
 
-----
 
32
 
 
33
lsyncd_version = '2.1.5'
 
34
 
 
35
--
 
36
-- Hides the core interface from user scripts.
27
37
--
28
38
local _l = lsyncd
29
39
lsyncd = nil
30
40
local lsyncd = _l
31
41
_l = nil
32
42
 
33
 
-----
 
43
--
34
44
-- Shortcuts (which user is supposed to be able to use them as well)
35
45
--
36
46
log       = lsyncd.log
37
47
terminate = lsyncd.terminate
38
48
now       = lsyncd.now
39
49
readdir   = lsyncd.readdir
 
50
 
 
51
--
 
52
-- Coping globals to ensure userscripts don't change this.
 
53
--
40
54
local log       = log
41
55
local terminate = terminate
42
56
local now       = now
43
57
 
44
 
------
 
58
--
45
59
-- Predeclarations
46
60
--
47
61
local Monitors
48
62
 
49
 
-----
 
63
--
50
64
-- Global: total number of processess running
 
65
--
51
66
local processCount = 0
52
67
 
 
68
--
 
69
-- Settings specified by command line.
 
70
--
 
71
local clSettings = { }
 
72
 
 
73
--
 
74
-- Settings specified by config scripts.
 
75
--
 
76
local uSettings = { }
 
77
 
 
78
--
 
79
-- A copy of the settings function to see if the
 
80
-- user script replaced the settings() by a table
 
81
-- ( pre Lsyncd 2.1 style )
 
82
--
 
83
local settingsSafe
 
84
 
53
85
--============================================================================
54
86
-- Lsyncd Prototypes
55
87
--============================================================================
56
88
 
57
 
-----
58
 
--
59
 
local Array = (function()
 
89
--
 
90
-- Array tables error if accessed with a non-number.
 
91
--
 
92
local Array = ( function( )
 
93
 
60
94
        -- Metatable
61
 
        local mt = {}
 
95
        local mt = { }
62
96
 
63
97
        -- on accessing a nil index.
64
 
        mt.__index = function(t, k)
 
98
        mt.__index = function( t, k )
65
99
                if type(k) ~= 'number' then
66
 
                        error('Key "'..k..'" invalid for Array', 2)
 
100
                        error( 'Key "'..k..'" invalid for Array', 2 )
67
101
                end
68
 
                return rawget(t, k)
 
102
                return rawget( t, k )
69
103
        end
70
104
 
71
105
        -- on assigning a new index.
72
 
        mt.__newindex = function(t, k, v)
73
 
                if type(k) ~= 'number' then
74
 
                        error('Key "'..k..'" invalid for Array', 2)
 
106
        mt.__newindex = function( t, k, v )
 
107
                if type( k ) ~= 'number' then
 
108
                        error( 'Key "'..k..'" invalid for Array', 2 )
75
109
                end
76
 
                rawset(t, k, v)
 
110
                rawset( t, k, v )
77
111
        end
78
112
 
79
113
        -- creates a new object
80
 
        local function new()
81
 
                local o = {}
82
 
                setmetatable(o, mt)
 
114
        local function new( )
 
115
                local o = { }
 
116
                setmetatable( o, mt )
83
117
                return o
84
118
        end
85
119
 
86
 
        -- objects public interface
87
 
        return {new = new}
88
 
end)()
89
 
 
90
 
 
91
 
-----
 
120
        --
 
121
        -- Public interface
 
122
        --
 
123
        return { new = new }
 
124
 
 
125
end )( )
 
126
 
 
127
 
 
128
--
 
129
-- Count array tables error if accessed with a non-number.
 
130
--
 
131
-- Additionally they maintain their length as 'size' attribute,
 
132
-- since Lua's # operator does not work on tables whose key values are not
92
133
-- strictly linear.
93
134
--
94
 
local CountArray = (function()
 
135
local CountArray = ( function( )
 
136
 
 
137
        --
95
138
        -- Metatable
96
 
        local mt = {}
97
 
 
98
 
        -----
99
 
        -- key to native table
100
 
        local k_nt = {}
101
 
 
102
 
        -----
103
 
        -- on accessing a nil index.
104
 
        mt.__index = function(t, k)
105
 
                if type(k) ~= 'number' then
106
 
                        error('Key "'..k..'" invalid for CountArray', 2)
 
139
        --
 
140
        local mt = { }
 
141
 
 
142
        --
 
143
        -- Key to native table
 
144
        --
 
145
        local k_nt = { }
 
146
 
 
147
        --
 
148
        -- On accessing a nil index.
 
149
        --
 
150
        mt.__index = function( t, k )
 
151
                if type( k ) ~= 'number' then
 
152
                        error( 'Key "'..k..'" invalid for CountArray', 2 )
107
153
                end
108
 
                return t[k_nt][k]
 
154
                return t[ k_nt ][ k ]
109
155
        end
110
156
 
111
 
        -----
112
 
        -- on assigning a new index.
113
 
        mt.__newindex = function(t, k, v)
 
157
        --
 
158
        -- On assigning a new index.
 
159
        --
 
160
        mt.__newindex = function( t, k, v )
 
161
 
114
162
                if type(k) ~= 'number' then
115
 
                        error('Key "'..k..'" invalid for CountArray', 2)
 
163
                        error( 'Key "'..k..'" invalid for CountArray', 2 )
116
164
                end
 
165
 
117
166
                -- value before
118
 
                local vb = t[k_nt][k]
 
167
                local vb = t[ k_nt ][ k ]
119
168
                if v and not vb then
120
169
                        t._size = t._size + 1
121
170
                elseif not v and vb then
122
171
                        t._size = t._size - 1
123
172
                end
124
 
                t[k_nt][k] = v
 
173
                t[ k_nt ][ k ] = v
125
174
        end
126
175
 
127
 
        -----
 
176
        --
128
177
        -- Walks through all entries in any order.
129
178
        --
130
 
        local function walk(self)
131
 
                return pairs(self[k_nt])
 
179
        local function walk( self )
 
180
                return pairs( self[ k_nt ] )
132
181
        end
133
182
 
134
 
        -----
135
 
        -- returns the count
136
 
        --
137
 
        local function size(self)
 
183
        --
 
184
        -- Returns the count
 
185
        --
 
186
        local function size( self )
138
187
                return self._size
139
188
        end
140
189
 
141
 
        -----
142
 
        -- creates a new count array
143
 
        --
144
 
        local function new()
 
190
        --
 
191
        -- Creates a new count array
 
192
        --
 
193
        local function new( )
 
194
 
145
195
                -- k_nt is native table, private for this object.
146
 
                local o = {_size = 0, walk = walk, size = size, [k_nt] = {} }
 
196
                local o = {
 
197
                        _size = 0,
 
198
                        walk = walk,
 
199
                        size = size,
 
200
                        [k_nt] = { }
 
201
                }
 
202
 
147
203
                setmetatable(o, mt)
148
204
                return o
149
205
        end
150
206
 
151
 
        -----
152
 
        -- public interface
153
 
        --
154
 
        return {new = new}
155
 
end)()
156
 
 
157
 
-----
158
 
--
159
 
--
160
 
Queue = (function()
161
 
        -----
 
207
 
 
208
        --
 
209
        -- Public interface
 
210
        --
 
211
        return { new = new }
 
212
end )( )
 
213
 
 
214
--
 
215
-- A queue is optimized for pushing on the right and poping on the left.
 
216
--
 
217
Queue = ( function( )
 
218
 
 
219
        --
162
220
        -- Creates a new queue.
163
221
        --
164
 
        local function new()
165
 
                return { first = 1, last = 0, size = 0};
 
222
        local function new( )
 
223
                return {
 
224
                        first = 1,
 
225
                        last  = 0,
 
226
                        size  = 0
 
227
                };
166
228
        end
167
229
 
168
 
        -----
 
230
        --
169
231
        -- Pushes a value on the queue.
170
232
        -- Returns the last value
171
233
        --
172
 
        local function push(list, value)
 
234
        local function push( list, value )
 
235
 
173
236
                if not value then
174
237
                        error('Queue pushing nil value', 2)
175
238
                end
 
239
 
176
240
                local last = list.last + 1
177
241
                list.last = last
178
 
                list[last] = value
 
242
                list[ last ] = value
179
243
                list.size = list.size + 1
180
244
                return last
181
245
        end
182
246
 
183
 
        -----
184
 
        -- Removes item at pos from Queue.
185
 
        --
186
 
        local function remove(list, pos)
187
 
                if list[pos] == nil then
 
247
        --
 
248
        -- Removes an item at pos from the Queue.
 
249
        --
 
250
        local function remove( list, pos )
 
251
 
 
252
                if list[ pos ] == nil then
188
253
                        error('Removing nonexisting item in Queue', 2)
189
254
                end
190
 
                list[pos] = nil
191
 
 
192
 
                -- if removing first element, move list on.
 
255
 
 
256
                list[ pos ] = nil
 
257
 
 
258
                -- if removing first or last element,
 
259
                -- the queue limits are adjusted.
193
260
                if pos == list.first then
 
261
 
194
262
                        local last = list.last
195
 
                        while list[pos] == nil and pos <= list.last do
 
263
 
 
264
                        while list[ pos ] == nil and pos <= list.last do
196
265
                                pos = pos + 1
197
266
                        end
 
267
 
198
268
                        list.first = pos
 
269
 
199
270
                elseif pos == list.last then
200
 
                        while list[pos] == nil and pos >= list.first do
 
271
 
 
272
                        while list[ pos ] == nil and pos >= list.first do
201
273
                                pos = pos - 1
202
274
                        end
 
275
 
203
276
                        list.last = pos
 
277
 
204
278
                end
205
279
 
206
 
                -- reset indizies if list is empty
 
280
                -- reset the indizies if the queue is empty
207
281
                if list.last < list.first then
208
282
                        list.first = 1
209
283
                        list.last  = 0
210
284
                end
 
285
 
211
286
                list.size = list.size - 1
212
287
        end
213
288
 
214
 
        -----
 
289
        --
215
290
        -- Queue iterator (stateless)
216
291
        --
217
 
        local function iter(list, pos)
 
292
        local function iter( list, pos )
 
293
 
218
294
                pos = pos + 1
219
 
                while list[pos] == nil and pos <= list.last do
 
295
 
 
296
                while list[ pos ] == nil and pos <= list.last do
220
297
                        pos = pos + 1
221
298
                end
 
299
 
222
300
                if pos > list.last then
223
301
                        return nil
224
302
                end
225
 
                return pos, list[pos]
 
303
 
 
304
                return pos, list[ pos ]
226
305
        end
227
306
 
228
 
        -----
229
 
        -- Reverse queue iterator. (stateless)
230
 
        --
231
 
        local function iterReverse(list, pos)
 
307
        --
 
308
        -- Reverse queue iterator (stateless)
 
309
        --
 
310
        local function iterReverse( list, pos )
 
311
 
232
312
                pos = pos - 1
 
313
 
233
314
                while list[pos] == nil and pos >= list.first do
234
315
                        pos = pos - 1
235
316
                end
 
317
 
236
318
                if pos < list.first then
237
319
                        return nil
238
320
                end
239
 
                return pos, list[pos]
 
321
 
 
322
                return pos, list[ pos ]
240
323
        end
241
324
 
242
 
        -----
 
325
        --
243
326
        -- Iteraters through the queue
244
 
        -- returning all non-nil pos-value entries
 
327
        -- returning all non-nil pos-value entries.
245
328
        --
246
 
        local function qpairs(list)
 
329
        local function qpairs( list )
247
330
                return iter, list, list.first - 1
248
331
        end
249
332
 
250
 
        -----
 
333
        --
251
334
        -- Iteraters backwards through the queue
252
 
        -- returning all non-nil pos-value entries
 
335
        -- returning all non-nil pos-value entries.
253
336
        --
254
 
        local function qpairsReverse(list)
 
337
        local function qpairsReverse( list )
255
338
                return iterReverse, list, list.last + 1
256
339
        end
257
340
 
258
 
        return {new = new,
259
 
                        push = push,
260
 
                        remove = remove,
261
 
                        qpairs = qpairs,
262
 
                        qpairsReverse = qpairsReverse}
263
 
end)()
 
341
        return {
 
342
                new = new,
 
343
                push = push,
 
344
                remove = remove,
 
345
                qpairs = qpairs,
 
346
                qpairsReverse = qpairsReverse
 
347
        }
 
348
end )( )
264
349
 
265
 
-----
 
350
--
266
351
-- Locks globals,
 
352
-- No more globals can be created after this
267
353
--
268
 
local function lockGlobals()
 
354
local function lockGlobals( )
 
355
 
269
356
        local t = _G
270
 
        local mt = getmetatable(t) or {}
271
 
        mt.__index = function(t, k)
272
 
                if (k~='_' and string.sub(k, 1, 2) ~= '__') then
273
 
                        error('Access of non-existing global "'..k..'"', 2)
 
357
        local mt = getmetatable( t ) or { }
 
358
 
 
359
        -- TODO try to remove the underscore exceptions
 
360
        mt.__index = function( t, k )
 
361
                if k ~= '_' and string.sub(k, 1, 2) ~= '__' then
 
362
                        error( 'Access of non-existing global "'..k..'"', 2 )
274
363
                else
275
 
                        rawget(t, k)
 
364
                        rawget( t, k )
276
365
                end
277
366
        end
278
 
        mt.__newindex = function(t, k, v)
279
 
                if (k~='_' and string.sub(k, 1, 2) ~= '__') then
 
367
 
 
368
        mt.__newindex = function( t, k, v )
 
369
                if k ~= '_' and string.sub( k, 1, 2 ) ~= '__' then
280
370
                        error('Lsyncd does not allow GLOBALS to be created on the fly. '..
281
371
                              'Declare "'..k..'" local or declare global on load.', 2)
282
372
                else
283
 
                        rawset(t, k, v)
 
373
                        rawset( t, k, v )
284
374
                end
285
375
        end
286
 
        setmetatable(t, mt)
 
376
 
 
377
        setmetatable( t, mt )
287
378
end
288
379
 
289
 
-----
290
 
--
291
 
local Delay = (function()
292
 
        -----
 
380
--
 
381
-- Holds the information about a delayed event for one Sync.
 
382
--
 
383
local Delay = ( function( )
 
384
 
 
385
        --
293
386
        -- Creates a new delay.
294
387
        --
295
 
        -- @params see below
 
388
        -- Params see below.
296
389
        --
297
 
        local function new(etype, sync, alarm, path, path2)
 
390
        local function new( etype, sync, alarm, path, path2 )
 
391
 
298
392
                local o = {
299
 
                        -----
 
393
                        --
300
394
                        -- Type of event.
301
395
                        -- Can be 'Create', 'Modify', 'Attrib', 'Delete' and 'Move'
 
396
                        --
302
397
                        etype = etype,
303
398
 
304
 
                        ------
305
 
                        -- Sync this delay belongs to
 
399
                        --
 
400
                        -- the Sync this delay belongs to
 
401
                        --
306
402
                        sync = sync,
307
403
 
308
 
                        -----
 
404
                        --
309
405
                        -- Latest point in time this should be catered for.
310
406
                        -- This value is in kernel ticks, return of the C's
311
407
                        -- times(NULL) call.
312
408
                        alarm = alarm,
313
409
 
314
 
                        -----
315
 
                        -- path and filename or dirname of the delay relative
 
410
                        --
 
411
                        -- Path and filename or dirname of the delay relative
316
412
                        -- to the syncs root.
 
413
                        --
317
414
                        -- for the directories it contains a trailing slash
318
415
                        --
319
 
                        path  = path,
 
416
                        path = path,
320
417
 
321
 
                        ------
322
 
                        -- only not nil for 'Move's.
323
 
                        -- path and file/dirname of a move destination.
 
418
                        --
 
419
                        -- Used only for Moves.
 
420
                        -- Path and file/dirname of a move destination.
 
421
                        --
324
422
                        path2  = path2,
325
423
 
326
 
                        ------
 
424
                        --
327
425
                        -- Status of the event. Valid stati are:
 
426
                        --
328
427
                        -- 'wait'    ... the event is ready to be handled.
 
428
                        --
329
429
                        -- 'active'  ... there is process running catering for this event.
 
430
                        --
330
431
                        -- 'blocked' ... this event waits for another to be handled first.
 
432
                        --
331
433
                        -- 'done'    ... event has been collected. This should never be
332
434
                        --               visible as all references should be droped on
333
 
                        --               collection, nevertheless seperat status for
334
 
                        --               insurrance.
 
435
                        --               collection, nevertheless the seperate status is
 
436
                        --               used as insurrance everything is running correctly.
335
437
                        status = 'wait',
336
438
 
337
 
                        -----
 
439
                        --
338
440
                        -- Position in the queue
 
441
                        --
339
442
                        dpos = -1,
340
443
                }
 
444
 
341
445
                return o
342
446
        end
343
447
 
344
 
        -- public interface
345
 
        return {new = new}
346
 
end)()
347
 
 
348
 
-----
349
 
--
350
 
local Combiner = (function()
351
 
 
352
 
        ----
353
 
        -- new delay absorbed by old
354
 
        --
355
 
        local function abso(d1, d2)
356
 
                log('Delay',d2.etype,':',d2.path,' absorbed by ',d1.etype,':',d1.path)
 
448
 
 
449
        --
 
450
        -- Public interface
 
451
        --
 
452
        return { new = new }
 
453
 
 
454
end )( )
 
455
 
 
456
--
 
457
-- Combines delays
 
458
--
 
459
local Combiner = ( function( )
 
460
 
 
461
        --
 
462
        -- The new delay is absorbed by an older one.
 
463
        --
 
464
        local function abso( d1, d2 )
 
465
 
 
466
                log(
 
467
                        'Delay',
 
468
                        d2.etype, ':',d2.path,
 
469
                        ' absorbed by ',
 
470
                        d1.etype,':',d1.path
 
471
                )
 
472
 
357
473
                return 'absorb'
 
474
 
358
475
        end
359
476
 
360
 
        ----
361
 
        -- new delay replaces the old one if it is a file
362
 
        --
363
 
        local function refi(d1, d2)
364
 
                if d2.path:byte(-1) == 47 then
365
 
                        log('Delay',d2.etype,':',d2.path,' blocked by ',d1.etype,':',d1.path)
 
477
        --
 
478
        -- The new delay replaces the old one if it's a file
 
479
        --
 
480
        local function refi( d1, d2 )
 
481
 
 
482
                -- but a directory blocks
 
483
                if d2.path:byte( -1 ) == 47 then
 
484
 
 
485
                        log(
 
486
                                'Delay',
 
487
                                d2.etype,':',d2.path,
 
488
                                ' blocked by ',
 
489
                                d1.etype,':',d1.path
 
490
                        )
 
491
 
366
492
                        return 'stack'
 
493
 
367
494
                end
368
 
                log('Delay',d2.etype,':',d2.path,' replaces ',d1.etype,':',d1.path)
369
 
                return 'replace'
370
 
        end
371
 
 
372
 
        ----
373
 
        -- new delay replaces the old one
374
 
        --
375
 
        local function repl(d1, d2)
376
 
                log('Delay',d2.etype,':',d2.path,' replaces ',d1.etype,':',d1.path)
377
 
                return 'replace'
378
 
        end
379
 
 
380
 
        ----
381
 
        -- delays nullificate each other
382
 
        --
383
 
        local function null(d1, d2)
384
 
                log('Delay',d2.etype,':',d2.path,' nullifies ',d1.etype,':',d1.path)
 
495
 
 
496
                log(
 
497
                        'Delay',
 
498
                        d2.etype, ':', d2.path,
 
499
                        ' replaces ',
 
500
                        d1.etype, ':', d1.path
 
501
                )
 
502
 
 
503
                return 'replace'
 
504
 
 
505
        end
 
506
 
 
507
        --
 
508
        -- The new delay replaces an older one.
 
509
        --
 
510
        local function repl( d1, d2 )
 
511
 
 
512
                log(
 
513
                        'Delay',
 
514
                        d2.etype, ':', d2.path,
 
515
                        ' replaces ',
 
516
                        d1.etype, ':', d1.path
 
517
                )
 
518
 
 
519
                return 'replace'
 
520
 
 
521
        end
 
522
 
 
523
        --
 
524
        -- Two delays nullificate each other.
 
525
        --
 
526
        local function null( d1, d2 )
 
527
 
 
528
                log(
 
529
                        'Delay',
 
530
                        d2.etype,':',d2.path,
 
531
                        ' nullifies ',
 
532
                        d1.etype,':',d1.path
 
533
                )
 
534
 
385
535
                return 'remove'
 
536
 
386
537
        end
387
538
 
388
 
        -----
389
 
        -- Table how to combine events that dont involve a move.
 
539
        --
 
540
        -- Table on how to combine events that dont involve a move.
390
541
        --
391
542
        local combineNoMove = {
392
 
                Attrib = {Attrib=abso, Modify=repl, Create=repl, Delete=repl },
393
 
                Modify = {Attrib=abso, Modify=abso, Create=repl, Delete=repl },
394
 
                Create = {Attrib=abso, Modify=abso, Create=abso, Delete=repl },
395
 
                Delete = {Attrib=abso, Modify=abso, Create=refi, Delete=abso },
 
543
 
 
544
                Attrib = {
 
545
                        Attrib = abso,
 
546
                        Modify = repl,
 
547
                        Create = repl,
 
548
                        Delete = repl
 
549
                },
 
550
 
 
551
                Modify = {
 
552
                        Attrib = abso,
 
553
                        Modify = abso,
 
554
                        Create = repl,
 
555
                        Delete = repl
 
556
                },
 
557
 
 
558
                Create = {
 
559
                        Attrib = abso,
 
560
                        Modify = abso,
 
561
                        Create = abso,
 
562
                        Delete = repl
 
563
                },
 
564
 
 
565
                Delete = {
 
566
                        Attrib = abso,
 
567
                        Modify = abso,
 
568
                        Create = refi,
 
569
                        Delete = abso
 
570
                },
396
571
        }
397
572
 
398
 
        ------
399
 
        -- combines two delays
400
 
        --
401
 
        local function combine(d1, d2)
 
573
        --
 
574
        -- Combines two delays
 
575
        --
 
576
        local function combine( d1, d2 )
 
577
 
402
578
                if d1.etype == 'Init' or d1.etype == 'Blanket' then
 
579
 
403
580
                        -- everything is blocked by init or blanket delays.
404
581
                        if d2.path2 then
405
 
                                log('Delay',d2.etype,':',d2.path,'->',d2.path2,'blocked by',d1.etype,' event')
 
582
                                log(
 
583
                                        'Delay',
 
584
                                        d2.etype,':',d2.path,'->',d2.path2,
 
585
                                        ' blocked by ',
 
586
                                        d1.etype,' event'
 
587
                                )
406
588
                        else
407
 
                                log('Delay',d2.etype,':',d2.path,'blocked by',d1.etype,' event')
 
589
                                log(
 
590
                                        'Delay',
 
591
                                        d2.etype,':',d2.path,
 
592
                                        ' blocked by ',
 
593
                                        d1.etype,' event'
 
594
                                )
408
595
                        end
 
596
 
409
597
                        return 'stack'
410
598
                end
411
599
 
412
 
                -- Two normal events
 
600
                -- two normal events
413
601
                if d1.etype ~= 'Move' and d2.etype ~= 'Move' then
 
602
 
414
603
                        if d1.path == d2.path then
415
604
                                if d1.status == 'active' then
416
605
                                        return 'stack'
417
606
                                end
418
 
                                return combineNoMove[d1.etype][d2.etype](d1, d2)
 
607
 
 
608
                                return combineNoMove[ d1.etype ][ d2.etype ]( d1, d2 )
419
609
                        end
420
610
 
421
 
                        -- blocks events if one is a parent directory of another
 
611
                        -- if one is a parent directory of another, events are blocking
422
612
                        if d1.path:byte(-1) == 47 and string.starts(d2.path, d1.path) or
423
613
                           d2.path:byte(-1) == 47 and string.starts(d1.path, d2.path)
424
614
                        then
425
615
                                return 'stack'
426
616
                        end
 
617
 
427
618
                        return nil
 
619
 
428
620
                end
429
621
 
430
 
                -- Normal upon a Move
 
622
                -- non-move event on a move.
431
623
                if d1.etype == 'Move' and d2.etype ~= 'Move' then
432
 
                        -- stacks the move if the from field could anyway be damaged
 
624
                        -- if the from field could be damaged the events are stacked
433
625
                        if d1.path == d2.path or
434
626
                           d2.path:byte(-1) == 47 and string.starts(d1.path, d2.path) or
435
627
                           d1.path:byte(-1) == 47 and string.starts(d2.path, d1.path)
436
628
                        then
437
 
                                log('Delay',d2.etype,':',d2.path,' blocked by','Move :',d1.path,'->',d1.path2)
 
629
                                log(
 
630
                                        'Delay',
 
631
                                        d2.etype, ':', d2.path,
 
632
                                        ' blocked by ',
 
633
                                        'Move :', d1.path,'->', d1.path2
 
634
                                )
 
635
 
438
636
                                return 'stack'
439
637
                        end
440
638
 
441
 
                        --  Event does something with the move destination
 
639
                        --  the event does something with the move destination
 
640
 
442
641
                        if d1.path2 == d2.path then
 
642
 
443
643
                                if d2.etype == 'Delete' or d2.etype == 'Create' then
 
644
 
444
645
                                        if d1.status == 'active' then
445
646
                                                return 'stack'
446
647
                                        end
447
 
                                        log('Delay',d2.etype,':',d2.path,' turns ',
448
 
                                        'Move :',d1.path,'->',d1.path2,' into ','Delete:',d1.path)
 
648
 
 
649
                                        log(
 
650
                                                'Delay',
 
651
                                                d2.etype, ':', d2.path,
 
652
                                                ' turns ',
 
653
                                        'Move :', d1.path, '->', d1.path2,
 
654
                                                ' into ',
 
655
                                                'Delete:', d1.path
 
656
                                        )
449
657
                                        d1.etype = 'Delete'
450
658
                                        d1.path2 = nil
 
659
 
451
660
                                        return 'stack'
452
661
                                end
453
 
                                -- on 'Attrib' or 'Modify' simply wait for the move first
 
662
 
 
663
                                -- on 'Attrib' or 'Modify' simply stack on moves
 
664
 
454
665
                                return 'stack'
455
666
                        end
456
667
 
457
668
                        if d2.path :byte(-1) == 47 and string.starts(d1.path2, d2.path) or
458
669
                           d1.path2:byte(-1) == 47 and string.starts(d2.path,  d1.path2)
459
670
                        then
460
 
                                log('Delay',d2.etype,':',d2.path,' blocked by ','Move:',d1.path,'->',d1.path2)
 
671
                                log(
 
672
                                        'Delay'
 
673
                                        ,d2.etype, ':', d2.path,
 
674
                                        ' blocked by ',
 
675
                                        'Move:', d1.path, '->', d1.path2
 
676
                                )
 
677
 
461
678
                                return 'stack'
462
679
                        end
 
680
 
463
681
                        return nil
464
682
                end
465
683
 
466
 
                -- Move upon a single event
 
684
                -- a move upon a non-move event
467
685
                if d1.etype ~= 'Move' and d2.etype == 'Move' then
468
686
                        if d1.path == d2.path or d1.path == d2.path2 or
469
687
                           d1.path :byte(-1) == 47 and string.starts(d2.path,  d1.path) or
483
689
                           d2.path :byte(-1) == 47 and string.starts(d1.path,  d2.path) or
484
690
                           d2.path2:byte(-1) == 47 and string.starts(d1.path,  d2.path2)
485
691
                        then
486
 
                                log('Delay','Move:',d2.path,'->',d2.path2,' splits on ',d1.etype,':',d1.path)
 
692
                                log(
 
693
                                        'Delay',
 
694
                                        'Move:', d2.path, '->', d2.path2,
 
695
                                        ' splits on ',
 
696
                                        d1.etype, ':', d1.path
 
697
                                )
487
698
                                return 'split'
488
699
                        end
 
700
 
489
701
                        return nil
490
702
                end
491
703
 
492
 
                -- Move upon move
 
704
                --
 
705
                -- a move event upon a move event
 
706
                --
493
707
                if d1.etype == 'Move' and d2.etype == 'Move' then
494
708
                        -- TODO combine moves,
495
709
 
504
718
                           d2.path2:byte(-1) == 47 and string.starts(d1.path,  d2.path2) or
505
719
                           d2.path2:byte(-1) == 47 and string.starts(d1.path2, d2.path2)
506
720
                        then
507
 
                                log('Delay','Move:',d2.path,'->',d1.path2,
508
 
                                        ' splits on Move:',d1.path,'->',d1.path2)
 
721
                                log(
 
722
                                        'Delay',
 
723
                                        'Move:', d2.path, '->', d1.path2,
 
724
                                        ' splits on Move:',
 
725
                                        d1.path, '->', d1.path2
 
726
                                )
 
727
 
509
728
                                return 'split'
510
729
                        end
 
730
 
511
731
                        return nil
512
732
                end
513
733
 
514
 
                error('reached impossible state')
 
734
                error( 'reached impossible state' )
515
735
        end
516
736
 
517
 
        -- public interface
518
 
        return {combine = combine}
519
 
end)()
520
 
 
521
 
-----
 
737
 
 
738
        --
 
739
        -- Public interface
 
740
        --
 
741
        return { combine = combine }
 
742
 
 
743
end )( )
 
744
 
 
745
--
522
746
-- Creates inlets for syncs: the user interface for events.
523
747
--
524
 
local InletFactory = (function()
525
 
        -----
526
 
        -- table to receive the delay of an event
 
748
local InletFactory = ( function( )
 
749
 
 
750
        --
 
751
        -- Table to receive the delay of an event
527
752
        -- or the delay list of an event list.
528
753
        --
529
754
        -- Keys are events and values are delays.
530
 
        local e2d = {}
 
755
        --
 
756
        local e2d = { }
531
757
 
532
 
        -----
533
 
        -- table to ensure the uniqueness of every event
 
758
        --
 
759
        -- Table to ensure the uniqueness of every event
534
760
        -- related to a delay.
535
761
        --
536
762
        -- Keys are delay and values are events.
537
 
        local e2d2 = {}
 
763
        --
 
764
        local e2d2 = { }
538
765
 
539
 
        -----
540
 
        -- allows the garbage collector to remove not refrenced
 
766
        --
 
767
        -- Allows the garbage collector to remove not refrenced
541
768
        -- events.
542
 
        setmetatable(e2d,  { __mode = 'k' })
543
 
        setmetatable(e2d2, { __mode = 'v' })
 
769
        --
 
770
        setmetatable( e2d,  { __mode = 'k' } )
 
771
        setmetatable( e2d2, { __mode = 'v' } )
544
772
 
545
 
        -----
546
 
        -- removes the trailing slash from a path
547
 
        local function cutSlash(path)
 
773
        --
 
774
        -- Removes the trailing slash from a path.
 
775
        --
 
776
        local function cutSlash( path )
548
777
                if string.byte(path, -1) == 47 then
549
778
                        return string.sub(path, 1, -2)
550
779
                else
552
781
                end
553
782
        end
554
783
 
555
 
        local function getPath(event)
 
784
        --
 
785
        -- Gets the path of an event.
 
786
        --
 
787
        local function getPath( event )
556
788
                if event.move ~= 'To' then
557
 
                        return e2d[event].path
 
789
                        return e2d[ event ].path
558
790
                else
559
 
                        return e2d[event].path2
 
791
                        return e2d[ event ].path2
560
792
                end
561
793
        end
562
794
 
563
 
        -----
 
795
        --
564
796
        -- Interface for user scripts to get event fields.
565
797
        --
566
798
        local eventFields = {
567
 
                -----
 
799
 
 
800
                --
568
801
                -- Returns a copy of the configuration as called by sync.
569
802
                -- But including all inherited data and default values.
570
803
                --
571
804
                -- TODO give user a readonly version.
572
805
                --
573
 
                config = function(event)
574
 
                        return e2d[event].sync.config
 
806
                config = function( event )
 
807
                        return e2d[ event ].sync.config
575
808
                end,
576
809
 
577
810
                -----
578
811
                -- Returns the inlet belonging to an event.
579
812
                --
580
 
                inlet = function(event)
581
 
                        return e2d[event].sync.inlet
 
813
                inlet = function( event )
 
814
                        return e2d[ event ].sync.inlet
582
815
                end,
583
816
 
584
 
                -----
 
817
                --
585
818
                -- Returns the type of the event.
 
819
                --
586
820
                -- Can be: 'Attrib', 'Create', 'Delete', 'Modify' or 'Move',
587
821
                --
588
 
                etype = function(event)
589
 
                        return e2d[event].etype
 
822
                etype = function( event )
 
823
                        return e2d[ event ].etype
590
824
                end,
591
825
 
592
 
                -----
593
 
                -- Tells this isn't a list.
594
 
                --
595
 
                isList = function()
 
826
                --
 
827
                -- Events are not lists.
 
828
                --
 
829
                isList = function( )
596
830
                        return false
597
831
                end,
598
832
 
599
 
                -----
600
 
                -- Return the status of the event.
 
833
                --
 
834
                -- Returns the status of the event.
 
835
                --
601
836
                -- Can be:
602
837
                --    'wait', 'active', 'block'.
603
838
                --
604
 
                status = function(event)
605
 
                        return e2d[event].status
606
 
                end,
607
 
 
608
 
                -----
609
 
                -- Returns true if event relates to a directory.
610
 
                --
611
 
                isdir = function(event)
612
 
                        return string.byte(getPath(event), -1) == 47
613
 
                end,
614
 
 
615
 
                -----
 
839
                status = function( event )
 
840
                        return e2d[ event ].status
 
841
                end,
 
842
 
 
843
                --
 
844
                -- Returns true if event relates to a directory
 
845
                --
 
846
                isdir = function( event )
 
847
                        return string.byte( getPath( event ), -1 ) == 47
 
848
                end,
 
849
 
 
850
                --
616
851
                -- Returns the name of the file/dir.
 
852
                --
617
853
                -- Includes a trailing slash for dirs.
618
854
                --
619
 
                name = function(event)
620
 
                        return string.match(getPath(event), '[^/]+/?$')
621
 
                end,
622
 
 
623
 
                -----
624
 
                -- Returns the name of the file/dir.
625
 
                -- Excludes a trailing slash for dirs.
626
 
                --
627
 
                basename = function(event)
628
 
                        return string.match(getPath(event), '([^/]+)/?$')
629
 
                end,
630
 
 
631
 
                -----
 
855
                name = function( event )
 
856
                        return string.match( getPath( event ), '[^/]+/?$' )
 
857
                end,
 
858
 
 
859
                --
 
860
                -- Returns the name of the file/dir
 
861
                -- excluding a trailing slash for dirs.
 
862
                --
 
863
                basename = function( event )
 
864
                        return string.match( getPath( event ), '([^/]+)/?$')
 
865
                end,
 
866
 
 
867
                ---
632
868
                -- Returns the file/dir relative to watch root
633
 
                -- Includes a trailing slash for dirs.
 
869
                -- including a trailing slash for dirs.
634
870
                --
635
 
                path = function(event)
636
 
                        return getPath(event)
 
871
                path = function( event )
 
872
                        return getPath( event )
637
873
                end,
638
874
 
639
 
                -----
 
875
                --
640
876
                -- Returns the directory of the file/dir relative to watch root
641
877
                -- Always includes a trailing slash.
642
878
                --
643
 
                pathdir = function(event)
644
 
                        return string.match(getPath(event), '^(.*/)[^/]+/?') or ''
 
879
                pathdir = function( event )
 
880
                        return string.match( getPath( event ), '^(.*/)[^/]+/?' ) or ''
645
881
                end,
646
882
 
647
 
                -----
 
883
                --
648
884
                -- Returns the file/dir relativ to watch root
649
 
                -- Excludes a trailing slash for dirs.
 
885
                -- excluding a trailing slash for dirs.
650
886
                --
651
 
                pathname = function(event)
652
 
                        return cutSlash(getPath(event))
 
887
                pathname = function( event )
 
888
                        return cutSlash( getPath( event ) )
653
889
                end,
654
890
 
655
 
                ------
 
891
                ---
656
892
                -- Returns the absolute path of the watch root.
657
 
                -- All symlinks will have been resolved.
658
 
                --
659
 
                source = function(event)
660
 
                        return e2d[event].sync.source
661
 
                end,
662
 
 
663
 
                ------
664
 
                -- Returns the absolute path of the file/dir.
665
 
                -- Includes a trailing slash for dirs.
666
 
                --
667
 
                sourcePath = function(event)
668
 
                        return e2d[event].sync.source .. getPath(event)
669
 
                end,
670
 
 
671
 
                ------
672
 
                -- Returns the absolute dir of the file/dir.
673
 
                -- Includes a trailing slash.
674
 
                --
675
 
                sourcePathdir = function(event)
 
893
                -- All symlinks are resolved.
 
894
                --
 
895
                source = function( event )
 
896
                        return e2d[ event ].sync.source
 
897
                end,
 
898
 
 
899
                --
 
900
                -- Returns the absolute path of the file/dir
 
901
                -- including a trailing slash for dirs.
 
902
                --
 
903
                sourcePath = function( event )
 
904
                        return e2d[ event ].sync.source .. getPath( event )
 
905
                end,
 
906
 
 
907
                --
 
908
                -- Returns the absolute dir of the file/dir
 
909
                -- including a trailing slash.
 
910
                --
 
911
                sourcePathdir = function( event )
676
912
                        return e2d[event].sync.source ..
677
 
                                (string.match(getPath(event), '^(.*/)[^/]+/?') or '')
678
 
                end,
679
 
 
680
 
                ------
681
 
                -- Returns the absolute path of the file/dir.
682
 
                -- Excludes a trailing slash for dirs.
683
 
                --
684
 
                sourcePathname = function(event)
685
 
                        return e2d[event].sync.source .. cutSlash(getPath(event))
686
 
                end,
687
 
 
688
 
                ------
689
 
                -- Returns the target.
690
 
                -- Just for user comfort
691
 
                --
692
 
                -- (except here, the lsyncd.runner does not care event about the
693
 
                -- existance of 'target', this is up to the scripts.)
694
 
                --
695
 
                target = function(event)
696
 
                        return e2d[event].sync.config.target
697
 
                end,
698
 
 
699
 
                ------
700
 
                -- Returns the relative dir/file appended to the target.
701
 
                -- Includes a trailing slash for dirs.
702
 
                --
703
 
                targetPath = function(event)
704
 
                        return e2d[event].sync.config.target .. getPath(event)
705
 
                end,
706
 
 
707
 
                ------
708
 
                -- Returns the dir of the dir/file appended to the target.
709
 
                -- Includes a trailing slash.
710
 
                --
711
 
                targetPathdir = function(event)
712
 
                        return e2d[event].sync.config.target ..
713
 
                                (string.match(getPath(event), '^(.*/)[^/]+/?') or '')
714
 
                end,
715
 
 
716
 
                ------
717
 
                -- Returns the relative dir/file appended to the target.
718
 
                -- Excludes a trailing slash for dirs.
719
 
                --
720
 
                targetPathname = function(event)
721
 
                        return e2d[event].sync.config.target ..
722
 
                                cutSlash(getPath(event))
 
913
                                ( string.match( getPath( event ), '^(.*/)[^/]+/?' ) or '' )
 
914
                end,
 
915
 
 
916
                ------
 
917
                -- Returns the absolute path of the file/dir
 
918
                -- excluding a trailing slash for dirs.
 
919
                --
 
920
                sourcePathname = function( event )
 
921
                        return e2d[ event ].sync.source .. cutSlash( getPath( event ) )
 
922
                end,
 
923
 
 
924
                --
 
925
                -- Returns the configured target
 
926
                --
 
927
                target = function( event )
 
928
                        return e2d[ event ].sync.config.target
 
929
                end,
 
930
 
 
931
                --
 
932
                -- Returns the relative dir/file appended to the target
 
933
                -- including a trailing slash for dirs.
 
934
                --
 
935
                targetPath = function( event )
 
936
                        return e2d[ event ].sync.config.target .. getPath( event )
 
937
                end,
 
938
 
 
939
                --
 
940
                -- Returns the dir of the dir/file appended to the target
 
941
                -- including a trailing slash.
 
942
                --
 
943
                targetPathdir = function( event )
 
944
                        return e2d[ event ].sync.config.target ..
 
945
                                ( string.match( getPath( event ), '^(.*/)[^/]+/?' ) or '' )
 
946
                end,
 
947
 
 
948
                --
 
949
                -- Returns the relative dir/file appended to the target
 
950
                -- excluding a trailing slash for dirs.
 
951
                --
 
952
                targetPathname = function( event )
 
953
                        return e2d[ event ].sync.config.target ..
 
954
                                cutSlash( getPath( event ) )
723
955
                end,
724
956
        }
725
957
 
726
 
        -----
 
958
        --
727
959
        -- Retrievs event fields for the user script.
728
960
        --
729
961
        local eventMeta = {
730
 
                __index = function(event, field)
731
 
                        local f = eventFields[field]
 
962
 
 
963
                __index = function( event, field )
 
964
                        local f = eventFields[ field ]
732
965
                        if not f then
733
966
                                if field == 'move' then
734
967
                                        -- possibly undefined
735
968
                                        return nil
736
969
                                end
737
 
                                error('event does not have field "'..field..'"', 2)
 
970
                                error( 'event does not have field "'..field..'"', 2 )
738
971
                        end
739
 
                        return f(event)
 
972
                        return f( event )
740
973
                end
 
974
 
741
975
        }
742
976
 
743
 
        -----
744
 
        -- Interface for user scripts to get event fields.
 
977
        --
 
978
        -- Interface for user scripts to get list fields.
745
979
        --
746
980
        local eventListFuncs = {
747
 
                -----
 
981
 
 
982
                --
748
983
                -- Returns a list of paths of all events in list.
749
984
                --
750
985
                -- @param elist -- handle returned by getevents()
751
986
                -- @param mutator -- if not nil called with (etype, path, path2)
752
987
                --                   returns one or two strings to add.
753
988
                --
754
 
                getPaths = function(elist, mutator)
 
989
                getPaths = function( elist, mutator )
 
990
 
755
991
                        local dlist = e2d[elist]
 
992
 
756
993
                        if not dlist then
757
 
                                error('cannot find delay list from event list.')
 
994
                                error( 'cannot find delay list from event list.' )
758
995
                        end
759
 
                        local result = {}
 
996
 
 
997
                        local result  = { }
760
998
                        local resultn = 1
761
 
                        for k, d in ipairs(dlist) do
 
999
 
 
1000
                        for k, d in ipairs( dlist ) do
 
1001
 
762
1002
                                local s1, s2
 
1003
 
763
1004
                                if mutator then
764
 
                                        s1, s2 = mutator(d.etype, d.path, d.path2)
 
1005
                                        s1, s2 = mutator( d.etype, d.path, d.path2 )
765
1006
                                else
766
1007
                                        s1, s2 = d.path, d.path2
767
1008
                                end
768
 
                                result[resultn] = s1
 
1009
 
 
1010
                                result[ resultn ] = s1
769
1011
                                resultn = resultn + 1
 
1012
 
770
1013
                                if s2 then
771
 
                                        result[resultn] = s2
 
1014
                                        result[ resultn ] = s2
772
1015
                                        resultn = resultn + 1
773
1016
                                end
774
1017
                        end
 
1018
 
775
1019
                        return result
 
1020
 
776
1021
                end
777
1022
        }
778
1023
 
779
 
        -----
780
 
        -- Retrievs event list fields for the user script.
 
1024
        --
 
1025
        -- Retrievs event list fields for the user script
781
1026
        --
782
1027
        local eventListMeta = {
783
 
                __index = function(elist, func)
784
 
                        if func == 'isList' then return true end
785
 
 
786
 
                        if func == 'config' then return e2d[elist].sync.config end
787
 
 
788
 
                        local f = eventListFuncs[func]
 
1028
 
 
1029
                __index = function( elist, func )
 
1030
 
 
1031
                        if func == 'isList' then
 
1032
                                return true
 
1033
                        end
 
1034
 
 
1035
                        if func == 'config' then
 
1036
                                return e2d[ elist ].sync.config
 
1037
                        end
 
1038
 
 
1039
                        local f = eventListFuncs[ func ]
 
1040
 
789
1041
                        if not f then
790
 
                                error('event list does not have function "'..func..'"', 2)
791
 
                        end
792
 
 
793
 
                        return function(...)
794
 
                                return f(elist, ...)
795
 
                        end
 
1042
                                error(
 
1043
                                        'event list does not have function "' .. func .. '"',
 
1044
                                        2
 
1045
                                )
 
1046
                        end
 
1047
 
 
1048
                        return function( ... )
 
1049
                                return f( elist, ... )
 
1050
                        end
 
1051
 
796
1052
                end
 
1053
 
797
1054
        }
798
1055
 
799
 
        -----
800
 
        -- table of all inlets with their syncs
801
 
        --
802
 
        local inlets = {}
803
 
 
804
 
        -----
805
 
        -- allows the garbage collector to remove entries.
806
 
        -- TODO check memory use
807
 
        setmetatable(inlets, { __mode = 'v' })
808
 
 
809
 
        -----
 
1056
        --
 
1057
        -- Table of all inlets with their syncs.
 
1058
        --
 
1059
        local inlets = { }
 
1060
 
 
1061
        --
 
1062
        -- Allows the garbage collector to remove entries.
 
1063
        --
 
1064
        setmetatable( inlets, { __mode = 'v' } )
 
1065
 
 
1066
        --
810
1067
        -- Encapsulates a delay into an event for the user script.
811
1068
        --
812
 
        local function d2e(delay)
 
1069
        local function d2e( delay )
 
1070
 
813
1071
                -- already created?
814
1072
                local eu = e2d2[delay]
815
1073
 
816
1074
                if delay.etype ~= 'Move' then
817
 
                        if eu then return eu end
818
 
 
819
 
                        local event = {}
820
 
                        setmetatable(event, eventMeta)
821
 
                        e2d[event]  = delay
822
 
                        e2d2[delay] = event
 
1075
 
 
1076
                        if eu then
 
1077
                                return eu
 
1078
                        end
 
1079
 
 
1080
                        local event = { }
 
1081
                        setmetatable( event, eventMeta )
 
1082
                        e2d[ event ]  = delay
 
1083
                        e2d2[ delay ] = event
 
1084
 
823
1085
                        return event
 
1086
 
824
1087
                else
825
1088
                        -- moves have 2 events - origin and destination
826
 
                        if eu then return eu[1], eu[2] end
 
1089
                        if eu then
 
1090
                                return eu[1], eu[2]
 
1091
                        end
827
1092
 
828
1093
                        local event  = { move = 'Fr' }
829
1094
                        local event2 = { move = 'To' }
830
 
                        setmetatable(event, eventMeta)
831
 
                        setmetatable(event2, eventMeta)
832
 
                        e2d[event]  = delay
833
 
                        e2d[event2] = delay
834
 
                        e2d2[delay] = { event, event2 }
 
1095
 
 
1096
                        setmetatable( event,  eventMeta )
 
1097
                        setmetatable( event2, eventMeta )
 
1098
 
 
1099
                        e2d[ event ]  = delay
 
1100
                        e2d[ event2 ] = delay
 
1101
 
 
1102
                        e2d2[ delay ] = { event, event2 }
 
1103
 
835
1104
                        -- move events have a field 'move'
836
1105
                        return event, event2
 
1106
 
837
1107
                end
838
1108
        end
839
1109
 
840
 
        -----
 
1110
        --
841
1111
        -- Encapsulates a delay list into an event list for the user script.
842
1112
        --
843
 
        local function dl2el(dlist)
844
 
                local eu = e2d2[dlist]
845
 
                if eu then return eu end
846
 
 
847
 
                local elist = {}
848
 
                setmetatable(elist, eventListMeta)
849
 
                e2d[elist] = dlist
850
 
                e2d2[dlist] = elist
 
1113
        local function dl2el( dlist )
 
1114
 
 
1115
                local eu = e2d2[ dlist ]
 
1116
 
 
1117
                if eu then
 
1118
                        return eu
 
1119
                end
 
1120
 
 
1121
                local elist = { }
 
1122
 
 
1123
                setmetatable( elist, eventListMeta )
 
1124
 
 
1125
                e2d [ elist ] = dlist
 
1126
                e2d2[ dlist ] = elist
 
1127
 
851
1128
                return elist
 
1129
 
852
1130
        end
853
1131
 
854
 
        -----
 
1132
        --
855
1133
        -- The functions the inlet provides.
856
1134
        --
857
1135
        local inletFuncs = {
858
 
                -----
859
 
                -- adds an exclude.
860
 
                --
861
 
                addExclude = function(sync, pattern)
862
 
                        sync:addExclude(pattern)
863
 
                end,
864
 
 
865
 
                -----
866
 
                -- removes an exclude.
867
 
                --
868
 
                rmExclude = function(sync, pattern)
869
 
                        sync:rmExclude(pattern)
870
 
                end,
871
 
 
872
 
                -----
873
 
                -- gets the list of excludes in their original rsynlike patterns form.
874
 
                --
875
 
                getExcludes = function(sync)
 
1136
 
 
1137
                --
 
1138
                -- Adds an exclude.
 
1139
                --
 
1140
                addExclude = function( sync, pattern )
 
1141
                        sync:addExclude( pattern )
 
1142
                end,
 
1143
 
 
1144
                --
 
1145
                -- Removes an exclude.
 
1146
                --
 
1147
                rmExclude = function( sync, pattern )
 
1148
                        sync:rmExclude( pattern )
 
1149
                end,
 
1150
 
 
1151
                --
 
1152
                -- Gets the list of excludes in their original rsynlike patterns form.
 
1153
                --
 
1154
                getExcludes = function( sync )
 
1155
 
876
1156
                        -- creates a copy
877
 
                        local e = {}
 
1157
                        local e = { }
878
1158
                        local en = 1;
879
 
                        for k, _ in pairs(sync.excludes.list) do
880
 
                                e[en] = k;
 
1159
 
 
1160
                        for k, _ in pairs( sync.excludes.list ) do
 
1161
                                e[ en ] = k;
881
1162
                                en = en + 1;
882
1163
                        end
 
1164
 
883
1165
                        return e;
884
1166
                end,
885
1167
 
886
 
                -----
 
1168
                --
887
1169
                -- Creates a blanketEvent that blocks everything
888
1170
                -- and is blocked by everything.
889
1171
                --
890
 
                createBlanketEvent = function(sync)
891
 
                        return d2e(sync:addBlanketDelay())
 
1172
                createBlanketEvent = function( sync )
 
1173
                        return d2e( sync:addBlanketDelay( ) )
892
1174
                end,
893
1175
 
894
 
                -----
 
1176
                --
895
1177
                -- Discards a waiting event.
896
1178
                --
897
 
                discardEvent = function(sync, event)
898
 
                        local delay = e2d[event]
 
1179
                discardEvent = function( sync, event )
 
1180
                        local delay = e2d[ event ]
899
1181
                        if delay.status ~= 'wait' then
900
 
                                log('Error',
 
1182
                                log(
 
1183
                                        'Error',
901
1184
                                        'Ignored cancel of a non-waiting event of type ',
902
 
                                        event.etype)
 
1185
                                        event.etype
 
1186
                                )
903
1187
                                return
904
1188
                        end
905
 
                        sync:removeDelay(delay)
 
1189
                        sync:removeDelay( delay )
906
1190
                end,
907
1191
 
908
 
                -----
 
1192
                --
909
1193
                -- Gets the next not blocked event from queue.
910
1194
                --
911
 
                getEvent = function(sync)
912
 
                        return d2e(sync:getNextDelay(now()))
 
1195
                getEvent = function( sync )
 
1196
                        return d2e( sync:getNextDelay( now( ) ) )
913
1197
                end,
914
1198
 
915
 
                -----
 
1199
                --
916
1200
                -- Gets all events that are not blocked by active events.
917
1201
                --
918
1202
                -- @param if not nil a function to test each delay
919
1203
                --
920
 
                getEvents = function(sync, test)
921
 
                        local dlist = sync:getDelays(test)
922
 
                        return dl2el(dlist)
 
1204
                getEvents = function( sync, test )
 
1205
                        local dlist = sync:getDelays( test )
 
1206
                        return dl2el( dlist )
923
1207
                end,
924
1208
 
925
 
                -----
 
1209
                --
926
1210
                -- Returns the configuration table specified by sync{}
927
1211
                --
928
 
                getConfig = function(sync)
 
1212
                getConfig = function( sync )
929
1213
                        -- TODO gives a readonly handler only.
930
1214
                        return sync.config
931
1215
                end,
932
1216
        }
933
1217
 
934
 
        -----
 
1218
        --
935
1219
        -- Forwards access to inlet functions.
936
1220
        --
937
1221
        local inletMeta = {
938
 
                __index = function(inlet, func)
939
 
                        local f = inletFuncs[func]
940
 
                        if not f then error('inlet does not have function "'..func..'"', 2) end
941
 
                        return function(...) return f(inlets[inlet], ...) end
 
1222
                __index = function( inlet, func )
 
1223
                        local f = inletFuncs[ func ]
 
1224
                        if not f then
 
1225
                                error(
 
1226
                                        'inlet does not have function "'..func..'"',
 
1227
                                        2
 
1228
                                )
 
1229
                        end
 
1230
 
 
1231
                        return function( ... )
 
1232
                                return f( inlets[ inlet ], ... )
 
1233
                        end
942
1234
                end,
943
1235
        }
944
1236
 
945
 
        -----
946
 
        -- Creates a new inlet for Sync
947
 
        local function newInlet(sync)
948
 
                -- lua runner controlled variables
949
 
                local inlet = {}
 
1237
        --
 
1238
        -- Creates a new inlet for Sync.
 
1239
        --
 
1240
        local function newInlet( sync )
 
1241
 
 
1242
                -- Lsyncd runner controlled variables
 
1243
                local inlet = { }
950
1244
 
951
1245
                -- sets use access methods
952
 
                setmetatable(inlet, inletMeta)
953
 
                inlets[inlet] = sync
 
1246
                setmetatable( inlet, inletMeta )
 
1247
                inlets[ inlet ] = sync
954
1248
                return inlet
955
1249
        end
956
1250
 
957
 
        -----
 
1251
        --
958
1252
        -- Returns the delay from a event.
959
1253
        --
960
 
        local function getDelayOrList(event)
961
 
                return e2d[event]
 
1254
        local function getDelayOrList( event )
 
1255
                return e2d[ event ]
962
1256
        end
963
1257
 
964
 
        -----
 
1258
        --
965
1259
        -- Returns the sync from an event or list
966
1260
        --
967
 
        local function getSync(event)
968
 
                return e2d[event].sync
 
1261
        local function getSync( event )
 
1262
                return e2d[ event ].sync
969
1263
        end
970
1264
 
971
 
        -----
972
 
        -- public interface.
973
 
        -- this one is split, one for user one for runner.
 
1265
        --
 
1266
        -- Public interface.
 
1267
        --
974
1268
        return {
975
1269
                getDelayOrList = getDelayOrList,
976
1270
                d2e            = d2e,
978
1272
                getSync        = getSync,
979
1273
                newInlet       = newInlet,
980
1274
        }
981
 
end)()
982
 
 
983
 
 
984
 
-----
 
1275
 
 
1276
end )( )
 
1277
 
 
1278
 
 
1279
--
985
1280
-- A set of exclude patterns
986
1281
--
987
 
local Excludes = (function()
 
1282
local Excludes = ( function( )
988
1283
 
989
 
        -----
 
1284
        --
990
1285
        -- Turns a rsync like file pattern to a lua pattern.
 
1286
        -- ( at best it can )
991
1287
        --
992
 
        local function toLuaPattern(p)
 
1288
        local function toLuaPattern( p )
993
1289
                local o = p
994
 
                p = string.gsub(p, '%%', '%%%%')
995
 
                p = string.gsub(p, '%^', '%%^')
996
 
                p = string.gsub(p, '%$', '%%$')
997
 
                p = string.gsub(p, '%(', '%%(')
998
 
                p = string.gsub(p, '%)', '%%)')
999
 
                p = string.gsub(p, '%.', '%%.')
1000
 
                p = string.gsub(p, '%[', '%%[')
1001
 
                p = string.gsub(p, '%]', '%%]')
1002
 
                p = string.gsub(p, '%+', '%%+')
1003
 
                p = string.gsub(p, '%-', '%%-')
1004
 
                p = string.gsub(p, '%?', '[^/]')
1005
 
                p = string.gsub(p, '%*', '[^/]*')
 
1290
                p = string.gsub( p, '%%', '%%%%'  )
 
1291
                p = string.gsub( p, '%^', '%%^'   )
 
1292
                p = string.gsub( p, '%$', '%%$'   )
 
1293
                p = string.gsub( p, '%(', '%%('   )
 
1294
                p = string.gsub( p, '%)', '%%)'   )
 
1295
                p = string.gsub( p, '%.', '%%.'   )
 
1296
                p = string.gsub( p, '%[', '%%['   )
 
1297
                p = string.gsub( p, '%]', '%%]'   )
 
1298
                p = string.gsub( p, '%+', '%%+'   )
 
1299
                p = string.gsub( p, '%-', '%%-'   )
 
1300
                p = string.gsub( p, '%?', '[^/]'  )
 
1301
                p = string.gsub( p, '%*', '[^/]*' )
1006
1302
                -- this was a ** before
1007
 
                p = string.gsub(p, '%[%^/%]%*%[%^/%]%*', '.*')
1008
 
                p = string.gsub(p, '^/', '^/')
1009
 
                if p:sub(1,2) ~= '^/' then -- does not begin with '^/'
1010
 
                        -- all matches should begin with '/'.
1011
 
                        p = '/'..p;
 
1303
                p = string.gsub( p, '%[%^/%]%*%[%^/%]%*', '.*' )
 
1304
                p = string.gsub( p, '^/', '^/'    )
 
1305
 
 
1306
                if p:sub( 1, 2 ) ~= '^/' then
 
1307
                        -- if does not begin with '^/'
 
1308
                        -- then all matches should begin with '/'.
 
1309
                        p = '/' .. p;
1012
1310
                end
1013
 
                log('Exclude', 'toLuaPattern "',o,'" = "',p,'"')
 
1311
 
 
1312
                log(
 
1313
                        'Exclude',
 
1314
                        'toLuaPattern "',
 
1315
                        o, '" = "', p, '"'
 
1316
                )
 
1317
 
1014
1318
                return p
1015
1319
        end
1016
1320
 
1017
 
        -----
1018
 
        -- Adds a pattern to exclude.
1019
 
        --
1020
 
        local function add(self, pattern)
1021
 
                if self.list[pattern] then
 
1321
        --
 
1322
        -- Adds a pattern to exclude
 
1323
        --
 
1324
        local function add( self, pattern )
 
1325
 
 
1326
                if self.list[ pattern ] then
1022
1327
                        -- already in the list
1023
1328
                        return
1024
1329
                end
1025
 
                local lp = toLuaPattern(pattern)
1026
 
                self.list[pattern] = lp
 
1330
 
 
1331
                local lp = toLuaPattern( pattern )
 
1332
                self.list[ pattern ] = lp
 
1333
 
1027
1334
        end
1028
1335
 
1029
 
        -----
 
1336
        --
1030
1337
        -- Removes a pattern to exclude.
1031
1338
        --
1032
 
        local function remove(self, pattern)
1033
 
                if not self.list[pattern] then
1034
 
                        -- already in the list
1035
 
                        log('Normal', 'Removing not excluded exclude "'..pattern..'"')
 
1339
        local function remove( self, pattern )
 
1340
 
 
1341
                if not self.list[ pattern ] then
 
1342
                        -- already in the list?
 
1343
 
 
1344
                        log(
 
1345
                                'Normal',
 
1346
                                'Removing not excluded exclude "' .. pattern .. '"'
 
1347
                        )
 
1348
 
1036
1349
                        return
1037
1350
                end
 
1351
 
1038
1352
                self.list[pattern] = nil
 
1353
 
1039
1354
        end
1040
1355
 
1041
1356
 
1048
1363
                end
1049
1364
        end
1050
1365
 
1051
 
        -----
1052
 
        -- loads excludes from a file
1053
 
        --
1054
 
        local function loadFile(self, file)
1055
 
                f, err = io.open(file)
 
1366
        --
 
1367
        -- Loads the excludes from a file
 
1368
        --
 
1369
        local function loadFile( self, file )
 
1370
 
 
1371
                f, err = io.open( file )
 
1372
 
1056
1373
                if not f then
1057
 
                        log('Error', 'Cannot open exclude file "',file,'": ', err)
1058
 
                        terminate(-1) -- ERRNO
 
1374
                        log(
 
1375
                                'Error',
 
1376
                                'Cannot open exclude file "', file,'": ',
 
1377
                                err
 
1378
                        )
 
1379
 
 
1380
                        terminate( -1 )
1059
1381
                end
 
1382
 
1060
1383
            for line in f:lines() do
 
1384
 
1061
1385
                        -- lsyncd 2.0 does not support includes
 
1386
 
1062
1387
                        if not string.match(line, '%s*+') then
1063
 
                                local p = string.match(line, '%s*-?%s*(.*)')
1064
 
                                if p then add(self, p) end
 
1388
                                local p = string.match(
 
1389
                                        line, '%s*-?%s*(.*)'
 
1390
                                )
 
1391
                                if p then
 
1392
                                        add(self, p)
 
1393
                                end
1065
1394
                        end
1066
1395
                end
1067
 
                f:close()
 
1396
 
 
1397
                f:close( )
1068
1398
        end
1069
1399
 
1070
 
        -----
 
1400
        --
1071
1401
        -- Tests if 'path' is excluded.
1072
1402
        --
1073
 
        local function test(self, path)
1074
 
                for _, p in pairs(self.list) do
1075
 
                        if p:byte(-1) == 36 then
 
1403
        local function test( self, path )
 
1404
 
 
1405
                for _, p in pairs( self.list ) do
 
1406
 
 
1407
                        if p:byte( -1 ) == 36 then
1076
1408
                                -- ends with $
1077
 
                                if path:match(p) then return true end
 
1409
 
 
1410
                                if path:match( p ) then
 
1411
                                        return true
 
1412
                                end
 
1413
 
1078
1414
                        else
 
1415
 
1079
1416
                                -- ends either end with / or $
1080
 
                                if path:match(p..'/') or path:match(p..'$') then return true end
 
1417
                                if path:match(p .. '/') or path:match(p .. '$') then
 
1418
                                        return true
 
1419
                                end
 
1420
 
1081
1421
                        end
1082
1422
                end
 
1423
 
1083
1424
                return false
 
1425
 
1084
1426
        end
1085
1427
 
1086
 
        -----
 
1428
 
 
1429
        --
1087
1430
        -- Cretes a new exclude set
1088
1431
        --
1089
 
        local function new()
 
1432
        local function new( )
 
1433
 
1090
1434
                return {
1091
 
                        list = {},
 
1435
                        list = { },
1092
1436
 
1093
1437
                        -- functions
1094
1438
                        add      = add,
1097
1441
                        remove   = remove,
1098
1442
                        test     = test,
1099
1443
                }
 
1444
 
1100
1445
        end
1101
1446
 
1102
 
        -----
 
1447
        --
1103
1448
        -- Public interface
 
1449
        --
1104
1450
        return { new = new }
1105
 
end)()
1106
 
 
1107
 
-----
 
1451
 
 
1452
end )( )
 
1453
 
 
1454
--
1108
1455
-- Holds information about one observed directory inclusively subdirs.
1109
1456
--
1110
 
local Sync = (function()
1111
 
        -----
 
1457
local Sync = ( function( )
 
1458
 
 
1459
        --
1112
1460
        -- Syncs that have no name specified by the user script
1113
1461
        -- get an incremental default name 'Sync[X]'
1114
1462
        --
1115
1463
        local nextDefaultName = 1
1116
1464
 
1117
 
        -----
 
1465
        --
1118
1466
        -- Adds an exclude.
1119
1467
        --
1120
 
        local function addExclude(self, pattern)
1121
 
                return self.excludes:add(pattern)
 
1468
        local function addExclude( self, pattern )
 
1469
 
 
1470
                return self.excludes:add( pattern )
 
1471
 
1122
1472
        end
1123
1473
 
1124
 
        -----
 
1474
        --
1125
1475
        -- Removes an exclude.
1126
1476
        --
1127
 
        local function rmExclude(self, pattern)
1128
 
                return self.excludes:remove(pattern)
 
1477
        local function rmExclude( self, pattern )
 
1478
 
 
1479
                return self.excludes:remove( pattern )
 
1480
 
1129
1481
        end
1130
1482
 
1131
 
        -----
 
1483
        --
1132
1484
        -- Removes a delay.
1133
1485
        --
1134
 
        local function removeDelay(self, delay)
1135
 
                if self.delays[delay.dpos] ~= delay then
1136
 
                        error('Queue is broken, delay not a dpos')
 
1486
        local function removeDelay( self, delay )
 
1487
                if self.delays[ delay.dpos ] ~= delay then
 
1488
                        error( 'Queue is broken, delay not a dpos' )
1137
1489
                end
1138
 
                Queue.remove(self.delays, delay.dpos)
 
1490
 
 
1491
                Queue.remove( self.delays, delay.dpos )
1139
1492
 
1140
1493
                -- free all delays blocked by this one.
1141
1494
                if delay.blocks then
1142
 
                        for i, vd in pairs(delay.blocks) do
 
1495
                        for i, vd in pairs( delay.blocks ) do
1143
1496
                                vd.status = 'wait'
1144
1497
                        end
1145
1498
                end
1146
1499
        end
1147
1500
 
1148
 
        -----
 
1501
        --
1149
1502
        -- Returns true if this Sync concerns about 'path'
1150
1503
        --
1151
 
        local function concerns(self, path)
 
1504
        local function concerns( self, path )
 
1505
 
1152
1506
                -- not concerned if watch rootdir doesnt match
1153
 
                if not path:starts(self.source) then
 
1507
                if not path:starts( self.source ) then
1154
1508
                        return false
1155
1509
                end
1156
1510
 
1157
1511
                -- a sub dir and not concerned about subdirs
1158
1512
                if self.config.subdirs == false and
1159
 
                        path:sub(#self.source, -1):match('[^/]+/?')
 
1513
                        path:sub( #self.source, -1 ):match( '[^/]+/?' )
1160
1514
                then
1161
1515
                        return false
1162
1516
                end
1163
1517
 
1164
1518
                -- concerned if not excluded
1165
 
                return not self.excludes:test(path:sub(#self.source))
 
1519
                return not self.excludes:test( path:sub( #self.source ) )
1166
1520
        end
1167
1521
 
1168
 
        -----
 
1522
        --
1169
1523
        -- Collects a child process
1170
1524
        --
1171
 
        local function collect(self, pid, exitcode)
1172
 
                local delay = self.processes[pid]
 
1525
        local function collect( self, pid, exitcode )
 
1526
 
 
1527
                local delay = self.processes[ pid ]
 
1528
 
1173
1529
                if not delay then
1174
1530
                        -- not a child of this sync.
1175
1531
                        return
1176
1532
                end
1177
1533
 
1178
1534
                if delay.status then
1179
 
                        log('Delay', 'collected an event')
 
1535
 
 
1536
                        log( 'Delay', 'collected an event' )
 
1537
 
1180
1538
                        if delay.status ~= 'active' then
1181
1539
                                error('collecting a non-active process')
1182
1540
                        end
 
1541
 
1183
1542
                        local rc = self.config.collect(
1184
 
                                InletFactory.d2e(delay),
1185
 
                                exitcode)
 
1543
                                InletFactory.d2e( delay ),
 
1544
                                exitcode
 
1545
                        )
 
1546
 
1186
1547
                        if rc == 'die' then
1187
 
                                log('Error', 'Critical exitcode.');
1188
 
                                terminate(-1) --ERRNO
 
1548
                                log( 'Error', 'Critical exitcode.' );
 
1549
                                terminate( -1 )
1189
1550
                        end
 
1551
 
1190
1552
                        if rc ~= 'again' then
1191
1553
                                -- if its active again the collecter restarted the event
1192
 
                                removeDelay(self, delay)
1193
 
                                log('Delay', 'Finish of ',delay.etype,' on ',self.source,delay.path,' = ',exitcode)
 
1554
                                removeDelay( self, delay )
 
1555
                                log(
 
1556
                                        'Delay',
 
1557
                                        'Finish of ',
 
1558
                                        delay.etype,
 
1559
                                        ' on ',
 
1560
                                        self.source,delay.path,
 
1561
                                        ' = ',
 
1562
                                        exitcode
 
1563
                                )
1194
1564
                        else
1195
1565
                                -- sets the delay on wait again
1196
1566
                                delay.status = 'wait'
 
1567
 
1197
1568
                                local alarm = self.config.delay
 
1569
 
1198
1570
                                -- delays at least 1 second
1199
1571
                                if alarm < 1 then
1200
1572
                                        alarm = 1
1201
1573
                                end
1202
 
                                delay.alarm = now() + alarm
 
1574
 
 
1575
                                delay.alarm = now( ) + alarm
1203
1576
                        end
1204
1577
                else
1205
 
                        log('Delay', 'collected a list')
 
1578
                        log(
 
1579
                                'Delay',
 
1580
                                'collected a list'
 
1581
                        )
 
1582
 
1206
1583
                        local rc = self.config.collect(
1207
 
                                InletFactory.dl2el(delay),
1208
 
                                exitcode)
 
1584
                                InletFactory.dl2el( delay ),
 
1585
                                exitcode
 
1586
                        )
 
1587
 
1209
1588
                        if rc == 'die' then
1210
 
                                log('Error', 'Critical exitcode.');
1211
 
                                terminate(-1) --ERRNO
 
1589
                                log( 'Error', 'Critical exitcode.' );
 
1590
                                terminate( -1 )
1212
1591
                        end
 
1592
 
1213
1593
                        if rc == 'again' then
 
1594
 
1214
1595
                                -- sets the delay on wait again
1215
1596
                                delay.status = 'wait'
1216
1597
                                local alarm = self.config.delay
1218
1599
                                if alarm < 1 then
1219
1600
                                        alarm = 1
1220
1601
                                end
 
1602
 
1221
1603
                                alarm = now() + alarm
1222
 
                                for _, d in ipairs(delay) do
 
1604
 
 
1605
                                for _, d in ipairs( delay ) do
1223
1606
                                        d.alarm = alarm
1224
1607
                                        d.status = 'wait'
1225
1608
                                end
1226
1609
                        end
1227
 
                        for _, d in ipairs(delay) do
 
1610
 
 
1611
                        for _, d in ipairs( delay ) do
1228
1612
                                if rc ~= 'again' then
1229
 
                                        removeDelay(self, d)
 
1613
                                        removeDelay( self, d )
1230
1614
                                else
1231
1615
                                        d.status = 'wait'
1232
1616
                                end
1233
1617
                        end
1234
 
                        log('Delay','Finished list = ',exitcode)
 
1618
 
 
1619
                        log( 'Delay','Finished list = ',exitcode )
1235
1620
                end
1236
 
                self.processes[pid] = nil
 
1621
 
 
1622
                self.processes[ pid ] = nil
1237
1623
        end
1238
1624
 
1239
 
        -----
 
1625
        --
1240
1626
        -- Stacks a newDelay on the oldDelay,
1241
1627
        -- the oldDelay blocks the new Delay.
1242
1628
        --
1243
1629
        -- A delay can block 'n' other delays,
1244
1630
        -- but is blocked at most by one, the latest delay.
1245
1631
        --
1246
 
        local function stack(oldDelay, newDelay)
 
1632
        local function stack( oldDelay, newDelay )
 
1633
 
1247
1634
                newDelay.status = 'block'
 
1635
 
1248
1636
                if not oldDelay.blocks then
1249
 
                        oldDelay.blocks = {}
 
1637
                        oldDelay.blocks = { }
1250
1638
                end
1251
 
                table.insert(oldDelay.blocks, newDelay)
 
1639
 
 
1640
                table.insert( oldDelay.blocks, newDelay )
1252
1641
        end
1253
1642
 
1254
 
        -----
 
1643
        --
1255
1644
        -- Puts an action on the delay stack.
1256
1645
        --
1257
 
        local function delay(self, etype, time, path, path2)
1258
 
                log('Function', 'delay(',self.config.name,', ',etype,', ',path,', ',path2,')')
 
1646
        local function delay( self, etype, time, path, path2 )
 
1647
 
 
1648
                log(
 
1649
                        'Function',
 
1650
                        'delay( ',
 
1651
                                self.config.name, ', ',
 
1652
                                etype, ', ',
 
1653
                                path, ', ',
 
1654
                                path2,
 
1655
                        ' )'
 
1656
                )
1259
1657
 
1260
1658
                -- TODO
1261
 
                local function recurse()
1262
 
                        if etype == 'Create' and path:byte(-1) == 47 then
1263
 
                                local entries = lsyncd.readdir(self.source .. path)
 
1659
                local function recurse( )
 
1660
 
 
1661
                        if etype == 'Create' and path:byte( -1 ) == 47 then
 
1662
                                local entries = lsyncd.readdir( self.source .. path )
 
1663
 
1264
1664
                                if entries then
 
1665
 
1265
1666
                                        for dirname, isdir in pairs(entries) do
 
1667
 
1266
1668
                                                local pd = path .. dirname
1267
 
                                                if isdir then pd = pd..'/' end
1268
 
                                                log('Delay', 'Create creates Create on ',pd)
1269
 
                                                delay(self, 'Create', time, pd, nil)
 
1669
 
 
1670
                                                if isdir then
 
1671
                                                        pd = pd..'/'
 
1672
                                                end
 
1673
 
 
1674
                                                log(
 
1675
                                                        'Delay',
 
1676
                                                        'Create creates Create on ',
 
1677
                                                        pd
 
1678
                                                )
 
1679
                                                delay( self, 'Create', time, pd, nil )
 
1680
 
1270
1681
                                        end
 
1682
 
1271
1683
                                end
 
1684
 
1272
1685
                        end
 
1686
 
1273
1687
                end
1274
1688
 
1275
1689
                -- exclusion tests
1276
1690
                if not path2 then
1277
1691
                        -- simple test for single path events
1278
1692
                        if self.excludes:test(path) then
1279
 
                                log('Exclude', 'excluded ',etype,' on "',path,'"')
 
1693
                                log(
 
1694
                                        'Exclude',
 
1695
                                        'excluded ',
 
1696
                                        etype,
 
1697
                                        ' on "',
 
1698
                                        path,
 
1699
                                        '"'
 
1700
                                )
1280
1701
                                return
1281
1702
                        end
1282
1703
                else
1283
1704
                        -- for double paths (move) it might result into a split
1284
 
                        local ex1 = self.excludes:test(path)
1285
 
                        local ex2 = self.excludes:test(path2)
 
1705
                        local ex1 = self.excludes:test( path  )
 
1706
                        local ex2 = self.excludes:test( path2 )
 
1707
 
1286
1708
                        if ex1 and ex2 then
1287
 
                                log('Exclude', 'excluded "',etype,' on "',path,'" -> "',path2,'"')
 
1709
 
 
1710
                                log(
 
1711
                                        'Exclude',
 
1712
                                        'excluded "',
 
1713
                                        etype,
 
1714
                                        ' on "',
 
1715
                                        path,
 
1716
                                        '" -> "',
 
1717
                                        path2,
 
1718
                                        '"'
 
1719
                                )
 
1720
 
1288
1721
                                return
 
1722
 
1289
1723
                        elseif not ex1 and ex2 then
 
1724
 
1290
1725
                                -- splits the move if only partly excluded
1291
 
                                log('Exclude', 'excluded destination transformed ',etype,' to Delete ',path)
1292
 
                                delay(self, 'Delete', time, path, nil)
 
1726
                                log(
 
1727
                                        'Exclude',
 
1728
                                        'excluded destination transformed ',
 
1729
                                        etype,
 
1730
                                        ' to Delete ',
 
1731
                                        path
 
1732
                                )
 
1733
 
 
1734
                                delay(
 
1735
                                        self,
 
1736
                                        'Delete',
 
1737
                                        time,
 
1738
                                        path,
 
1739
                                        nil
 
1740
                                )
 
1741
 
1293
1742
                                return
 
1743
 
1294
1744
                        elseif ex1 and not ex2 then
1295
1745
                                -- splits the move if only partly excluded
1296
 
                                log('Exclude', 'excluded origin transformed ',etype,' to Create.',path2)
1297
 
                                delay(self, 'Create', time, path2, nil)
 
1746
                                log(
 
1747
                                        'Exclude',
 
1748
                                        'excluded origin transformed ',
 
1749
                                        etype,
 
1750
                                        ' to Create.',
 
1751
                                        path2
 
1752
                                )
 
1753
 
 
1754
                                delay(
 
1755
                                        self,
 
1756
                                        'Create',
 
1757
                                        time,
 
1758
                                        path2,
 
1759
                                        nil
 
1760
                                )
 
1761
 
1298
1762
                                return
1299
1763
                        end
1300
1764
                end
1301
1765
 
1302
1766
                if etype == 'Move' and not self.config.onMove then
 
1767
 
1303
1768
                        -- if there is no move action defined,
1304
1769
                        -- split a move as delete/create
1305
1770
                        -- layer 1 scripts which want moves events have to
1306
1771
                        -- set onMove simply to 'true'
1307
 
                        log('Delay', 'splitting Move into Delete & Create')
1308
 
                        delay(self, 'Delete', time, path,  nil)
1309
 
                        delay(self, 'Create', time, path2, nil)
 
1772
                        log( 'Delay', 'splitting Move into Delete & Create' )
 
1773
                        delay( self, 'Delete', time, path,  nil )
 
1774
                        delay( self, 'Create', time, path2, nil )
1310
1775
                        return
 
1776
 
1311
1777
                end
1312
1778
 
1313
1779
                -- creates the new action
1315
1781
                if time and self.config.delay then
1316
1782
                        alarm = time + self.config.delay
1317
1783
                else
1318
 
                        alarm = now()
 
1784
                        alarm = now( )
1319
1785
                end
 
1786
 
1320
1787
                -- new delay
1321
 
                local nd = Delay.new(etype, self, alarm, path, path2)
 
1788
                local nd = Delay.new(
 
1789
                        etype,
 
1790
                        self,
 
1791
                        alarm,
 
1792
                        path,
 
1793
                        path2
 
1794
                )
 
1795
 
1322
1796
                if nd.etype == 'Init' or nd.etype == 'Blanket' then
1323
 
                        -- always stack blanket events on the last event
1324
 
                        log('Delay', 'Stacking ',nd.etype,' event.')
 
1797
 
 
1798
                        -- always stack init or blanket events on the last event
 
1799
                        log(
 
1800
                                'Delay',
 
1801
                                'Stacking ',
 
1802
                                nd.etype,
 
1803
                                ' event.'
 
1804
                        )
 
1805
 
1325
1806
                        if self.delays.size > 0 then
1326
 
                                stack(self.delays[self.delays.last], nd)
 
1807
                                stack( self.delays[ self.delays.last ], nd )
1327
1808
                        end
1328
 
                        nd.dpos = Queue.push(self.delays, nd)
1329
 
                        recurse()
 
1809
 
 
1810
                        nd.dpos = Queue.push( self.delays, nd )
 
1811
                        recurse( )
 
1812
 
1330
1813
                        return
 
1814
 
1331
1815
                end
1332
1816
 
1333
1817
                -- detects blocks and combos by working from back until
1334
1818
                -- front through the fifo
1335
 
                for il, od in Queue.qpairsReverse(self.delays) do
 
1819
                for il, od in Queue.qpairsReverse( self.delays ) do
 
1820
 
1336
1821
                        -- asks Combiner what to do
1337
 
                        local ac = Combiner.combine(od, nd)
 
1822
                        local ac = Combiner.combine( od, nd )
1338
1823
 
1339
1824
                        if ac then
1340
1825
                                if ac == 'remove' then
1341
 
                                        Queue.remove(self.delays, il)
 
1826
                                        Queue.remove( self.delays, il )
1342
1827
                                elseif ac == 'stack' then
1343
 
                                        stack(od, nd)
1344
 
                                        nd.dpos = Queue.push(self.delays, nd)
 
1828
                                        stack( od, nd )
 
1829
                                        nd.dpos = Queue.push( self.delays, nd )
1345
1830
                                elseif ac == 'absorb' then
1346
1831
                                        -- nada
1347
1832
                                elseif ac == 'replace' then
1349
1834
                                        od.path  = nd.path
1350
1835
                                        od.path2 = nd.path2
1351
1836
                                elseif ac == 'split' then
1352
 
                                        delay(self, 'Delete', time, path,  nil)
1353
 
                                        delay(self, 'Create', time, path2, nil)
 
1837
                                        delay( self, 'Delete', time, path,  nil )
 
1838
                                        delay( self, 'Create', time, path2, nil )
1354
1839
                                else
1355
 
                                        error('unknown result of combine()')
 
1840
                                        error( 'unknown result of combine()' )
1356
1841
                                end
1357
 
                                recurse()
 
1842
                                recurse( )
1358
1843
                                return
1359
1844
                        end
 
1845
 
1360
1846
                        il = il - 1
1361
1847
                end
 
1848
 
1362
1849
                if nd.path2 then
1363
 
                        log('Delay','New ',nd.etype,':',nd.path,'->',nd.path2)
 
1850
                        log( 'Delay','New ',nd.etype,':',nd.path,'->',nd.path2 )
1364
1851
                else
1365
 
                        log('Delay','New ',nd.etype,':',nd.path)
 
1852
                        log( 'Delay','New ',nd.etype,':',nd.path )
1366
1853
                end
 
1854
 
1367
1855
                -- no block or combo
1368
 
                nd.dpos = Queue.push(self.delays, nd)
1369
 
                recurse()
 
1856
                nd.dpos = Queue.push( self.delays, nd )
 
1857
                recurse( )
1370
1858
        end
1371
1859
 
1372
 
        -----
1373
 
        -- Returns the nearest alarm for this Sync.
1374
 
        --
1375
 
        local function getAlarm(self)
1376
 
                if self.processes:size() >= self.config.maxProcesses then
 
1860
        --
 
1861
        -- Returns the soonest alarm for this Sync.
 
1862
        --
 
1863
        local function getAlarm( self )
 
1864
 
 
1865
                if self.processes:size( ) >= self.config.maxProcesses then
1377
1866
                        return false
1378
1867
                end
1379
1868
 
1380
1869
                -- first checks if more processes could be spawned
1381
 
                if self.processes:size() < self.config.maxProcesses then
 
1870
                if self.processes:size( ) < self.config.maxProcesses then
 
1871
 
1382
1872
                        -- finds the nearest delay waiting to be spawned
1383
 
                        for _, d in Queue.qpairs(self.delays) do
 
1873
                        for _, d in Queue.qpairs( self.delays ) do
1384
1874
                                if d.status == 'wait' then return d.alarm end
1385
1875
                        end
 
1876
 
1386
1877
                end
1387
1878
 
1388
1879
                -- nothing to spawn
1389
1880
                return false
1390
1881
        end
1391
1882
 
1392
 
        -----
 
1883
        --
1393
1884
        -- Gets all delays that are not blocked by active delays.
1394
1885
        --
1395
1886
        -- @param test   function to test each delay
1396
1887
        --
1397
 
        local function getDelays(self, test)
1398
 
                local dlist = { sync = self}
 
1888
        local function getDelays( self, test )
 
1889
                local dlist  = { sync = self}
1399
1890
                local dlistn = 1
1400
 
                local blocks = {}
 
1891
                local blocks = { }
1401
1892
 
1402
 
                ----
 
1893
                --
1403
1894
                -- inheritly transfers all blocks from delay
1404
1895
                --
1405
 
                local function getBlocks(delay)
1406
 
                        blocks[delay] = true
 
1896
                local function getBlocks( delay )
 
1897
                        blocks[ delay ] = true
1407
1898
                        if delay.blocks then
1408
 
                                for i, d in ipairs(delay.blocks) do
1409
 
                                        getBlocks(d)
 
1899
                                for i, d in ipairs( delay.blocks ) do
 
1900
                                        getBlocks( d )
1410
1901
                                end
1411
1902
                        end
1412
1903
                end
1413
1904
 
1414
 
                for i, d in Queue.qpairs(self.delays) do
 
1905
                for i, d in Queue.qpairs( self.delays ) do
1415
1906
                        if d.status == 'active' or
1416
 
                                (test and not test(InletFactory.d2e(d)))
 
1907
                                ( test and not test( InletFactory.d2e( d ) ) )
1417
1908
                        then
1418
 
                                getBlocks(d)
1419
 
                        elseif not blocks[d] then
1420
 
                                dlist[dlistn] = d
 
1909
                                getBlocks( d )
 
1910
                        elseif not blocks[ d ] then
 
1911
                                dlist[ dlistn ] = d
1421
1912
                                dlistn = dlistn + 1
1422
1913
                        end
1423
1914
                end
1425
1916
                return dlist
1426
1917
        end
1427
1918
 
1428
 
        -----
 
1919
        --
1429
1920
        -- Creates new actions
1430
1921
        --
1431
 
        local function invokeActions(self, timestamp)
1432
 
                log('Function', 'invokeActions("',self.config.name,'",',timestamp,')')
1433
 
                if self.processes:size() >= self.config.maxProcesses then
 
1922
        local function invokeActions( self, timestamp )
 
1923
 
 
1924
                log(
 
1925
                        'Function',
 
1926
                        'invokeActions( "',
 
1927
                                self.config.name, '", ',
 
1928
                                timestamp,
 
1929
                        ' )'
 
1930
                )
 
1931
 
 
1932
                if self.processes:size( ) >= self.config.maxProcesses then
1434
1933
                        -- no new processes
1435
1934
                        return
1436
1935
                end
1437
 
                for _, d in Queue.qpairs(self.delays) do
 
1936
 
 
1937
                for _, d in Queue.qpairs( self.delays ) do
 
1938
 
1438
1939
                        -- if reached the global limit return
1439
 
                        if settings.maxProcesses and processCount >= settings.maxProcesses then
 
1940
                        if uSettings.maxProcesses and
 
1941
                                processCount >= uSettings.maxProcesses
 
1942
                        then
1440
1943
                                log('Alarm', 'at global process limit.')
1441
1944
                                return
1442
1945
                        end
 
1946
 
1443
1947
                        if self.delays.size < self.config.maxDelays then
1444
1948
                                -- time constrains are only concerned if not maxed
1445
1949
                                -- the delay FIFO already.
1448
1952
                                        return
1449
1953
                                end
1450
1954
                        end
 
1955
 
1451
1956
                        if d.status == 'wait' then
 
1957
 
1452
1958
                                -- found a waiting delay
1453
1959
                                if d.etype ~= 'Init' then
1454
 
                                        self.config.action(self.inlet)
 
1960
                                        self.config.action( self.inlet )
1455
1961
                                else
1456
 
                                        self.config.init(InletFactory.d2e(d))
 
1962
                                        self.config.init( InletFactory.d2e( d ) )
1457
1963
                                end
1458
 
                                if self.processes:size() >= self.config.maxProcesses then
 
1964
 
 
1965
                                if self.processes:size( ) >= self.config.maxProcesses then
1459
1966
                                        -- no further processes
1460
1967
                                        return
1461
1968
                                end
1463
1970
                end
1464
1971
        end
1465
1972
 
1466
 
        -----
 
1973
        --
1467
1974
        -- Gets the next event to be processed.
1468
1975
        --
1469
 
        local function getNextDelay(self, timestamp)
1470
 
                for i, d in Queue.qpairs(self.delays) do
 
1976
        local function getNextDelay( self, timestamp )
 
1977
 
 
1978
                for i, d in Queue.qpairs( self.delays ) do
 
1979
 
1471
1980
                        if self.delays.size < self.config.maxDelays then
1472
1981
                                -- time constrains are only concerned if not maxed
1473
1982
                                -- the delay FIFO already.
1476
1985
                                        return nil
1477
1986
                                end
1478
1987
                        end
 
1988
 
1479
1989
                        if d.status == 'wait' then
1480
1990
                                -- found a waiting delay
1481
1991
                                return d
1482
1992
                        end
1483
1993
                end
 
1994
 
1484
1995
        end
1485
1996
 
1486
1997
        ------
1487
1998
        -- Adds and returns a blanket delay thats blocks all.
1488
1999
        -- Used as custom marker.
1489
2000
        --
1490
 
        local function addBlanketDelay(self)
1491
 
                local newd = Delay.new('Blanket', self, true, '')
1492
 
                newd.dpos = Queue.push(self.delays, newd)
 
2001
        local function addBlanketDelay( self )
 
2002
                local newd = Delay.new( 'Blanket', self, true, '' )
 
2003
                newd.dpos = Queue.push( self.delays, newd )
1493
2004
                return newd
1494
2005
        end
1495
2006
 
1496
 
        ------
 
2007
        --
1497
2008
        -- Adds and returns a blanket delay thats blocks all.
1498
2009
        -- Used as startup marker to call init asap.
1499
2010
        --
1500
 
        local function addInitDelay(self)
1501
 
                local newd = Delay.new('Init', self, true, '')
1502
 
                newd.dpos = Queue.push(self.delays, newd)
 
2011
        local function addInitDelay( self )
 
2012
 
 
2013
                local newd = Delay.new( 'Init', self, true, '' )
 
2014
 
 
2015
                newd.dpos = Queue.push( self.delays, newd )
 
2016
 
1503
2017
                return newd
1504
2018
        end
1505
2019
 
1506
 
        -----
 
2020
        --
1507
2021
        -- Writes a status report about delays in this sync.
1508
2022
        --
1509
 
        local function statusReport(self, f)
 
2023
        local function statusReport( self, f )
 
2024
 
1510
2025
                local spaces = '                    '
1511
 
                f:write(self.config.name,' source=',self.source,'\n')
1512
 
                f:write('There are ',self.delays.size, ' delays\n')
1513
 
                for i, vd in Queue.qpairs(self.delays) do
 
2026
 
 
2027
                f:write( self.config.name, ' source=', self.source, '\n' )
 
2028
                f:write( 'There are ', self.delays.size, ' delays\n')
 
2029
 
 
2030
                for i, vd in Queue.qpairs( self.delays ) do
1514
2031
                        local st = vd.status
1515
 
                        f:write(st, string.sub(spaces, 1, 7 - #st))
1516
 
                        f:write(vd.etype,' ')
1517
 
                        f:write(vd.path)
1518
 
                        if (vd.path2) then
1519
 
                                f:write(' -> ',vd.path2)
 
2032
                        f:write( st, string.sub( spaces, 1, 7 - #st ) )
 
2033
                        f:write( vd.etype, ' ' )
 
2034
                        f:write( vd.path )
 
2035
 
 
2036
                        if vd.path2 then
 
2037
                                f:write( ' -> ',vd.path2 )
1520
2038
                        end
 
2039
 
1521
2040
                        f:write('\n')
 
2041
 
1522
2042
                end
1523
 
                f:write('Excluding:\n')
 
2043
 
 
2044
                f:write( 'Excluding:\n' )
 
2045
 
1524
2046
                local nothing = true
1525
 
                for t, p in pairs(self.excludes.list) do
 
2047
 
 
2048
                for t, p in pairs( self.excludes.list ) do
1526
2049
                        nothing = false
1527
 
                        f:write(t,'\n')
 
2050
                        f:write( t,'\n' )
1528
2051
                end
1529
2052
                if nothing then
1530
2053
                        f:write('  nothing.\n')
1531
2054
                end
1532
 
                f:write('\n')
 
2055
 
 
2056
                f:write( '\n' )
1533
2057
        end
1534
2058
 
1535
 
        -----
 
2059
        --
1536
2060
        -- Creates a new Sync
1537
2061
        --
1538
 
        local function new(config)
 
2062
        local function new( config )
1539
2063
                local s = {
1540
2064
                        -- fields
 
2065
 
1541
2066
                        config = config,
1542
 
                        delays = Queue.new(),
 
2067
                        delays = Queue.new( ),
1543
2068
                        source = config.source,
1544
 
                        processes = CountArray.new(),
1545
 
                        excludes = Excludes.new(),
 
2069
                        processes = CountArray.new( ),
 
2070
                        excludes = Excludes.new( ),
1546
2071
 
1547
2072
                        -- functions
 
2073
 
1548
2074
                        addBlanketDelay = addBlanketDelay,
1549
2075
                        addExclude      = addExclude,
1550
2076
                        addInitDelay    = addInitDelay,
1559
2085
                        rmExclude       = rmExclude,
1560
2086
                        statusReport    = statusReport,
1561
2087
                }
1562
 
                s.inlet = InletFactory.newInlet(s)
 
2088
 
 
2089
                s.inlet = InletFactory.newInlet( s )
1563
2090
 
1564
2091
                -- provides a default name if needed
1565
2092
                if not config.name then
1566
 
                        config.name = 'Sync'..nextDefaultName
 
2093
                        config.name = 'Sync' .. nextDefaultName
1567
2094
                end
1568
 
                -- increments default nevertheless to cause less confusion
1569
 
                -- so name will be the n-th call to sync{}
 
2095
 
 
2096
                -- increments defaults if a config name was given or not
 
2097
                -- so Sync{n} will be the n-th call to sync{}
1570
2098
                nextDefaultName = nextDefaultName + 1
1571
2099
 
1572
2100
                -- loads exclusions
1573
2101
                if config.exclude then
1574
 
                        local te = type(config.exclude)
 
2102
 
 
2103
                        local te = type( config.exclude )
 
2104
 
1575
2105
                        if te == 'table' then
1576
 
                                s.excludes:addList(config.exclude)
 
2106
                                s.excludes:addList( config.exclude )
1577
2107
                        elseif te == 'string' then
1578
 
                                s.excludes:add(config.exclude)
 
2108
                                s.excludes:add( config.exclude )
1579
2109
                        else
1580
 
                                error('type for exclude must be table or string', 2)
 
2110
                                error( 'type for exclude must be table or string', 2 )
1581
2111
                        end
1582
2112
                end
 
2113
 
 
2114
                if
 
2115
                        config.delay ~= nil and
 
2116
                        (
 
2117
                                type(config.delay) ~= 'number' or
 
2118
                                config.delay < 0
 
2119
                        )
 
2120
                then
 
2121
                        error( 'delay must be a number and >= 0', 2 )
 
2122
                end
 
2123
 
1583
2124
                if config.excludeFrom then
1584
 
                        s.excludes:loadFile(config.excludeFrom)
 
2125
                        s.excludes:loadFile( config.excludeFrom )
1585
2126
                end
1586
2127
 
1587
2128
                return s
1588
2129
        end
1589
2130
 
1590
 
        -----
1591
 
        -- public interface
1592
 
        --
1593
 
        return {new = new}
1594
 
end)()
1595
 
 
1596
 
 
1597
 
-----
 
2131
        --
 
2132
        -- Public interface
 
2133
        --
 
2134
        return { new = new }
 
2135
 
 
2136
end )( )
 
2137
 
 
2138
 
 
2139
--
1598
2140
-- Syncs - a singleton
1599
2141
--
 
2142
-- Syncs maintains all configured syncs.
1600
2143
--
1601
 
local Syncs = (function()
1602
 
        -----
 
2144
local Syncs = ( function( )
 
2145
 
 
2146
        --
1603
2147
        -- the list of all syncs
1604
2148
        --
1605
 
        local list = Array.new()
 
2149
        local syncsList = Array.new( )
1606
2150
 
1607
 
        -----
 
2151
        --
1608
2152
        -- The round robin pointer. In case of global limited maxProcesses
1609
2153
        -- gives every sync equal chances to spawn the next process.
1610
2154
        --
1611
2155
        local round = 1
1612
2156
 
1613
 
        -----
1614
 
        -- The cycle() sheduler goes into the next round of roundrobin.
1615
 
        local function nextRound()
 
2157
        --
 
2158
        -- The cycle( ) sheduler goes into the next round of roundrobin.
 
2159
        --
 
2160
        local function nextRound( )
 
2161
 
1616
2162
                round = round + 1;
1617
 
                if round > #list then
 
2163
 
 
2164
                if round > #syncsList then
1618
2165
                        round = 1
1619
2166
                end
 
2167
 
1620
2168
                return round
1621
2169
        end
1622
2170
 
1623
 
        -----
 
2171
        --
1624
2172
        -- Returns the round
1625
 
        local function getRound()
 
2173
        --
 
2174
        local function getRound( )
1626
2175
                return round
1627
2176
        end
1628
2177
 
1629
 
        -----
 
2178
        --
1630
2179
        -- Returns sync at listpos i
1631
 
        local function get(i)
1632
 
                return list[i];
1633
 
        end
1634
 
 
1635
 
        -----
1636
 
        -- Inheritly copies all non integer keys from
1637
 
        -- copy source (cs) to copy destination (cd).
1638
 
        --
1639
 
        -- all entries with integer keys are treated as new sources to copy
1640
 
        --
1641
 
        local function inherit(cd, cs)
1642
 
                -- first copies from source all
1643
 
                -- non-defined non-integer keyed values
1644
 
                for k, v in pairs(cs) do
1645
 
                        if type(k) ~= 'number' and cd[k] == nil then
1646
 
                                cd[k] = v
1647
 
                        end
1648
 
                end
1649
 
                -- first recurses into all integer keyed tables
1650
 
                for i, v in ipairs(cs) do
1651
 
                        if type(v) == 'table' then
1652
 
                                inherit(cd, v)
1653
 
                        end
1654
 
                end
1655
 
        end
1656
 
 
1657
 
        -----
 
2180
        --
 
2181
        local function get( i )
 
2182
                return syncsList[ i ];
 
2183
        end
 
2184
 
 
2185
        --
 
2186
        -- Helper function for inherit
 
2187
        -- defined below
 
2188
        --
 
2189
        local inheritKV
 
2190
 
 
2191
        --
 
2192
        -- Recurvely inherits a source table to a destionation table
 
2193
        -- copying all keys from source.
 
2194
        --
 
2195
        -- table copy source ( cs )
 
2196
        -- table copy destination ( cd )
 
2197
        --
 
2198
        -- All entries with integer keys are inherited as additional
 
2199
        -- sources for non-verbatim tables
 
2200
        --
 
2201
        local function inherit( cd, cs )
 
2202
 
 
2203
                --
 
2204
                -- First copies all entries with non-integer keys
 
2205
                -- tables are merged, already present keys are not
 
2206
                -- overwritten
 
2207
                --
 
2208
                -- For verbatim tables integer keys are treated like
 
2209
                -- non integer keys
 
2210
                --
 
2211
                for k, v in pairs( cs ) do
 
2212
                        if
 
2213
                                (
 
2214
                                        type( k ) ~= 'number' or
 
2215
                                        cs._verbatim == true
 
2216
                                )
 
2217
                                and
 
2218
                                (
 
2219
                                        type( cs._merge ) ~= 'table' or
 
2220
                                        cs._merge[ k ] == true
 
2221
                                )
 
2222
                        then
 
2223
                                inheritKV( cd, k, v )
 
2224
                        end
 
2225
                end
 
2226
 
 
2227
                --
 
2228
                -- recursevely inherits all integer keyed tables
 
2229
                -- ( for non-verbatim tables )
 
2230
                --
 
2231
                if cs._verbatim ~= true then
 
2232
 
 
2233
                        local n = nil
 
2234
                        for k, v in ipairs( cs ) do
 
2235
                                n = k
 
2236
                                if type( v ) == 'table' then
 
2237
                                        inherit( cd, v )
 
2238
                                else
 
2239
                                        cd[ #cd + 1 ] = v
 
2240
                                end
 
2241
                        end
 
2242
 
 
2243
                end
 
2244
        end
 
2245
 
 
2246
        --
 
2247
        -- Helper to inherit. Inherits one key.
 
2248
        --
 
2249
        inheritKV = function( cd, k, v )
 
2250
 
 
2251
                -- don't merge inheritance controls
 
2252
                if k == '_merge' or k == '_verbatim' then
 
2253
                        return
 
2254
                end
 
2255
 
 
2256
                local dtype = type( cd [ k ] )
 
2257
 
 
2258
                if type( v ) == 'table' then
 
2259
 
 
2260
                        if dtype == 'nil' then
 
2261
                                cd[ k ] = { }
 
2262
                                inherit( cd[ k ], v )
 
2263
                        elseif
 
2264
                                dtype == 'table' and
 
2265
                                v._merge ~= false
 
2266
                        then
 
2267
                                inherit( cd[ k ], v )
 
2268
                        end
 
2269
 
 
2270
                elseif dtype == 'nil' then
 
2271
                        cd[ k ] = v
 
2272
                end
 
2273
 
 
2274
        end
 
2275
 
 
2276
 
 
2277
        --
1658
2278
        -- Adds a new sync (directory-tree to observe).
1659
2279
        --
1660
 
        local function add(config)
1661
 
                -- Creates a new config table and inherit all keys/values
 
2280
        local function add( config )
 
2281
 
 
2282
                -- workaround for backwards compatibility
 
2283
                -- FIXME: remove when dropping that
 
2284
                if settings ~= settingsSafe then
 
2285
                        log(
 
2286
                                'Warn',
 
2287
                                'settings = { ... } is deprecated.\n'..
 
2288
                                '      please use settings{ ... } (without the equal sign)'
 
2289
                        )
 
2290
 
 
2291
                        for k, v in pairs( settings ) do
 
2292
                                uSettings[ k ] = v
 
2293
                        end
 
2294
 
 
2295
                        settings = settingsSafe
 
2296
                end
 
2297
 
 
2298
                -- Creates a new config table which inherits all keys/values
1662
2299
                -- from integer keyed tables
1663
2300
                local uconfig = config
1664
 
                config = {}
1665
 
                inherit(config, uconfig)
1666
 
 
1667
 
                -- Lets settings or commandline override delay values.
1668
 
                if settings then
1669
 
                        config.delay = settings.delay or config.delay
1670
 
                end
1671
 
 
1672
 
                -- at very first lets the userscript 'prepare' function
1673
 
                -- fill out more values.
1674
 
                if type(config.prepare) == 'function' then
1675
 
                        -- explicitly gives a writeable copy of config.
1676
 
                        config.prepare(config)
1677
 
                end
1678
 
 
1679
 
                if not config['source'] then
1680
 
                        local info = debug.getinfo(3, 'Sl')
1681
 
                        log('Error', info.short_src,':',info.currentline,': source missing from sync.')
1682
 
                        terminate(-1) -- ERRNO
1683
 
                end
1684
 
 
 
2301
 
 
2302
                config = { }
 
2303
 
 
2304
                inherit( config, uconfig )
 
2305
 
 
2306
                --
 
2307
                -- last and least defaults are inherited
 
2308
                --
 
2309
                inherit( config, default )
 
2310
 
 
2311
                local inheritSettings = {
 
2312
                        'delay',
 
2313
                        'maxDelays',
 
2314
                        'maxProcesses'
 
2315
                }
 
2316
 
 
2317
                -- Lets settings override these values.
 
2318
                for _, v in ipairs( inheritSettings ) do
 
2319
                        if uSettings[ v ] then
 
2320
                                config[ v ] = uSettings[ v ]
 
2321
                        end
 
2322
                end
 
2323
 
 
2324
                -- Lets commandline override these values.
 
2325
                for _, v in ipairs( inheritSettings ) do
 
2326
                        if clSettings[ v ] then
 
2327
                                config[ v ] = clSettings[ v ]
 
2328
                        end
 
2329
                end
 
2330
 
 
2331
                --
 
2332
                -- lets the userscript 'prepare' function
 
2333
                -- check and complete the config
 
2334
                --
 
2335
                if type( config.prepare ) == 'function' then
 
2336
 
 
2337
                        -- prepare is given a writeable copy of config
 
2338
                        config.prepare( config, 4 )
 
2339
 
 
2340
                end
 
2341
 
 
2342
                if not config[ 'source' ] then
 
2343
                        local info = debug.getinfo( 3, 'Sl' )
 
2344
                        log(
 
2345
                                'Error',
 
2346
                                info.short_src,':',
 
2347
                                info.currentline,': source missing from sync.'
 
2348
                        )
 
2349
                        terminate( -1 )
 
2350
                end
 
2351
 
 
2352
                --
1685
2353
                -- absolute path of source
1686
 
                local realsrc = lsyncd.realdir(config.source)
 
2354
                --
 
2355
                local realsrc = lsyncd.realdir( config.source )
 
2356
 
1687
2357
                if not realsrc then
1688
 
                        log('Error', 'Cannot access source directory: ',config.source)
1689
 
                        terminate(-1) -- ERRNO
 
2358
                        log(
 
2359
                                'Error',
 
2360
                                'Cannot access source directory: ',
 
2361
                                config.source
 
2362
                        )
 
2363
                        terminate( -1 )
1690
2364
                end
 
2365
 
1691
2366
                config._source = config.source
1692
2367
                config.source = realsrc
1693
2368
 
1694
 
                if not config.action   and not config.onAttrib and
1695
 
                   not config.onCreate and not config.onModify and
1696
 
                   not config.onDelete and not config.onMove
 
2369
                if
 
2370
                        not config.action   and
 
2371
                        not config.onAttrib and
 
2372
                        not config.onCreate and
 
2373
                        not config.onModify and
 
2374
                        not config.onDelete and
 
2375
                        not config.onMove
1697
2376
                then
1698
 
                        local info = debug.getinfo(3, 'Sl')
1699
 
                        log('Error', info.short_src, ':', info.currentline,
1700
 
                                ': no actions specified, use e.g. "config = default.rsync".')
1701
 
                        terminate(-1) -- ERRNO
1702
 
                end
 
2377
                        local info = debug.getinfo( 3, 'Sl' )
 
2378
                        log(
 
2379
                                'Error',
 
2380
                                info.short_src, ':',
 
2381
                                info.currentline,
 
2382
                                ': no actions specified.'
 
2383
                        )
1703
2384
 
1704
 
                -- loads a default value for an option if not existent
1705
 
                if not settings then
1706
 
                        settings = {}
1707
 
                end
1708
 
                local defaultValues = {
1709
 
                        'action',
1710
 
                        'collect',
1711
 
                        'init',
1712
 
                        'maxDelays',
1713
 
                        'maxProcesses',
1714
 
                }
1715
 
                for _, dn in pairs(defaultValues) do
1716
 
                        if config[dn] == nil then
1717
 
                                config[dn] = settings[dn] or default[dn]
1718
 
                        end
 
2385
                        terminate( -1 )
1719
2386
                end
1720
2387
 
1721
2388
                -- the monitor to use
1722
2389
                config.monitor =
1723
 
                        settings.monitor or config.monitor or Monitors.default()
1724
 
                if config.monitor ~= 'inotify' and config.monitor ~= 'fsevents' then
1725
 
                        local info = debug.getinfo(3, 'Sl')
1726
 
                        log('Error',info.short_src,':',info.currentline,
1727
 
                                ': event monitor "',config.monitor,'" unknown.')
1728
 
                        terminate(-1) -- ERRNO
 
2390
                        uSettings.monitor or
 
2391
                        config.monitor or
 
2392
                        Monitors.default( )
 
2393
 
 
2394
                if
 
2395
                        config.monitor ~= 'inotify' and
 
2396
                        config.monitor ~= 'fsevents'
 
2397
                then
 
2398
                        local info = debug.getinfo( 3, 'Sl' )
 
2399
 
 
2400
                        log(
 
2401
                                'Error',
 
2402
                                info.short_src, ':',
 
2403
                                info.currentline,
 
2404
                                ': event monitor "',
 
2405
                                config.monitor,
 
2406
                                '" unknown.'
 
2407
                        )
 
2408
 
 
2409
                        terminate( -1 )
1729
2410
                end
1730
2411
 
1731
2412
                --- creates the new sync
1732
 
                local s = Sync.new(config)
1733
 
                table.insert(list, s)
 
2413
                local s = Sync.new( config )
 
2414
 
 
2415
                table.insert( syncsList, s )
 
2416
 
1734
2417
                return s
1735
2418
        end
1736
2419
 
1737
 
        -----
 
2420
        --
1738
2421
        -- Allows a for-loop to walk through all syncs.
1739
2422
        --
1740
 
        local function iwalk()
1741
 
                return ipairs(list)
 
2423
        local function iwalk( )
 
2424
                return ipairs( syncsList )
1742
2425
        end
1743
2426
 
1744
 
        -----
 
2427
        --
1745
2428
        -- Returns the number of syncs.
1746
2429
        --
1747
 
        local size = function()
1748
 
                return #list
 
2430
        local size = function( )
 
2431
                return #syncsList
1749
2432
        end
1750
2433
 
1751
 
        ------
 
2434
        --
1752
2435
        -- Tests if any sync is interested in a path.
1753
2436
        --
1754
 
        local function concerns(path)
1755
 
                for _, s in ipairs(list) do
1756
 
                        if s:concerns(path) then
 
2437
        local function concerns( path )
 
2438
                for _, s in ipairs( syncsList ) do
 
2439
                        if s:concerns( path ) then
1757
2440
                                return true
1758
2441
                        end
1759
2442
                end
 
2443
 
1760
2444
                return false
1761
2445
        end
1762
2446
 
1763
 
        -- public interface
 
2447
        --
 
2448
        -- Public interface
 
2449
        --
1764
2450
        return {
1765
2451
                add = add,
1766
2452
                get = get,
1771
2456
                nextRound = nextRound,
1772
2457
                size = size
1773
2458
        }
1774
 
end)()
1775
 
 
1776
 
 
1777
 
-----
 
2459
end )( )
 
2460
 
 
2461
 
 
2462
--
 
2463
-- Utility function,
 
2464
-- Returns the relative part of absolute path if it
1778
2465
-- begins with root
1779
2466
--
1780
 
local function splitPath(path, root)
 
2467
local function splitPath( path, root )
 
2468
 
1781
2469
        local rlen = #root
1782
 
        local sp = string.sub(path, 1, rlen)
 
2470
        local sp = string.sub( path, 1, rlen )
1783
2471
 
1784
2472
        if sp == root then
1785
 
                return string.sub(path, rlen, -1)
 
2473
                return string.sub( path, rlen, -1 )
1786
2474
        else
1787
2475
                return nil
1788
2476
        end
1789
2477
end
1790
2478
 
1791
 
-----
1792
 
--
1793
 
--
1794
 
local Inotify = (function()
1795
 
 
1796
 
        -----
1797
 
        -- A list indexed by inotifies watch descriptor yielding the
1798
 
        -- directories absolute paths.
1799
 
        --
1800
 
        local wdpaths = CountArray.new()
1801
 
 
1802
 
        -----
1803
 
        -- The same vice versa, all watch descriptors by its
1804
 
        -- absolute path.
1805
 
        --
1806
 
        local pathwds = {}
1807
 
 
1808
 
        -----
1809
 
        -- A list indexed by sync's containing the root path this
1810
 
        -- sync is interested in.
1811
 
        --
1812
 
        local syncRoots = {}
1813
 
 
1814
 
        -----
 
2479
--
 
2480
-- Interface to inotify.
 
2481
--
 
2482
-- watches recursively subdirs and sends events.
 
2483
--
 
2484
-- All inotify specific implementation is enclosed here.
 
2485
--
 
2486
local Inotify = ( function( )
 
2487
 
 
2488
        --
 
2489
        -- A list indexed by inotify watch descriptors yielding
 
2490
        -- the directories absolute paths.
 
2491
        --
 
2492
        local wdpaths = CountArray.new( )
 
2493
 
 
2494
        --
 
2495
        -- The same vice versa,
 
2496
        -- all watch descriptors by their absolute paths.
 
2497
        --
 
2498
        local pathwds = { }
 
2499
 
 
2500
        --
 
2501
        -- A list indexed by syncs containing yielding
 
2502
        -- the root paths the syncs are interested in.
 
2503
        --
 
2504
        local syncRoots = { }
 
2505
 
 
2506
        --
1815
2507
        -- Stops watching a directory
1816
2508
        --
1817
 
        -- @param path    absolute path to unwatch
1818
 
        -- @param core    if false not actually send the unwatch to the kernel
1819
 
        --                (used in moves which reuse the watch)
 
2509
        -- path ... absolute path to unwatch
 
2510
        -- core ... if false not actually send the unwatch to the kernel
 
2511
        --          (used in moves which reuse the watch)
1820
2512
        --
1821
 
        local function removeWatch(path, core)
1822
 
                local wd = pathwds[path]
1823
 
                if not wd then return end
1824
 
                if core then lsyncd.inotify.rmwatch(wd) end
1825
 
                wdpaths[wd] = nil
1826
 
                pathwds[path] = nil
 
2513
        local function removeWatch( path, core )
 
2514
 
 
2515
                local wd = pathwds[ path ]
 
2516
 
 
2517
                if not wd then
 
2518
                        return
 
2519
                end
 
2520
 
 
2521
                if core then
 
2522
                        lsyncd.inotify.rmwatch( wd )
 
2523
                end
 
2524
 
 
2525
                wdpaths[ wd   ] = nil
 
2526
                pathwds[ path ] = nil
1827
2527
        end
1828
2528
 
1829
 
        -----
 
2529
 
 
2530
        --
1830
2531
        -- Adds watches for a directory (optionally) including all subdirectories.
1831
2532
        --
1832
2533
        -- @param path       absolute path of directory to observe
1833
2534
        -- @param recurse    true if recursing into subdirs
1834
2535
        --
1835
2536
        local function addWatch(path)
1836
 
                log('Function','Inotify.addWatch(',path,')')
 
2537
 
 
2538
                log(
 
2539
                        'Function',
 
2540
                        'Inotify.addWatch( ',
 
2541
                                path,
 
2542
                        ' )'
 
2543
                )
1837
2544
 
1838
2545
                if not Syncs.concerns(path) then
1839
2546
                        log('Inotify', 'not concerning "',path,'"')
1845
2548
                end
1846
2549
 
1847
2550
                -- registers the watch
1848
 
                local inotifyMode = (settings and settings.inotifyMode) or '';
1849
 
                local wd = lsyncd.inotify.addwatch(path, inotifyMode);
 
2551
                local inotifyMode = ( uSettings and uSettings.inotifyMode ) or '';
 
2552
 
 
2553
                local wd = lsyncd.inotify.addwatch( path, inotifyMode) ;
 
2554
 
1850
2555
                if wd < 0 then
1851
 
                        log('Inotify','Unable to add watch "',path,'"')
 
2556
                        log( 'Inotify','Unable to add watch "', path, '"' )
1852
2557
                        return
1853
2558
                end
1854
2559
 
1855
2560
                do
1856
2561
                        -- If this watch descriptor is registered already
1857
2562
                        -- the kernel reuses it since old dir is gone.
1858
 
                        local op = wdpaths[wd]
 
2563
                        local op = wdpaths[ wd ]
1859
2564
                        if op and op ~= path then
1860
 
                                pathwds[op] = nil
 
2565
                                pathwds[ op ] = nil
1861
2566
                        end
1862
2567
                end
1863
 
                pathwds[path] = wd
1864
 
                wdpaths[wd] = path
 
2568
 
 
2569
                pathwds[ path ] = wd
 
2570
                wdpaths[ wd   ] = path
1865
2571
 
1866
2572
                -- registers and adds watches for all subdirectories
1867
 
                local entries = lsyncd.readdir(path)
1868
 
                if not entries then return end
1869
 
                for dirname, isdir in pairs(entries) do
1870
 
                        if isdir then addWatch(path .. dirname .. '/') end
 
2573
                local entries = lsyncd.readdir( path )
 
2574
 
 
2575
                if not entries then
 
2576
                        return
 
2577
                end
 
2578
 
 
2579
                for dirname, isdir in pairs( entries ) do
 
2580
                        if isdir then
 
2581
                                addWatch( path .. dirname .. '/' )
 
2582
                        end
1871
2583
                end
1872
2584
        end
1873
2585
 
1874
 
        -----
 
2586
        --
1875
2587
        -- Adds a Sync to receive events.
1876
2588
        --
1877
2589
        -- sync:    Object to receive events
1878
2590
        -- rootdir: root dir to watch
1879
2591
        --
1880
 
        local function addSync(sync, rootdir)
1881
 
                if syncRoots[sync] then error('duplicate sync in Inotify.addSync()') end
1882
 
                syncRoots[sync] = rootdir
1883
 
                addWatch(rootdir)
 
2592
        local function addSync( sync, rootdir )
 
2593
                if syncRoots[ sync ] then
 
2594
                        error( 'duplicate sync in Inotify.addSync()' )
 
2595
                end
 
2596
                syncRoots[ sync ] = rootdir
 
2597
                addWatch( rootdir )
1884
2598
        end
1885
2599
 
1886
 
        -----
 
2600
        --
1887
2601
        -- Called when an event has occured.
1888
2602
        --
1889
 
        -- etype:     'Attrib', 'Modify', 'Create', 'Delete', 'Move'
1890
 
        -- wd:        watch descriptor, matches lsyncd.inotifyadd()
1891
 
        -- isdir:     true if filename is a directory
1892
 
        -- time:      time of event
1893
 
        -- filename:  string filename without path
1894
 
        -- wd2:       watch descriptor for target if it's a Move
1895
 
        -- filename2: string filename without path of Move target
1896
 
        --
1897
 
        local function event(etype, wd, isdir, time, filename, wd2, filename2)
 
2603
        local function event(
 
2604
                etype,     -- 'Attrib', 'Modify', 'Create', 'Delete', 'Move'
 
2605
                wd,        --  watch descriptor, matches lsyncd.inotifyadd()
 
2606
                isdir,     --  true if filename is a directory
 
2607
                time,      --  time of event
 
2608
                filename,  --  string filename without path
 
2609
                wd2,       --  watch descriptor for target if it's a Move
 
2610
                filename2  --  string filename without path of Move target
 
2611
        )
1898
2612
                if isdir then
1899
 
                        filename = filename..'/'
1900
 
                        if filename2 then filename2 = filename2..'/' end
 
2613
                        filename = filename .. '/'
 
2614
 
 
2615
                        if filename2 then
 
2616
                                filename2 = filename2 .. '/'
 
2617
                        end
1901
2618
                end
1902
2619
 
1903
2620
                if filename2 then
1904
 
                        log('Inotify','got event ',etype,' ',filename,'(',wd,') to ',filename2,'(',wd2,')')
 
2621
                        log(
 
2622
                                'Inotify',
 
2623
                                'got event ',
 
2624
                                etype,
 
2625
                                ' ',
 
2626
                                filename,
 
2627
                                '(', wd, ') to ',
 
2628
                                filename2,
 
2629
                                '(', wd2 ,')'
 
2630
                        )
1905
2631
                else
1906
 
                        log('Inotify','got event ',etype,' ',filename,'(',wd,')')
 
2632
                        log(
 
2633
                                'Inotify',
 
2634
                                'got event ',
 
2635
                                etype,
 
2636
                                ' ',
 
2637
                                filename,
 
2638
                                '(', wd, ')'
 
2639
                        )
1907
2640
                end
1908
2641
 
1909
2642
                -- looks up the watch descriptor id
1910
 
                local path = wdpaths[wd]
1911
 
                if path then path = path..filename end
1912
 
 
1913
 
                local path2 = wd2 and wdpaths[wd2]
1914
 
                if path2 and filename2 then path2 = path2..filename2 end
 
2643
                local path = wdpaths[ wd ]
 
2644
                if path then
 
2645
                        path = path..filename
 
2646
                end
 
2647
 
 
2648
                local path2 = wd2 and wdpaths[ wd2 ]
 
2649
 
 
2650
                if path2 and filename2 then
 
2651
                        path2 = path2..filename2
 
2652
                end
1915
2653
 
1916
2654
                if not path and path2 and etype == 'Move' then
1917
 
                        log('Inotify', 'Move from deleted directory ',path2,' becomes Create.')
1918
 
                        path = path2
 
2655
                        log(
 
2656
                                'Inotify',
 
2657
                                'Move from deleted directory ',
 
2658
                                path2,
 
2659
                                ' becomes Create.'
 
2660
                        )
 
2661
                        path  = path2
1919
2662
                        path2 = nil
1920
2663
                        etype = 'Create'
1921
2664
                end
1922
2665
 
1923
2666
                if not path then
1924
2667
                        -- this is normal in case of deleted subdirs
1925
 
                        log('Inotify', 'event belongs to unknown watch descriptor.')
 
2668
                        log(
 
2669
                                'Inotify',
 
2670
                                'event belongs to unknown watch descriptor.'
 
2671
                        )
1926
2672
                        return
1927
2673
                end
1928
2674
 
1929
 
                for sync, root in pairs(syncRoots) do repeat
1930
 
                        local relative  = splitPath(path, root)
 
2675
                for sync, root in pairs( syncRoots ) do repeat
 
2676
 
 
2677
                        local relative  = splitPath( path, root )
1931
2678
                        local relative2 = nil
 
2679
 
1932
2680
                        if path2 then
1933
 
                                relative2 = splitPath(path2, root)
 
2681
                                relative2 = splitPath( path2, root )
1934
2682
                        end
 
2683
 
1935
2684
                        if not relative and not relative2 then
1936
2685
                                -- sync is not interested in this dir
1937
2686
                                break -- continue
1939
2688
 
1940
2689
                        -- makes a copy of etype to possibly change it
1941
2690
                        local etyped = etype
 
2691
 
1942
2692
                        if etyped == 'Move' then
1943
2693
                                if not relative2 then
1944
 
                                        log('Normal', 'Transformed Move to Delete for ', sync.config.name)
 
2694
                                        log(
 
2695
                                                'Normal',
 
2696
                                                'Transformed Move to Delete for ',
 
2697
                                                sync.config.name
 
2698
                                        )
1945
2699
                                        etyped = 'Delete'
1946
2700
                                elseif not relative then
1947
2701
                                        relative = relative2
1948
2702
                                        relative2 = nil
1949
 
                                        log('Normal', 'Transformed Move to Create for ', sync.config.name)
 
2703
                                        log(
 
2704
                                                'Normal',
 
2705
                                                'Transformed Move to Create for ',
 
2706
                                                sync.config.name
 
2707
                                        )
1950
2708
                                        etyped = 'Create'
1951
2709
                                end
1952
2710
                        end
1953
2711
 
1954
2712
                        if isdir then
1955
2713
                                if etyped == 'Create' then
1956
 
                                        addWatch(path)
 
2714
                                        addWatch( path )
1957
2715
                                elseif etyped == 'Delete' then
1958
 
                                        removeWatch(path, true)
 
2716
                                        removeWatch( path, true )
1959
2717
                                elseif etyped == 'Move' then
1960
 
                                        removeWatch(path, false)
1961
 
                                        addWatch(path2)
 
2718
                                        removeWatch( path, false )
 
2719
                                        addWatch( path2 )
1962
2720
                                end
1963
2721
                        end
1964
2722
 
1965
 
                        sync:delay(etyped, time, relative, relative2)
 
2723
                        sync:delay( etyped, time, relative, relative2 )
 
2724
 
1966
2725
                until true end
1967
2726
        end
1968
2727
 
1969
 
        -----
1970
 
        -- Writes a status report about inotifies to a filedescriptor
1971
 
        --
1972
 
        local function statusReport(f)
1973
 
                f:write('Inotify watching ',wdpaths:size(),' directories\n')
1974
 
                for wd, path in wdpaths:walk() do
1975
 
                        f:write('  ',wd,': ',path,'\n')
 
2728
        --
 
2729
        -- Writes a status report about inotify to a file descriptor
 
2730
        --
 
2731
        local function statusReport( f )
 
2732
 
 
2733
                f:write( 'Inotify watching ', wdpaths:size(), ' directories\n' )
 
2734
 
 
2735
                for wd, path in wdpaths:walk( ) do
 
2736
                        f:write( '  ', wd, ': ', path, '\n' )
1976
2737
                end
1977
2738
        end
1978
2739
 
1979
 
        -- public interface
 
2740
 
 
2741
        --
 
2742
        -- Public interface.
 
2743
        --
1980
2744
        return {
1981
2745
                addSync = addSync,
1982
2746
                event = event,
1983
2747
                statusReport = statusReport,
1984
2748
        }
1985
 
end)()
1986
 
 
1987
 
----
1988
 
--
1989
 
--
1990
 
local Fsevents = (function()
1991
 
 
1992
 
        -----
1993
 
        -- A list indexed by sync's containing the root path this
1994
 
        -- sync is interested in.
1995
 
        --
1996
 
        local syncRoots = {}
1997
 
 
1998
 
        -----
1999
 
        -- adds a Sync to receive events
 
2749
 
 
2750
end)( )
 
2751
 
 
2752
--
 
2753
-- Interface to OSX /dev/fsevents
 
2754
--
 
2755
-- This watches all the filesystems at once,
 
2756
-- but needs root access.
 
2757
--
 
2758
-- All fsevents specific implementation are enclosed here.
 
2759
--
 
2760
local Fsevents = ( function( )
 
2761
 
 
2762
 
 
2763
        --
 
2764
        -- A list indexed by syncs yielding
 
2765
        -- the root path the sync is interested in.
 
2766
        --
 
2767
        local syncRoots = { }
 
2768
 
 
2769
 
 
2770
        --
 
2771
        -- Adds a Sync to receive events.
2000
2772
        --
2001
2773
        -- @param sync   Object to receive events
2002
2774
        -- @param dir    dir to watch
2003
2775
        --
2004
 
        local function addSync(sync, dir)
2005
 
                if syncRoots[sync] then error('duplicate sync in Fanotify.addSync()') end
2006
 
                syncRoots[sync] = dir
 
2776
        local function addSync( sync, dir )
 
2777
 
 
2778
                if syncRoots[ sync ] then
 
2779
                        error( 'duplicate sync in Fanotify.addSync()' )
 
2780
                end
 
2781
 
 
2782
                syncRoots[ sync ] = dir
 
2783
 
2007
2784
        end
2008
2785
 
2009
 
        -----
2010
 
        -- Called when any event has occured.
2011
 
        --
2012
 
        -- etype:  'Attrib', 'Modify', 'Create', 'Delete', 'Move')
2013
 
        -- isdir:  true if filename is a directory
2014
 
        -- time:   time of event
2015
 
        -- path:   path of file
2016
 
        -- path2:  path of target in case of 'Move'
2017
 
        --
2018
 
        local function event(etype, isdir, time, path, path2)
 
2786
        --
 
2787
        -- Called when an event has occured.
 
2788
        --
 
2789
        local function event(
 
2790
                etype,  --  'Attrib', 'Modify', 'Create', 'Delete', 'Move'
 
2791
                isdir,  --  true if filename is a directory
 
2792
                time,   --  time of event
 
2793
                path,   --  path of file
 
2794
                path2   --  path of target in case of 'Move'
 
2795
        )
2019
2796
                if isdir then
2020
 
                        path = path..'/'
2021
 
                        if path2 then path2 = path2..'/' end
 
2797
                        path = path .. '/'
 
2798
 
 
2799
                        if path2 then
 
2800
                                path2 = path2 .. '/'
 
2801
                        end
2022
2802
                end
2023
2803
 
2024
 
                log('Fsevents',etype,',',isdir,',',time,',',path,',',path2)
 
2804
                log(
 
2805
                        'Fsevents',
 
2806
                        etype, ',',
 
2807
                        isdir, ',',
 
2808
                        time,  ',',
 
2809
                        path,  ',',
 
2810
                        path2
 
2811
                )
2025
2812
 
2026
2813
                for _, sync in Syncs.iwalk() do repeat
 
2814
 
2027
2815
                        local root = sync.source
2028
 
                        if not path:starts(root) then
2029
 
                                if not path2 or not path2:starts(root) then
 
2816
 
 
2817
                        -- TODO combine ifs
 
2818
                        if not path:starts( root ) then
 
2819
                                if not path2 or not path2:starts( root ) then
2030
2820
                                        break  -- continue
2031
2821
                                end
2032
2822
                        end
2033
 
                        local relative  = splitPath(path, root)
 
2823
 
 
2824
                        local relative  = splitPath( path, root )
 
2825
 
2034
2826
                        local relative2
2035
2827
                        if path2 then
2036
 
                                relative2 = splitPath(path2, root)
 
2828
                                relative2 = splitPath( path2, root )
2037
2829
                        end
2038
2830
 
2039
2831
                        -- possibly change etype for this iteration only
2051
2841
                                        etyped = 'Create'
2052
2842
                                end
2053
2843
                        end
2054
 
                        sync:delay(etyped, time, relative, relative2)
 
2844
 
 
2845
                        sync:delay( etyped, time, relative, relative2 )
 
2846
 
2055
2847
                until true end
 
2848
 
2056
2849
        end
2057
2850
 
2058
 
        -----
2059
 
        -- Writes a status report about inotifies to a filedescriptor
2060
 
        --
2061
 
        local function statusReport(f)
 
2851
 
 
2852
        --
 
2853
        -- Writes a status report about fsevents to a filedescriptor.
 
2854
        --
 
2855
        local function statusReport( f )
2062
2856
                -- TODO
2063
2857
        end
2064
2858
 
2065
 
        -- public interface
 
2859
        --
 
2860
        -- Public interface
 
2861
        --
2066
2862
        return {
2067
 
                addSync = addSync,
2068
 
                event = event,
 
2863
                addSync      = addSync,
 
2864
                event        = event,
2069
2865
                statusReport = statusReport
2070
2866
        }
2071
 
end)()
2072
 
 
2073
 
-----
 
2867
end )( )
 
2868
 
 
2869
 
 
2870
--
2074
2871
-- Holds information about the event monitor capabilities
2075
2872
-- of the core.
2076
2873
--
2077
 
Monitors = (function()
2078
 
 
2079
 
        -----
 
2874
Monitors = ( function( )
 
2875
 
 
2876
 
 
2877
        --
2080
2878
        -- The cores monitor list
2081
2879
        --
2082
 
        local list = {}
2083
 
 
2084
 
        -----
 
2880
        local list = { }
 
2881
 
 
2882
 
 
2883
        --
2085
2884
        -- The default event monitor.
2086
2885
        --
2087
 
        local function default()
2088
 
                return list[1]
 
2886
        local function default( )
 
2887
                return list[ 1 ]
2089
2888
        end
2090
2889
 
2091
 
        -----
2092
 
        -- initializes with info received from core
2093
 
        --
2094
 
        local function initialize(clist)
2095
 
                for k, v in ipairs(clist) do
2096
 
                        list[k] = v
 
2890
 
 
2891
        --
 
2892
        -- Initializes with info received from core
 
2893
        --
 
2894
        local function initialize( clist )
 
2895
                for k, v in ipairs( clist ) do
 
2896
                        list[ k ] = v
2097
2897
                end
2098
2898
        end
2099
2899
 
2100
 
        -- public interface
2101
 
        return { default = default,
2102
 
                         list = list,
2103
 
                 initialize = initialize
 
2900
 
 
2901
        --
 
2902
        -- Public interface
 
2903
        --
 
2904
        return {
 
2905
                default = default,
 
2906
                list = list,
 
2907
                initialize = initialize
2104
2908
        }
2105
 
end)()
2106
 
 
2107
 
------
2108
 
--
2109
 
local functionWriter = (function()
2110
 
 
2111
 
        -----
2112
 
        -- all variables for layer 3
 
2909
 
 
2910
end)( )
 
2911
 
 
2912
--
 
2913
-- Writes functions for the user for layer 3 configurations.
 
2914
--
 
2915
local functionWriter = ( function( )
 
2916
 
 
2917
        --
 
2918
        -- All variables known to layer 3 configs.
 
2919
        --
2113
2920
        transVars = {
2114
2921
                { '%^pathname',          'event.pathname',        1 },
2115
2922
                { '%^pathdir',           'event.pathdir',         1 },
2141
2947
                { '%^d%.targetPath',     'event2.targetPath',     2 },
2142
2948
        }
2143
2949
 
2144
 
        -----
2145
 
        -- Splits a user string into its arguments
2146
 
        --
2147
 
        -- @param a string where parameters are seperated by spaces.
2148
 
        --
2149
 
        -- @return a table of arguments
2150
 
        --
2151
 
        local function splitStr(str)
2152
 
                local args = {}
 
2950
        --
 
2951
        -- Splits a user string into its arguments.
 
2952
        -- Returns a table of arguments
 
2953
        --
 
2954
        local function splitStr(
 
2955
                str -- a string where parameters are seperated by spaces.
 
2956
        )
 
2957
                local args = { }
 
2958
 
2153
2959
                while str ~= '' do
 
2960
 
2154
2961
                        -- break where argument stops
2155
2962
                        local bp = #str
 
2963
 
2156
2964
                        -- in a quote
2157
2965
                        local inQuote = false
 
2966
 
2158
2967
                        -- tests characters to be space and not within quotes
2159
 
                        for i=1,#str do
2160
 
                                local c = string.sub(str, i, i)
 
2968
                        for i=1, #str do
 
2969
                                local c = string.sub( str, i, i )
 
2970
 
2161
2971
                                if c == '"' then
2162
2972
                                        inQuote = not inQuote
2163
2973
                                elseif c == ' ' and not inQuote then
2165
2975
                                        break
2166
2976
                                end
2167
2977
                        end
2168
 
                        local arg = string.sub(str, 1, bp)
2169
 
                        arg = string.gsub(arg, '"', '\\"')
2170
 
                        table.insert(args, arg)
2171
 
                        str = string.sub(str, bp + 1, -1)
2172
 
                        str = string.match(str, '^%s*(.-)%s*$')
 
2978
 
 
2979
                        local arg = string.sub( str, 1, bp )
 
2980
                        arg = string.gsub( arg, '"', '\\"' )
 
2981
                        table.insert( args, arg )
 
2982
                        str = string.sub( str, bp + 1, -1 )
 
2983
                        str = string.match( str, '^%s*(.-)%s*$' )
 
2984
 
2173
2985
                end
 
2986
 
2174
2987
                return args
2175
2988
        end
2176
2989
 
2177
 
        -----
 
2990
 
 
2991
        --
2178
2992
        -- Translates a call to a binary to a lua function.
2179
 
        --
2180
 
        -- TODO this has a little too much coding blocks.
2181
 
        --
2182
 
        local function translateBinary(str)
 
2993
        -- TODO this has a little too blocking.
 
2994
        --
 
2995
        local function translateBinary( str )
 
2996
 
2183
2997
                -- splits the string
2184
 
                local args = splitStr(str)
 
2998
                local args = splitStr( str )
2185
2999
 
2186
3000
                -- true if there is a second event
2187
3001
                local haveEvent2 = false
2188
3002
 
2189
 
                for ia, iv in ipairs(args) do
 
3003
                for ia, iv in ipairs( args ) do
 
3004
 
2190
3005
                        -- a list of arguments this arg is being split into
2191
 
                        local a = {{true, iv}}
 
3006
                        local a = { { true, iv } }
 
3007
 
2192
3008
                        -- goes through all translates
2193
 
                        for _, v in ipairs(transVars) do
 
3009
                        for _, v in ipairs( transVars ) do
2194
3010
                                local ai = 1
2195
3011
                                while ai <= #a do
2196
 
                                        if a[ai][1] then
 
3012
                                        if a[ ai ][ 1 ] then
2197
3013
                                                local pre, post =
2198
 
                                                        string.match(a[ai][2], '(.*)"..v[1].."(.*)')
 
3014
                                                        string.match( a[ ai ][ 2 ], '(.*)'..v[1]..'(.*)' )
 
3015
 
2199
3016
                                                if pre then
 
3017
 
2200
3018
                                                        if v[3] > 1 then
2201
3019
                                                                haveEvent2 = true
2202
3020
                                                        end
 
3021
 
2203
3022
                                                        if pre ~= '' then
2204
 
                                                                table.insert(a, ai, {true, pre})
 
3023
                                                                table.insert( a, ai, { true, pre } )
2205
3024
                                                                ai = ai + 1
2206
3025
                                                        end
2207
 
                                                        a[ai] = {false, v[2]}
2208
 
                                                        if post ~= '' then table.insert(a, ai + 1, {true, post}) end
 
3026
 
 
3027
                                                        a[ ai ] = { false, v[ 2 ] }
 
3028
 
 
3029
                                                        if post ~= '' then
 
3030
                                                                table.insert( a, ai + 1, { true, post } )
 
3031
                                                        end
2209
3032
                                                end
2210
3033
                                        end
2211
3034
                                        ai = ai + 1
2215
3038
                        -- concats the argument pieces into a string.
2216
3039
                        local as = ''
2217
3040
                        local first = true
2218
 
                        for _, v in ipairs(a) do
 
3041
 
 
3042
                        for _, v in ipairs( a ) do
 
3043
 
2219
3044
                                if not first then
2220
3045
                                        as = as..' .. '
2221
3046
                                end
2222
 
                                if v[1] then
2223
 
                                        as = as..'"'..v[2]..'"'
 
3047
 
 
3048
                                if v[ 1 ] then
 
3049
                                        as = as .. '"' .. v[ 2 ] .. '"'
2224
3050
                                else
2225
 
                                        as = as..v[2]
 
3051
                                        as = as .. v[ 2 ]
2226
3052
                                end
 
3053
 
2227
3054
                                first = false
2228
3055
                        end
2229
 
                        args[ia] = as
 
3056
 
 
3057
                        args[ ia ] = as
2230
3058
                end
2231
3059
 
2232
3060
                local ft
2235
3063
                else
2236
3064
                        ft = 'function(event, event2)\n'
2237
3065
                end
2238
 
                ft = ft..
2239
 
                        "    log('Normal', 'Event ', event.etype, \n"..
2240
 
                        "        ' spawns action \"".. str.."\"')\n"..
 
3066
 
 
3067
                ft = ft ..
 
3068
                        "    log('Normal', 'Event ', event.etype, \n" ..
 
3069
                        "        ' spawns action \"".. str.."\"')\n" ..
2241
3070
                        "    spawn(event"
2242
 
                for _, v in ipairs(args) do
2243
 
                        ft = ft..',\n         '..v
 
3071
 
 
3072
                for _, v in ipairs( args ) do
 
3073
                        ft = ft .. ',\n         ' .. v
2244
3074
                end
2245
 
                ft = ft..')\nend'
 
3075
 
 
3076
                ft = ft .. ')\nend'
2246
3077
                return ft
 
3078
 
2247
3079
        end
2248
3080
 
2249
 
        -----
 
3081
 
 
3082
        --
2250
3083
        -- Translates a call using a shell to a lua function
2251
3084
        --
2252
 
        local function translateShell(str)
 
3085
        local function translateShell( str )
 
3086
 
2253
3087
                local argn = 1
2254
 
                local args = {}
 
3088
                local args = { }
2255
3089
                local cmd = str
2256
3090
                local lc = str
 
3091
 
2257
3092
                -- true if there is a second event
2258
3093
                local haveEvent2 = false
2259
3094
 
2260
 
                for _, v in ipairs(transVars) do
 
3095
                for _, v in ipairs( transVars ) do
 
3096
 
2261
3097
                        local occur = false
2262
 
                        cmd = string.gsub(cmd, v[1],
2263
 
                                function()
 
3098
 
 
3099
                        cmd = string.gsub(
 
3100
                                cmd,
 
3101
                                v[ 1 ],
 
3102
                                function( )
2264
3103
                                        occur = true
2265
 
                                        return '"$'..argn..'"'
2266
 
                                end)
2267
 
                        lc = string.gsub(lc, v[1], ']]..'..v[2]..'..[[')
 
3104
                                        return '"$' .. argn .. '"'
 
3105
                                end
 
3106
                        )
 
3107
 
 
3108
                        lc = string.gsub(
 
3109
                                lc,
 
3110
                                v[1],
 
3111
                                ']]..' .. v[2] .. '..[['
 
3112
                        )
 
3113
 
2268
3114
                        if occur then
2269
3115
                                argn = argn + 1
2270
 
                                table.insert(args, v[2])
2271
 
                                if v[3] > 1 then
 
3116
                                table.insert( args, v[ 2 ] )
 
3117
 
 
3118
                                if v[ 3 ] > 1 then
2272
3119
                                        haveEvent2 = true
2273
3120
                                end
2274
3121
                        end
 
3122
 
2275
3123
                end
 
3124
 
2276
3125
                local ft
2277
3126
                if not haveEvent2 then
2278
3127
                        ft = 'function(event)\n'
2279
3128
                else
2280
3129
                        ft = 'function(event, event2)\n'
2281
3130
                end
 
3131
 
2282
3132
                -- TODO do array joining instead
2283
3133
                ft = ft..
2284
3134
                        "    log('Normal', 'Event ',event.etype,\n"..
2285
3135
                        "        [[ spawns shell \""..lc.."\"]])\n"..
2286
3136
                        "    spawnShell(event, [["..cmd.."]]"
2287
 
                for _, v in ipairs(args) do
 
3137
 
 
3138
                for _, v in ipairs( args ) do
2288
3139
                        ft = ft..',\n         '..v
2289
3140
                end
2290
 
                ft = ft..')\nend'
 
3141
 
 
3142
                ft = ft .. ')\nend'
 
3143
 
2291
3144
                return ft
 
3145
 
2292
3146
        end
2293
3147
 
2294
 
        -----
2295
 
        -- writes a lua function for a layer 3 user script.
2296
 
        local function translate(str)
 
3148
        --
 
3149
        -- Writes a lua function for a layer 3 user script.
 
3150
        --
 
3151
        local function translate( str )
2297
3152
                -- trim spaces
2298
 
                str = string.match(str, '^%s*(.-)%s*$')
 
3153
                str = string.match( str, '^%s*(.-)%s*$' )
2299
3154
 
2300
3155
                local ft
2301
 
                if string.byte(str, 1, 1) == 47 then
 
3156
                if string.byte( str, 1, 1 ) == 47 then
2302
3157
                        -- starts with /
2303
 
                         ft = translateBinary(str)
2304
 
                elseif string.byte(str, 1, 1) == 94 then
 
3158
                         ft = translateBinary( str )
 
3159
                elseif string.byte( str, 1, 1 ) == 94 then
2305
3160
                        -- starts with ^
2306
 
                         ft = translateShell(str:sub(2, -1))
 
3161
                         ft = translateShell( str:sub( 2, -1 ) )
2307
3162
                else
2308
 
                         ft = translateShell(str)
 
3163
                         ft = translateShell( str )
2309
3164
                end
2310
 
                log('FWrite','translated "',str,'" to \n',ft)
 
3165
 
 
3166
                log(
 
3167
                        'FWrite',
 
3168
                        'translated "',
 
3169
                        str,
 
3170
                        '" to \n',
 
3171
                        ft
 
3172
                )
 
3173
 
2311
3174
                return ft
2312
3175
        end
2313
3176
 
2314
 
        -----
2315
 
        -- public interface
2316
 
        --
2317
 
        return {translate = translate}
2318
 
end)()
2319
 
 
2320
 
 
2321
 
----
 
3177
 
 
3178
        --
 
3179
        -- Public interface.
 
3180
        --
 
3181
        return { translate = translate }
 
3182
 
 
3183
 
 
3184
end )( )
 
3185
 
 
3186
 
 
3187
 
 
3188
--
2322
3189
-- Writes a status report file at most every [statusintervall] seconds.
2323
3190
--
2324
 
--
2325
 
local StatusFile = (function()
2326
 
 
2327
 
        -----
 
3191
local StatusFile = ( function( )
 
3192
 
 
3193
 
 
3194
        --
2328
3195
        -- Timestamp when the status file has been written.
 
3196
        --
2329
3197
        local lastWritten = false
2330
3198
 
2331
 
        -----
2332
 
        -- Timestamp when a status file should be written
 
3199
 
 
3200
        --
 
3201
        -- Timestamp when a status file should be written.
 
3202
        --
2333
3203
        local alarm = false
2334
3204
 
2335
 
        -----
2336
 
        -- Returns when the status file should be written
 
3205
 
 
3206
        --
 
3207
        -- Returns the alarm when the status file should be written-
2337
3208
        --
2338
3209
        local function getAlarm()
2339
3210
                return alarm
2340
3211
        end
2341
3212
 
2342
 
        -----
 
3213
 
 
3214
        --
2343
3215
        -- Called to check if to write a status file.
2344
3216
        --
2345
 
        local function write(timestamp)
2346
 
                log('Function', 'write(', timestamp, ')')
2347
 
 
2348
 
                -- some logic to not write too often
2349
 
                if settings.statusInterval > 0 then
2350
 
                        -- already waiting
 
3217
        local function write( timestamp )
 
3218
 
 
3219
                log(
 
3220
                        'Function',
 
3221
                        'write( ',
 
3222
                                timestamp,
 
3223
                        ' )'
 
3224
                )
 
3225
 
 
3226
                --
 
3227
                -- takes care not write too often
 
3228
                --
 
3229
                if uSettings.statusInterval > 0 then
 
3230
 
 
3231
                        -- already waiting?
2351
3232
                        if alarm and timestamp < alarm then
2352
 
                                log('Statusfile', 'waiting(',timestamp,' < ',alarm,')')
 
3233
                                log(
 
3234
                                        'Statusfile',
 
3235
                                        'waiting(',
 
3236
                                        timestamp,
 
3237
                                        ' < ',
 
3238
                                        alarm,
 
3239
                                        ')'
 
3240
                                )
2353
3241
                                return
2354
3242
                        end
 
3243
 
2355
3244
                        -- determines when a next write will be possible
2356
3245
                        if not alarm then
 
3246
 
2357
3247
                                local nextWrite =
2358
 
                                        lastWritten and timestamp + settings.statusInterval
 
3248
                                        lastWritten and timestamp +
 
3249
                                        uSettings.statusInterval
 
3250
 
2359
3251
                                if nextWrite and timestamp < nextWrite then
2360
 
                                        log('Statusfile', 'setting alarm: ', nextWrite)
 
3252
                                        log(
 
3253
                                                'Statusfile',
 
3254
                                                'setting alarm: ',
 
3255
                                                nextWrite
 
3256
                                        )
2361
3257
                                        alarm = nextWrite
 
3258
 
2362
3259
                                        return
2363
3260
                                end
2364
3261
                        end
 
3262
 
2365
3263
                        lastWritten = timestamp
2366
3264
                        alarm = false
2367
3265
                end
2368
3266
 
2369
 
                log('Statusfile', 'writing now')
2370
 
                local f, err = io.open(settings.statusFile, 'w')
 
3267
                log( 'Statusfile', 'writing now' )
 
3268
 
 
3269
                local f, err = io.open( uSettings.statusFile, 'w' )
 
3270
 
2371
3271
                if not f then
2372
 
                        log('Error', 'Cannot open status file "'..settings.statusFile..  '" :'..err)
 
3272
                        log(
 
3273
                                'Error',
 
3274
                                'Cannot open status file "' ..
 
3275
                                        uSettings.statusFile ..
 
3276
                                        '" :' ..
 
3277
                                        err
 
3278
                        )
2373
3279
                        return
2374
3280
                end
2375
 
                f:write('Lsyncd status report at ',os.date(),'\n\n')
2376
 
                for i, s in Syncs.iwalk() do
2377
 
                        s:statusReport(f)
2378
 
                        f:write('\n')
 
3281
 
 
3282
                f:write( 'Lsyncd status report at ', os.date( ), '\n\n' )
 
3283
 
 
3284
                for i, s in Syncs.iwalk( ) do
 
3285
                        s:statusReport( f )
 
3286
                        f:write( '\n' )
2379
3287
                end
2380
3288
 
2381
 
                Inotify.statusReport(f)
2382
 
                f:close()
 
3289
                Inotify.statusReport( f )
 
3290
                f:close( )
2383
3291
        end
2384
3292
 
2385
 
        -- public interface
2386
 
        return {write = write, getAlarm = getAlarm}
2387
 
end)()
2388
 
 
2389
 
------
2390
 
--
2391
 
local UserAlarms = (function()
2392
 
        local alarms = {}
2393
 
 
2394
 
        -----
 
3293
 
 
3294
        --
 
3295
        -- Public interface
 
3296
        --
 
3297
        return {
 
3298
                write = write,
 
3299
                getAlarm = getAlarm
 
3300
        }
 
3301
 
 
3302
end )( )
 
3303
 
 
3304
 
 
3305
--
 
3306
-- Lets userscripts make their own alarms.
 
3307
--
 
3308
local UserAlarms = ( function( )
 
3309
 
 
3310
        local alarms = { }
 
3311
 
 
3312
 
 
3313
        --
2395
3314
        -- Calls the user function at timestamp.
2396
3315
        --
2397
 
        local function alarm(timestamp, func, extra)
 
3316
        local function alarm( timestamp, func, extra )
 
3317
 
2398
3318
                local idx
2399
 
                for k, v in ipairs(alarms) do
 
3319
                for k, v in ipairs( alarms ) do
2400
3320
                        if timestamp < v.timestamp then
2401
3321
                                idx = k
2402
3322
                                break
2403
3323
                        end
2404
3324
                end
2405
 
                local a = {timestamp = timestamp,
2406
 
                           func = func,
2407
 
                           extra = extra}
 
3325
 
 
3326
                local a = {
 
3327
                        timestamp = timestamp,
 
3328
                        func = func,
 
3329
                        extra = extra
 
3330
                }
 
3331
 
2408
3332
                if idx then
2409
 
                        table.insert(alarms, idx, a)
 
3333
                        table.insert( alarms, idx, a )
2410
3334
                else
2411
 
                        table.insert(alarms, a)
 
3335
                        table.insert( alarms, a )
2412
3336
                end
 
3337
 
2413
3338
        end
2414
3339
 
2415
 
        ----
2416
 
        -- Retrieves the nearest alarm.
2417
 
        --
2418
 
        local function getAlarm()
 
3340
 
 
3341
        --
 
3342
        -- Retrieves the soonest alarm.
 
3343
        --
 
3344
        local function getAlarm( )
 
3345
 
2419
3346
                if #alarms == 0 then
2420
3347
                        return false
2421
3348
                else
2422
3349
                        return alarms[1].timestamp
2423
3350
                end
 
3351
 
2424
3352
        end
2425
3353
 
2426
 
        -----
 
3354
 
 
3355
        --
2427
3356
        -- Calls user alarms.
2428
3357
        --
2429
 
        local function invoke(timestamp)
2430
 
                while #alarms > 0 and alarms[1].timestamp <= timestamp do
2431
 
                        alarms[1].func(alarms[1].timestamp, alarms[1].extra)
2432
 
                        table.remove(alarms, 1)
 
3358
        local function invoke( timestamp )
 
3359
                while
 
3360
                        #alarms > 0 and
 
3361
                        alarms[ 1 ].timestamp <= timestamp
 
3362
                do
 
3363
                        alarms[ 1 ].func( alarms[ 1 ].timestamp, alarms[ 1 ].extra )
 
3364
                        table.remove( alarms, 1 )
2433
3365
                end
2434
3366
        end
2435
3367
 
2436
 
        -- public interface
2437
 
        return { alarm    = alarm,
2438
 
                         getAlarm = getAlarm,
2439
 
                         invoke   = invoke }
2440
 
end)()
2441
 
 
2442
 
--============================================================================
2443
 
--============================================================================
2444
 
 
2445
 
-----
 
3368
 
 
3369
        --
 
3370
        -- Public interface
 
3371
        --
 
3372
        return {
 
3373
                alarm    = alarm,
 
3374
                getAlarm = getAlarm,
 
3375
                invoke   = invoke
 
3376
        }
 
3377
 
 
3378
 
 
3379
end )( )
 
3380
 
 
3381
--============================================================================
 
3382
-- Lsyncd runner's plugs. These functions are called from core.
 
3383
--============================================================================
 
3384
 
 
3385
--
 
3386
-- Current status of Lsyncd.
2446
3387
--
2447
3388
-- 'init'  ... on (re)init
2448
3389
-- 'run'   ... normal operation
2453
3391
--
2454
3392
local lsyncdStatus = 'init'
2455
3393
 
2456
 
----
2457
 
--
2458
 
local runner = {}
2459
 
 
2460
 
-----
 
3394
--
 
3395
-- The cores interface to the runner.
 
3396
--
 
3397
local runner = { }
 
3398
 
 
3399
--
 
3400
-- Last time said to be waiting for more child processes
 
3401
--
 
3402
local lastReportedWaiting = false
 
3403
 
 
3404
--
 
3405
-- Called from core whenever Lua code failed.
 
3406
--
2461
3407
-- Logs a backtrace
2462
3408
--
2463
 
function runner.callError(message)
2464
 
        log('Error', 'IN LUA: ', message)
 
3409
function runner.callError( message )
 
3410
        log('Error', 'in Lua: ', message )
 
3411
 
2465
3412
        -- prints backtrace
2466
3413
        local level = 2
2467
3414
        while true do
2468
 
                local info = debug.getinfo(level, 'Sl')
 
3415
 
 
3416
                local info = debug.getinfo( level, 'Sl' )
 
3417
 
2469
3418
                if not info then
2470
 
                        terminate(-1) -- ERRNO
 
3419
                        terminate( -1 )
2471
3420
                end
2472
 
                log('Error', 'Backtrace ',level - 1,' :',info.short_src,':',info.currentline)
 
3421
 
 
3422
                log(
 
3423
                        'Error',
 
3424
                        'Backtrace ',
 
3425
                        level - 1, ' :',
 
3426
                        info.short_src, ':',
 
3427
                        info.currentline
 
3428
                )
 
3429
 
2473
3430
                level = level + 1
2474
3431
        end
2475
3432
end
2476
3433
 
2477
 
-----
2478
 
--
2479
 
function runner.collectProcess(pid, exitcode)
 
3434
 
 
3435
--
 
3436
-- Called from core whenever a child process has finished and
 
3437
-- the zombie process was collected by core.
 
3438
--
 
3439
function runner.collectProcess( pid, exitcode )
 
3440
 
2480
3441
        processCount = processCount - 1
2481
 
        if processCount < 0 then error('negative number of processes!') end
 
3442
 
 
3443
        if processCount < 0 then
 
3444
                error( 'negative number of processes!' )
 
3445
        end
2482
3446
 
2483
3447
        for _, s in Syncs.iwalk() do
2484
3448
                if s:collect(pid, exitcode) then return end
2485
3449
        end
 
3450
 
2486
3451
end
2487
3452
 
2488
 
-----
 
3453
--
2489
3454
-- Called from core everytime a masterloop cycle runs through.
 
3455
--
2490
3456
-- This happens in case of
2491
3457
--   * an expired alarm.
2492
3458
--   * a returned child process.
2493
3459
--   * received filesystem events.
2494
3460
--   * received a HUP or TERM signal.
2495
3461
--
2496
 
--
2497
 
function runner.cycle(timestamp)
2498
 
        -- goes through all syncs and spawns more actions
2499
 
        -- if possible
 
3462
function runner.cycle(
 
3463
        timestamp   -- the current kernel time (in jiffies)
 
3464
)
 
3465
 
2500
3466
        if lsyncdStatus == 'fade' then
 
3467
 
2501
3468
                if processCount > 0 then
2502
 
                        log('Normal', 'waiting for ',processCount,' more child processes.')
 
3469
 
 
3470
                        if
 
3471
                                lastReportedWaiting == false or
 
3472
                                timestamp >= lastReportedWaiting + 60
 
3473
                        then
 
3474
                                lastReportedWaiting = timestamp
 
3475
 
 
3476
                                log(
 
3477
                                        'Normal',
 
3478
                                        'waiting for ',
 
3479
                                        processCount,
 
3480
                                        ' more child processes.'
 
3481
                                )
 
3482
                        end
 
3483
 
2503
3484
                        return true
2504
3485
                else
 
3486
 
2505
3487
                        return false
2506
3488
                end
2507
3489
        end
 
3490
 
2508
3491
        if lsyncdStatus ~= 'run' then
2509
 
                error('runner.cycle() called while not running!')
 
3492
                error( 'runner.cycle() called while not running!' )
2510
3493
        end
2511
3494
 
2512
 
        --- only let Syncs invoke actions if not on global limit
2513
 
        if not settings.maxProcesses or processCount < settings.maxProcesses then
2514
 
                local start = Syncs.getRound()
 
3495
        --
 
3496
        -- goes through all syncs and spawns more actions
 
3497
        -- if possibly. But only let Syncs invoke actions if
 
3498
        -- not at global limit
 
3499
        --
 
3500
        if
 
3501
                not uSettings.maxProcesses or
 
3502
                processCount < uSettings.maxProcesses
 
3503
        then
 
3504
                local start = Syncs.getRound( )
 
3505
 
2515
3506
                local ir = start
 
3507
 
2516
3508
                repeat
2517
 
                        local s = Syncs.get(ir)
2518
 
                        s:invokeActions(timestamp)
 
3509
 
 
3510
                        local s = Syncs.get( ir )
 
3511
                        s:invokeActions( timestamp )
2519
3512
                        ir = ir + 1
2520
 
                        if ir > Syncs.size() then
 
3513
 
 
3514
                        if ir > Syncs.size( ) then
 
3515
 
2521
3516
                                ir = 1
2522
3517
                        end
2523
3518
                until ir == start
2524
 
                Syncs.nextRound()
 
3519
 
 
3520
                Syncs.nextRound( )
2525
3521
        end
2526
3522
 
2527
 
        UserAlarms.invoke(timestamp)
 
3523
        UserAlarms.invoke( timestamp )
2528
3524
 
2529
 
        if settings.statusFile then
2530
 
                StatusFile.write(timestamp)
 
3525
        if uSettings.statusFile then
 
3526
                StatusFile.write( timestamp )
2531
3527
        end
2532
3528
 
2533
3529
        return true
2534
3530
end
2535
3531
 
2536
 
-----
 
3532
--
 
3533
-- Called by core if '-help' or '--help' is in
2537
3534
-- the arguments.
2538
3535
--
2539
 
function runner.help()
 
3536
function runner.help( )
2540
3537
        io.stdout:write(
2541
3538
[[
2542
3539
 
2584
3575
--  -monitor NAME       Uses operating systems event montior NAME
2585
3576
--                      (inotify/fanotify/fsevents)
2586
3577
 
2587
 
        os.exit(-1) -- ERRNO
 
3578
        os.exit( -1 )
2588
3579
end
2589
3580
 
2590
3581
 
2591
 
-----
2592
3582
--
2593
 
local clSettings = {}
2594
 
 
2595
 
-----
2596
3583
-- Called from core to parse the command line arguments
2597
 
--
2598
 
function runner.configure(args, monitors)
2599
 
        Monitors.initialize(monitors)
2600
 
 
2601
 
        -- a list of all valid --options
2602
 
        -- first paramter is number of options
2603
 
        --       if < 0 the function checks existance
2604
 
        -- second paramter is function to call when in args
 
3584
--
 
3585
-- returns a string as user script to load.
 
3586
--    or simply 'true' if running with rsync bevaiour
 
3587
--
 
3588
-- terminates on invalid arguments.
 
3589
--
 
3590
function runner.configure( args, monitors )
 
3591
 
 
3592
        Monitors.initialize( monitors )
 
3593
 
 
3594
        --
 
3595
        -- a list of all valid options
 
3596
        --
 
3597
        -- first paramter is the number of parameters an option takes
 
3598
        -- if < 0 the called function has to check the presence of
 
3599
        -- optional arguments.
 
3600
        --
 
3601
        -- second paramter is the function to call
2605
3602
        --
2606
3603
        local options = {
 
3604
 
2607
3605
                -- log is handled by core already.
2608
 
                delay    =
2609
 
                        {1, function(secs)
2610
 
                                clSettings.delay = secs
2611
 
                        end},
2612
 
                insist   =
2613
 
                        {0, function()
2614
 
                                clSettings.insist = true
2615
 
                        end},
2616
 
                log      =
2617
 
                        {1, nil},
2618
 
                logfile  =
2619
 
                        {1, function(file)
2620
 
                                clSettings.logfile = file
2621
 
                        end},
 
3606
 
 
3607
                delay =
 
3608
                        {
 
3609
                                1,
 
3610
                                function( secs )
 
3611
                                        clSettings.delay = secs + 0
 
3612
                                end
 
3613
                        },
 
3614
 
 
3615
                insist =
 
3616
                        {
 
3617
                                0,
 
3618
                                function( )
 
3619
                                        clSettings.insist = true
 
3620
                                end
 
3621
                        },
 
3622
 
 
3623
                log =
 
3624
                        {
 
3625
                                1,
 
3626
                                nil
 
3627
                        },
 
3628
 
 
3629
                logfile =
 
3630
                        {
 
3631
                                1,
 
3632
                                function( file )
 
3633
                                        clSettings.logfile = file
 
3634
                                end
 
3635
                        },
 
3636
 
2622
3637
                monitor =
2623
 
                        {-1, function(monitor)
2624
 
                                if not monitor then
2625
 
                                        io.stdout:write('This Lsyncd supports these monitors:\n')
2626
 
                                        for _, v in ipairs(Monitors.list) do
2627
 
                                                io.stdout:write('   ',v,'\n')
 
3638
                        {
 
3639
                                -1,
 
3640
                                function( monitor )
 
3641
                                        if not monitor then
 
3642
                                                io.stdout:write( 'This Lsyncd supports these monitors:\n' )
 
3643
                                                for _, v in ipairs(Monitors.list) do
 
3644
                                                        io.stdout:write('   ',v,'\n')
 
3645
                                                end
 
3646
 
 
3647
                                                io.stdout:write('\n')
 
3648
 
 
3649
                                                lsyncd.terminate(-1)
 
3650
                                        else
 
3651
                                                clSettings.monitor = monitor
2628
3652
                                        end
2629
 
                                        io.stdout:write('\n');
2630
 
                                        lsyncd.terminate(-1); -- ERRNO
2631
 
                                else
2632
 
                                        clSettings.monitor=monitor
2633
3653
                                end
2634
 
                        end},
 
3654
                        },
 
3655
 
2635
3656
                nodaemon =
2636
 
                        {0, function()
2637
 
                                clSettings.nodaemon = true
2638
 
                        end},
2639
 
                pidfile   =
2640
 
                        {1, function(file)
2641
 
                                clSettings.pidfile=file
2642
 
                        end},
 
3657
                        {
 
3658
                                0,
 
3659
                                function( )
 
3660
                                        clSettings.nodaemon = true
 
3661
                                end
 
3662
                        },
 
3663
 
 
3664
                pidfile =
 
3665
                        {
 
3666
                                1,
 
3667
                                function( file )
 
3668
                                        clSettings.pidfile=file
 
3669
                                end
 
3670
                        },
 
3671
 
2643
3672
                rsync    =
2644
 
                        {2, function(src, trg)
2645
 
                                clSettings.syncs = clSettings.syncs or {}
2646
 
                                table.insert(clSettings.syncs, {'rsync', src, trg})
2647
 
                        end},
 
3673
                        {
 
3674
                                2,
 
3675
                                function( src, trg )
 
3676
                                        clSettings.syncs = clSettings.syncs or { }
 
3677
                                        table.insert(
 
3678
                                                clSettings.syncs,
 
3679
                                                { 'rsync', src, trg }
 
3680
                                        )
 
3681
                                end
 
3682
                        },
 
3683
 
2648
3684
                rsyncssh =
2649
 
                        {3, function(src, host, tdir)
2650
 
                                clSettings.syncs = clSettings.syncs or {}
2651
 
                                table.insert(clSettings.syncs, {'rsyncssh', src, host, tdir})
2652
 
                        end},
 
3685
                        {
 
3686
                                3,
 
3687
                                function( src, host, tdir )
 
3688
                                        clSettings.syncs = clSettings.syncs or { }
 
3689
                                        table.insert(
 
3690
                                                clSettings.syncs,
 
3691
                                                { 'rsyncssh', src, host, tdir }
 
3692
                                        )
 
3693
                                end
 
3694
                        },
 
3695
 
2653
3696
                direct =
2654
 
                        {2, function(src, trg)
2655
 
                                clSettings.syncs = clSettings.syncs or {}
2656
 
                                table.insert(clSettings.syncs, {'direct', src, trg})
2657
 
                        end},
2658
 
                version  =
2659
 
                        {0, function()
2660
 
                                io.stdout:write('Version: ',lsyncd_version,'\n')
2661
 
                                os.exit(0)
2662
 
                        end}
 
3697
                        {
 
3698
                                2,
 
3699
                                function( src, trg )
 
3700
                                        clSettings.syncs = clSettings.syncs or { }
 
3701
                                        table.insert(
 
3702
                                                clSettings.syncs,
 
3703
                                                { 'direct', src, trg }
 
3704
                                        )
 
3705
                                end
 
3706
                        },
 
3707
 
 
3708
                version =
 
3709
                        {
 
3710
                                0,
 
3711
                                function( )
 
3712
                                        io.stdout:write( 'Version: ', lsyncd_version, '\n' )
 
3713
                                        os.exit( 0 )
 
3714
                                end
 
3715
                        }
2663
3716
        }
2664
 
        -- nonopts is filled with all args that were no part dash options
2665
 
        local nonopts = {}
 
3717
 
 
3718
        -- non-opts is filled with all args that were no part dash options
 
3719
 
 
3720
        local nonopts = { }
 
3721
 
2666
3722
        local i = 1
2667
3723
        while i <= #args do
2668
 
                local a = args[i]
2669
 
                if a:sub(1, 1) ~= '-' then
2670
 
                        table.insert(nonopts, args[i])
 
3724
 
 
3725
                local a = args[ i ]
 
3726
 
 
3727
                if a:sub( 1, 1 ) ~= '-' then
 
3728
                        table.insert( nonopts, args[ i ] )
2671
3729
                else
2672
 
                        if a:sub(1, 2) == '--' then
2673
 
                                a = a:sub(3)
 
3730
                        if a:sub( 1, 2 ) == '--' then
 
3731
                                a = a:sub( 3 )
2674
3732
                        else
2675
 
                                a = a:sub(2)
 
3733
                                a = a:sub( 2 )
2676
3734
                        end
2677
 
                        local o = options[a]
 
3735
 
 
3736
                        local o = options[ a ]
 
3737
 
2678
3738
                        if not o then
2679
 
                                log('Error','unknown option command line option ', args[i])
2680
 
                                os.exit(-1) -- ERRNO
 
3739
                                log(
 
3740
                                        'Error',
 
3741
                                        'unknown option command line option ',
 
3742
                                        args[i]
 
3743
                                )
 
3744
                                os.exit( -1 )
2681
3745
                        end
2682
 
                        if o[1] >= 0 and i + o[1] > #args then
2683
 
                                log('Error',a,' needs ',o[1],' arguments')
2684
 
                                os.exit(-1) -- ERRNO
 
3746
 
 
3747
                        if o[ 1 ] >= 0 and i + o[ 1 ] > #args then
 
3748
                                log( 'Error', a ,' needs ', o[ 1 ],' arguments' )
 
3749
                                os.exit( -1 )
2685
3750
                        elseif o[1] < 0 then
2686
 
                                o[1] = -o[1]
 
3751
                                o[ 1 ] = -o[ 1 ]
2687
3752
                        end
2688
 
                        if o[2] then
2689
 
                                if o[1] == 0 then
2690
 
                                        o[2]()
2691
 
                                elseif o[1] == 1 then
2692
 
                                        o[2](args[i + 1])
2693
 
                                elseif o[1] == 2 then
2694
 
                                        o[2](args[i + 1], args[i + 2])
2695
 
                                elseif o[1] == 3 then
2696
 
                                        o[2](args[i + 1], args[i + 2], args[i + 3])
 
3753
 
 
3754
                        if o[ 2 ] then
 
3755
                                if o[ 1 ] == 0 then
 
3756
                                        o[ 2 ]( )
 
3757
                                elseif o[ 1 ] == 1 then
 
3758
                                        o[ 2 ]( args[i + 1] )
 
3759
                                elseif o[ 1 ] == 2 then
 
3760
                                        o[ 2 ]( args[i + 1], args[i + 2] )
 
3761
                                elseif o[ 1 ] == 3 then
 
3762
                                        o[ 2 ]( args[i + 1], args[i + 2], args[i + 3] )
2697
3763
                                end
2698
3764
                        end
2699
3765
                        i = i + o[1]
2700
3766
                end
2701
3767
                i = i + 1
 
3768
 
2702
3769
        end
2703
3770
 
2704
3771
        if clSettings.syncs then
 
3772
 
2705
3773
                if #nonopts ~= 0 then
2706
 
                        log('Error', 'There cannot be command line default syncs with a config file.')
2707
 
                        os.exit(-1) -- ERRNO
 
3774
                        log(
 
3775
                                'Error',
 
3776
                                'There cannot be command line syncs and config file together.'
 
3777
                        )
 
3778
                        os.exit( -1 )
2708
3779
                end
 
3780
 
2709
3781
        else
 
3782
 
2710
3783
                if #nonopts == 0 then
2711
 
                        runner.help(args[0])
 
3784
 
 
3785
                        runner.help( args[ 0 ] )
 
3786
 
2712
3787
                elseif #nonopts == 1 then
2713
 
                        return nonopts[1]
 
3788
 
 
3789
                        return nonopts[ 1 ]
 
3790
 
2714
3791
                else
2715
 
                        log('Error', 'There can only be one config file in command line.')
2716
 
                        os.exit(-1) -- ERRNO
 
3792
 
 
3793
                        -- TODO make this possible
 
3794
                        log(
 
3795
                                'Error',
 
3796
                                'There can only be one config file in command line.'
 
3797
                        )
 
3798
                        os.exit( -1 )
 
3799
 
2717
3800
                end
 
3801
 
2718
3802
        end
2719
3803
end
2720
3804
 
2721
3805
 
2722
 
----
 
3806
--
2723
3807
-- Called from core on init or restart after user configuration.
2724
3808
--
 
3809
-- firstTime:
 
3810
--    true when Lsyncd startups the first time,
 
3811
--    false on resets, due to HUP signal or monitor queue overflow.
2725
3812
--
2726
 
function runner.initialize(firstTime)
2727
 
        -- creates settings if user didnt
2728
 
        settings = settings or {}
2729
 
 
 
3813
function runner.initialize( firstTime )
 
3814
 
 
3815
        if settings ~= settingsSafe then
 
3816
                log(
 
3817
                        'Warn',
 
3818
                        'settings = { ... } is deprecated.\n'..
 
3819
                        '      please use settings{ ... } (without the equal sign)'
 
3820
                )
 
3821
 
 
3822
                for k, v in pairs( settings ) do
 
3823
                        uSettings[ k ] = v
 
3824
                end
 
3825
 
 
3826
        end
 
3827
 
 
3828
        lastReportedWaiting = false
 
3829
 
 
3830
        --
2730
3831
        -- From this point on, no globals may be created anymore
2731
 
        lockGlobals()
2732
 
 
2733
 
        -- copies simple settings with numeric keys to 'key=true' settings.
2734
 
        for k, v in ipairs(settings) do
2735
 
                if settings[v] then
2736
 
                        log('Error', 'Double setting "'..v..'"')
2737
 
                        os.exit(-1) -- ERRNO
 
3832
        --
 
3833
        lockGlobals( )
 
3834
 
 
3835
        --
 
3836
        -- copies simple settings with numeric keys to 'key = true' settings.
 
3837
        --
 
3838
        -- FIXME this can be removed when
 
3839
        -- Lsyncd 2.0.x backwards compatibility is dropped
 
3840
        --
 
3841
        for k, v in ipairs( uSettings ) do
 
3842
 
 
3843
                if uSettings[ v ] then
 
3844
                        log(
 
3845
                                'Error',
 
3846
                                'Double setting "' .. v.. '"'
 
3847
                        )
 
3848
                        os.exit( -1 )
2738
3849
                end
2739
 
                settings[v]=true
 
3850
 
 
3851
                uSettings[ v ]= true
 
3852
 
2740
3853
        end
2741
3854
 
 
3855
        --
2742
3856
        -- all command line settings overwrite config file settings
2743
 
        for k, v in pairs(clSettings) do
 
3857
        --
 
3858
        for k, v in pairs( clSettings ) do
2744
3859
                if k ~= 'syncs' then
2745
 
                        settings[k]=v
 
3860
                        uSettings[ k ] = v
2746
3861
                end
2747
3862
        end
2748
3863
 
2749
 
        -- implicitly force insist to be true on Lsyncd resets.
 
3864
        --
 
3865
        -- implicitly forces 'insist' on Lsyncd resets.
 
3866
        --
2750
3867
        if not firstTime then
2751
 
                settings.insist = true
 
3868
                uSettings.insist = true
2752
3869
        end
2753
3870
 
 
3871
        --
2754
3872
        -- adds syncs specified by command line.
 
3873
        --
2755
3874
        if clSettings.syncs then
2756
 
                for _, s in ipairs(clSettings.syncs) do
2757
 
                        if s[1] == 'rsync' then
2758
 
                                sync{default.rsync, source=s[2], target=s[3]}
2759
 
                        elseif s[1] == 'rsyncssh' then
2760
 
                                sync{default.rsyncssh, source=s[2], host=s[3], targetdir=s[4]}
2761
 
                        elseif s[1] == 'direct' then
2762
 
                                sync{default.direct, source=s[2], target=s[3]}
 
3875
 
 
3876
                for _, s in ipairs( clSettings.syncs ) do
 
3877
 
 
3878
                        if s[ 1 ] == 'rsync' then
 
3879
 
 
3880
                                sync{
 
3881
                                        default.rsync,
 
3882
                                        source = s[ 2 ],
 
3883
                                        target = s[ 3 ]
 
3884
                                }
 
3885
 
 
3886
                        elseif s[ 1 ] == 'rsyncssh' then
 
3887
 
 
3888
                                sync{
 
3889
                                        default.rsyncssh,
 
3890
                                        source = s[ 2 ],
 
3891
                                        host   = s[ 3 ],
 
3892
                                        targetdir=s[ 4 ]
 
3893
                                }
 
3894
 
 
3895
                        elseif s[ 1 ] == 'direct' then
 
3896
                                sync{
 
3897
                                        default.direct,
 
3898
                                        source=s[ 2 ],
 
3899
                                        target=s[ 3 ]
 
3900
                                }
 
3901
 
2763
3902
                        end
 
3903
 
2764
3904
                end
2765
 
        end
2766
 
 
2767
 
        if settings.nodaemon then
2768
 
                lsyncd.configure('nodaemon')
2769
 
        end
2770
 
        if settings.logfile then
2771
 
                lsyncd.configure('logfile', settings.logfile)
2772
 
        end
2773
 
        if settings.logident then
2774
 
                lsyncd.configure('logident', settings.logident)
2775
 
        end
2776
 
        if settings.logfacility then
2777
 
                lsyncd.configure('logfacility', settings.logfacility)
2778
 
        end
2779
 
        if settings.pidfile then
2780
 
                lsyncd.configure('pidfile', settings.pidfile)
2781
 
        end
2782
 
 
2783
 
        -----
2784
 
        -- transfers some defaults to settings
2785
 
        if settings.statusInterval == nil then
2786
 
                settings.statusInterval = default.statusInterval
 
3905
 
 
3906
        end
 
3907
 
 
3908
        if uSettings.nodaemon then
 
3909
                lsyncd.configure( 'nodaemon' )
 
3910
        end
 
3911
 
 
3912
        if uSettings.logfile then
 
3913
                lsyncd.configure( 'logfile', uSettings.logfile )
 
3914
        end
 
3915
 
 
3916
        if uSettings.logident then
 
3917
                lsyncd.configure( 'logident', uSettings.logident )
 
3918
        end
 
3919
 
 
3920
        if uSettings.logfacility then
 
3921
                lsyncd.configure( 'logfacility', uSettings.logfacility )
 
3922
        end
 
3923
 
 
3924
        if uSettings.pidfile then
 
3925
                lsyncd.configure( 'pidfile', uSettings.pidfile )
 
3926
        end
 
3927
 
 
3928
        --
 
3929
        -- Transfers some defaults to uSettings
 
3930
        --
 
3931
        if uSettings.statusInterval == nil then
 
3932
                uSettings.statusInterval = default.statusInterval
2787
3933
        end
2788
3934
 
2789
3935
        -- makes sure the user gave Lsyncd anything to do
2790
3936
        if Syncs.size() == 0 then
2791
 
                log('Error', 'Nothing to watch!')
2792
 
                os.exit(-1) -- ERRNO
 
3937
 
 
3938
                log(
 
3939
                        'Error',
 
3940
                        'Nothing to watch!'
 
3941
                )
 
3942
 
 
3943
                os.exit( -1 )
2793
3944
        end
2794
3945
 
2795
3946
        -- from now on use logging as configured instead of stdout/err.
2796
3947
        lsyncdStatus = 'run';
2797
 
        lsyncd.configure('running');
 
3948
 
 
3949
        lsyncd.configure( 'running' );
2798
3950
 
2799
3951
        local ufuncs = {
2800
 
                'onAttrib', 'onCreate', 'onDelete',
2801
 
                'onModify', 'onMove',   'onStartup',
 
3952
                'onAttrib',
 
3953
                'onCreate',
 
3954
                'onDelete',
 
3955
                'onModify',
 
3956
                'onMove',
 
3957
                'onStartup',
2802
3958
        }
2803
3959
 
2804
3960
        -- translates layer 3 scripts
2805
3961
        for _, s in Syncs.iwalk() do
 
3962
 
2806
3963
                -- checks if any user functions is a layer 3 string.
2807
3964
                local config = s.config
 
3965
 
2808
3966
                for _, fn in ipairs(ufuncs) do
 
3967
 
2809
3968
                        if type(config[fn]) == 'string' then
 
3969
 
2810
3970
                                local ft = functionWriter.translate(config[fn])
2811
3971
                                config[fn] = assert(loadstring('return '..ft))()
 
3972
 
2812
3973
                        end
 
3974
 
2813
3975
                end
2814
3976
        end
2815
3977
 
2816
3978
        -- runs through the Syncs created by users
2817
 
        for _, s in Syncs.iwalk() do
 
3979
        for _, s in Syncs.iwalk( ) do
 
3980
 
2818
3981
                if s.config.monitor == 'inotify' then
2819
 
                        Inotify.addSync(s, s.source)
 
3982
 
 
3983
                        Inotify.addSync( s, s.source )
 
3984
 
2820
3985
                elseif s.config.monitor == 'fsevents' then
2821
 
                        Fsevents.addSync(s, s.source)
 
3986
 
 
3987
                        Fsevents.addSync( s, s.source )
 
3988
 
2822
3989
                else
2823
 
                        error('sync '..s.config.name..' has no known event monitor interface.')
 
3990
 
 
3991
                        error(
 
3992
                                'sync ' ..
 
3993
                                s.config.name ..
 
3994
                                ' has no known event monitor interface.'
 
3995
                        )
 
3996
 
2824
3997
                end
2825
 
                -- if the sync has an init function, stacks an init delay
2826
 
                -- that will cause the init function to be called.
 
3998
 
 
3999
                -- if the sync has an init function, the init delay
 
4000
                -- is stacked which causes the init function to be called.
2827
4001
                if s.config.init then
2828
 
                        s:addInitDelay()
 
4002
 
 
4003
                        s:addInitDelay( )
 
4004
 
2829
4005
                end
2830
4006
        end
 
4007
 
2831
4008
end
2832
4009
 
2833
 
----
 
4010
--
 
4011
-- Called by core to query the soonest alarm.
2834
4012
--
2835
4013
-- @return false ... no alarm, core can in untimed sleep, or
2836
4014
--         true  ... immediate action
2837
4015
--         times ... the alarm time (only read if number is 1)
2838
4016
--
2839
 
function runner.getAlarm()
 
4017
function runner.getAlarm( )
 
4018
 
2840
4019
        if lsyncdStatus ~= 'run' then
2841
4020
                return false
2842
4021
        end
 
4022
 
2843
4023
        local alarm = false
2844
 
        ----
2845
 
        -- checks if current nearest alarm or a is earlier
2846
 
        --
2847
 
        local function checkAlarm(a)
 
4024
 
 
4025
        --
 
4026
        -- Checks if 'a' is sooner than the 'alarm' up-value.
 
4027
        --
 
4028
        local function checkAlarm( a )
 
4029
 
2848
4030
                if a == nil then
2849
4031
                        error('got nil alarm')
2850
4032
                end
 
4033
 
2851
4034
                if alarm == true or not a then
2852
 
                        -- already immediate or no new alarm
 
4035
                        -- 'alarm' is already immediate or
 
4036
                        -- a not a new alarm
2853
4037
                        return
2854
4038
                end
2855
 
                -- returns the ealier time
 
4039
 
 
4040
                -- sets 'alarm' to a if a is sooner
2856
4041
                if not alarm or a < alarm then
2857
4042
                        alarm = a
2858
4043
                end
 
4044
 
2859
4045
        end
2860
4046
 
2861
 
        -- checks all syncs for their earliest alarm
 
4047
        --
 
4048
        -- checks all syncs for their earliest alarm,
2862
4049
        -- but only if the global process limit is not yet reached.
2863
 
        if not settings.maxProcesses or processCount < settings.maxProcesses then
2864
 
                for _, s in Syncs.iwalk() do
2865
 
                        checkAlarm(s:getAlarm())
 
4050
        --
 
4051
        if
 
4052
                not uSettings.maxProcesses or
 
4053
                processCount < uSettings.maxProcesses
 
4054
        then
 
4055
                for _, s in Syncs.iwalk( ) do
 
4056
                        checkAlarm( s:getAlarm ( ))
2866
4057
                end
2867
4058
        else
2868
 
                log('Alarm', 'at global process limit.')
 
4059
                log(
 
4060
                        'Alarm',
 
4061
                        'at global process limit.'
 
4062
                )
2869
4063
        end
2870
4064
 
2871
4065
        -- checks if a statusfile write has been delayed
2872
 
        checkAlarm(StatusFile.getAlarm())
 
4066
        checkAlarm( StatusFile.getAlarm( ) )
 
4067
 
2873
4068
        -- checks for an userAlarm
2874
 
        checkAlarm(UserAlarms.getAlarm())
2875
 
 
2876
 
        log('Alarm', 'runner.getAlarm returns: ',alarm)
 
4069
        checkAlarm( UserAlarms.getAlarm( ) )
 
4070
 
 
4071
        log(
 
4072
                'Alarm',
 
4073
                'runner.getAlarm returns: ',
 
4074
                alarm
 
4075
        )
 
4076
 
2877
4077
        return alarm
 
4078
 
2878
4079
end
2879
4080
 
2880
4081
 
2881
 
-----
 
4082
--
 
4083
-- Called when an file system monitor events arrive
2882
4084
--
2883
4085
runner.inotifyEvent = Inotify.event
2884
4086
runner.fsEventsEvent = Fsevents.event
2885
4087
 
2886
 
-----
 
4088
--
2887
4089
-- Collector for every child process that finished in startup phase
2888
4090
--
2889
 
--
2890
 
--
2891
 
function runner.collector(pid, exitcode)
 
4091
function runner.collector(
 
4092
        pid,       -- pid of the child process
 
4093
        exitcode   -- exitcode of the child process
 
4094
)
2892
4095
        if exitcode ~= 0 then
2893
4096
                log('Error', 'Startup process',pid,' failed')
2894
 
                terminate(-1) -- ERRNO
 
4097
                terminate( -1 )
2895
4098
        end
 
4099
 
2896
4100
        return 0
2897
4101
end
2898
4102
 
2899
 
-----
 
4103
--
2900
4104
-- Called by core when an overflow happened.
2901
4105
--
2902
 
function runner.overflow()
2903
 
        log('Normal', '--- OVERFLOW in event queue ---')
 
4106
function runner.overflow( )
 
4107
 
 
4108
        log(
 
4109
                'Normal',
 
4110
                '--- OVERFLOW in event queue ---'
 
4111
        )
 
4112
 
2904
4113
        lsyncdStatus = 'fade'
 
4114
 
2905
4115
end
2906
4116
 
2907
 
-----
 
4117
--
2908
4118
-- Called by core on a hup signal.
2909
4119
--
2910
 
function runner.hup()
2911
 
        log('Normal', '--- HUP signal, resetting ---')
 
4120
function runner.hup( )
 
4121
 
 
4122
        log(
 
4123
                'Normal',
 
4124
                '--- HUP signal, resetting ---'
 
4125
        )
 
4126
 
2912
4127
        lsyncdStatus = 'fade'
 
4128
 
2913
4129
end
2914
4130
 
2915
 
-----
 
4131
--
2916
4132
-- Called by core on a term signal.
2917
4133
--
2918
 
function runner.term()
2919
 
        log('Normal', '--- TERM signal, fading ---')
 
4134
function runner.term( )
 
4135
 
 
4136
        log(
 
4137
                'Normal',
 
4138
                '--- TERM signal, fading ---'
 
4139
        )
 
4140
 
2920
4141
        lsyncdStatus = 'fade'
 
4142
 
2921
4143
end
2922
4144
 
2923
4145
--============================================================================
 
4146
-- Lsyncd runner's user interface
2924
4147
--============================================================================
2925
4148
 
2926
 
-----
 
4149
--
2927
4150
-- Main utility to create new observations.
2928
 
--
2929
 
function sync(opts)
 
4151
--
 
4152
-- Returns an Inlet to that sync.
 
4153
--
 
4154
function sync( opts )
 
4155
 
2930
4156
        if lsyncdStatus ~= 'init' then
2931
 
                error('Sync can only be created during initialization.', 2)
 
4157
                error(
 
4158
                        'Sync can only be created during initialization.',
 
4159
                        2
 
4160
                )
2932
4161
        end
2933
 
        return Syncs.add(opts).inlet
 
4162
 
 
4163
        return Syncs.add( opts ).inlet
 
4164
 
2934
4165
end
2935
4166
 
2936
4167
 
2937
 
-----
 
4168
--
2938
4169
-- Spawns a new child process.
2939
4170
--
2940
 
--
2941
 
function spawn(agent, binary, ...)
2942
 
        if agent == nil or type(agent) ~= 'table' then
2943
 
                error('spawning with an invalid agent', 2)
 
4171
function spawn(
 
4172
        agent,  -- the reason why a process is spawned.
 
4173
                -- a delay or delay list for a sync
 
4174
                -- it will mark the related files as blocked.
 
4175
        binary, -- binary to call
 
4176
        ...     -- arguments
 
4177
)
 
4178
        if
 
4179
                agent == nil or
 
4180
                type( agent ) ~= 'table'
 
4181
        then
 
4182
                error(
 
4183
                        'spawning with an invalid agent',
 
4184
                        2
 
4185
                )
2944
4186
        end
2945
4187
 
2946
4188
        if lsyncdStatus == 'fade' then
2947
 
                log('Normal', 'ignored process spawning while fading')
 
4189
                log(
 
4190
                        'Normal',
 
4191
                        'ignored process spawning while fading'
 
4192
                )
2948
4193
                return
2949
4194
        end
2950
4195
 
2951
 
        if type(binary) ~= 'string' then
2952
 
                error('calling spawn(agent, binary, ...), binary is not a string', 2)
2953
 
        end
2954
 
 
2955
 
        local dol = InletFactory.getDelayOrList(agent)
2956
 
        if not dol then error('spawning with an unknown agent', 2) end
2957
 
 
2958
 
        -- checks if spawn is called on already active event
 
4196
        if type( binary ) ~= 'string' then
 
4197
                error(
 
4198
                        'calling spawn(agent, binary, ...): binary is not a string',
 
4199
                        2
 
4200
                )
 
4201
        end
 
4202
 
 
4203
        local dol = InletFactory.getDelayOrList( agent )
 
4204
 
 
4205
        if not dol then
 
4206
                error(
 
4207
                        'spawning with an unknown agent',
 
4208
                        2
 
4209
                )
 
4210
        end
 
4211
 
 
4212
        --
 
4213
        -- checks if a spawn is called on an already active event
 
4214
        --
2959
4215
        if dol.status then
 
4216
 
 
4217
                -- is an event
 
4218
 
2960
4219
                if dol.status ~= 'wait' then
2961
4220
                        error('spawn() called on an non-waiting event', 2)
2962
4221
                end
2963
 
        else -- is a list
 
4222
 
 
4223
        else
 
4224
                -- is a list
 
4225
 
2964
4226
                for _, d in ipairs(dol) do
2965
4227
                        if d.status ~= 'wait' and d.status ~= 'block' then
2966
4228
                                error('spawn() called on an non-waiting event list', 2)
2967
4229
                        end
2968
4230
                end
 
4231
 
2969
4232
        end
2970
4233
 
2971
 
        local pid = lsyncd.exec(binary, ...)
 
4234
        --
 
4235
        -- tries to spawn the process
 
4236
        --
 
4237
        local pid = lsyncd.exec( binary, ... )
2972
4238
 
2973
4239
        if pid and pid > 0 then
 
4240
 
2974
4241
                processCount = processCount + 1
2975
 
                if settings.maxProcesses and processCount > settings.maxProcesses then
2976
 
                        error('Spawned too much processes!')
 
4242
                if
 
4243
                        uSettings.maxProcesses and
 
4244
                        processCount > uSettings.maxProcesses
 
4245
                then
 
4246
                        error( 'Spawned too much processes!' )
2977
4247
                end
2978
 
                local sync = InletFactory.getSync(agent)
 
4248
 
 
4249
                local sync = InletFactory.getSync( agent )
 
4250
 
2979
4251
                -- delay or list
2980
4252
                if dol.status then
 
4253
 
2981
4254
                        -- is a delay
2982
4255
                        dol.status = 'active'
2983
 
                        sync.processes[pid] = dol
 
4256
                        sync.processes[ pid ] = dol
 
4257
 
2984
4258
                else
 
4259
 
2985
4260
                        -- is a list
2986
 
                        for _, d in ipairs(dol) do
 
4261
                        for _, d in ipairs( dol ) do
2987
4262
                                d.status = 'active'
2988
4263
                        end
2989
 
                        sync.processes[pid] = dol
 
4264
                        sync.processes[ pid ] = dol
 
4265
 
2990
4266
                end
 
4267
 
2991
4268
        end
2992
4269
end
2993
4270
 
2994
 
-----
 
4271
--
2995
4272
-- Spawns a child process using the default shell.
2996
4273
--
2997
 
function spawnShell(agent, command, ...)
2998
 
        return spawn(agent, '/bin/sh', '-c', command, '/bin/sh', ...)
 
4274
function spawnShell(
 
4275
        agent,     -- the delay(list) to spawn the command for
 
4276
        command,   -- the shell command
 
4277
        ...        -- additonal arguments
 
4278
)
 
4279
        return spawn(
 
4280
                agent,
 
4281
                '/bin/sh',
 
4282
                '-c',
 
4283
                command,
 
4284
                '/bin/sh',
 
4285
                ...
 
4286
        )
2999
4287
end
3000
4288
 
3001
4289
-----
3002
4290
-- Observes a filedescriptor
3003
4291
--
3004
 
function observefd(fd, ready, writey)
3005
 
        return lsyncd.observe_fd(fd, ready, writey)
3006
 
end
3007
 
 
3008
 
-----
3009
 
--
3010
 
function nonobservefd(fd)
3011
 
        return lsyncd.nonobserve_fd(fd)
3012
 
end
3013
 
 
3014
 
-----
 
4292
function observefd(
 
4293
        fd,     -- file descriptor
 
4294
        ready,  -- called when fd is ready to be read
 
4295
        writey  -- called when fd is ready to be written
 
4296
)
 
4297
        return lsyncd.observe_fd(
 
4298
                fd,
 
4299
                ready,
 
4300
                writey
 
4301
        )
 
4302
end
 
4303
 
 
4304
--
 
4305
-- Stops observeing a filedescriptor
 
4306
--
 
4307
function nonobservefd(
 
4308
        fd      -- file descriptor
 
4309
)
 
4310
        return lsyncd.nonobserve_fd( fd )
 
4311
end
 
4312
 
 
4313
--
3015
4314
-- Calls func at timestamp.
 
4315
--
3016
4316
-- Use now() to receive current timestamp
 
4317
-- add seconds with '+' to it
3017
4318
--
3018
4319
alarm = UserAlarms.alarm
3019
4320
 
3020
 
-----
 
4321
--
3021
4322
-- Comfort routine also for user.
3022
4323
-- Returns true if 'String' starts with 'Start'
3023
4324
--
3024
 
function string.starts(String,Start)
3025
 
        return string.sub(String,1,#Start)==Start
 
4325
function string.starts( String, Start )
 
4326
 
 
4327
        return string.sub( String, 1, #Start )==Start
 
4328
 
3026
4329
end
3027
4330
 
3028
 
-----
 
4331
--
3029
4332
-- Comfort routine also for user.
3030
4333
-- Returns true if 'String' ends with 'End'
3031
4334
--
3032
 
function string.ends(String,End)
3033
 
        return End=='' or string.sub(String,-#End)==End
3034
 
end
3035
 
 
3036
 
-----
3037
 
--
3038
 
settings = {}
3039
 
 
3040
 
-----
 
4335
function string.ends( String, End )
 
4336
 
 
4337
        return End == '' or string.sub( String, -#End ) == End
 
4338
 
 
4339
end
 
4340
 
 
4341
--
 
4342
-- The Lsyncd 2.1 settings call
 
4343
--
 
4344
function settings( a1 )
 
4345
        -- if a1 is a string this is a get operation
 
4346
        if type( a1 ) == 'string' then
 
4347
                return uSettings[ a1 ]
 
4348
        end
 
4349
 
 
4350
        -- if its a table it sets all the value of the bale
 
4351
        for k, v in pairs( a1 ) do
 
4352
                if type( k ) ~= 'number' then
 
4353
                        uSettings[ k ] = v
 
4354
                else
 
4355
                        uSettings[ v ] = true
 
4356
                end
 
4357
        end
 
4358
end
 
4359
settingsSafe = settings
 
4360
 
 
4361
--
3041
4362
-- Returns the core the runners function interface.
3042
4363
--
3043
4364
return runner