From d0c6770b7ce740eb1f07a26b78008ddd88c25728 Mon Sep 17 00:00:00 2001 From: Axel Kittenberger Date: Wed, 20 Jun 2018 08:39:16 +0200 Subject: [PATCH] allowing multiple config files, allowing stdin as config, allowing commandline commands as configs --- CMakeLists.txt | 1 + ChangeLog | 26 ++++++---- core/main.c | 82 +------------------------------ core/mci.c | 2 + core/stdin.c | 102 ++++++++++++++++++++++++++++++++++++++ core/stdin.h | 30 ++++++++++++ default/proto.lua | 17 +++---- default/signal.lua | 10 +--- mantle/mci.lua | 120 ++++++++++++++++++++++++--------------------- 9 files changed, 226 insertions(+), 164 deletions(-) create mode 100644 core/stdin.c create mode 100644 core/stdin.h diff --git a/CMakeLists.txt b/CMakeLists.txt index 5946c90..9d587fb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,7 @@ set( LSYNCD_SRC core/mci.c core/mem.c core/signal.c + core/stdin.c core/time.c core/userobs.c core/util.c diff --git a/ChangeLog b/ChangeLog index 1900fd5..3faad9c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,13 +1,21 @@ ????-??-??: 3.0.0 - change: switched to systemd-style daemon (that is Lsyncd will no longer fork itself) - change: dropping /dev/fsevents hack (OSX support) - change: dropping exclude and excludeFrom (replaced by filter and filterFrom) - change: "insist" by default - change: removed _extra (if someone is hacky enough to really need it, - they can hack Lsyncd itself) - change: user scripts run in their own global lua environment - change: removed _merge - change: moved the default layer 1 functions to default.proto + * switching to systemd-style daemon + this means Lsyncd will no longer fork itself + * heavily modularized source code + * dropping /dev/fsevents hack + this means, no more OSX support, sorry + * "insist" by default and no longer an option + * remove the command line configs -rsync -rsyncssh and -direct + * allowing multiple config files in the command line + * allowing "-" to read config from stdin + * added "-c" for command line lua configs + * removed _extra + if someone is hacky enough to really need it, they can hack Lsyncd source themself + * added uses overloadable signal handling + * signal TERM is forwarded to (rsync) subprocesses + * user scripts run in their own lua environment + * removed _merge inheritance template + * moved the default layer 1 functions to default.proto 2018-03-09: 2.2.3 enhaencement: supporting includes with new filter and filterFrom options diff --git a/core/main.c b/core/main.c index 646b274..f73439f 100644 --- a/core/main.c +++ b/core/main.c @@ -8,27 +8,13 @@ */ #include "feature.h" -// FIXME remove unneeded headers - -#include #include -#include -#include #include -#include -#include -#include -#include #include -#include #include #include #include -#include #include -#include -#include -#include #define LUA_USE_APICHECK 1 #include @@ -46,7 +32,7 @@ #include "userobs.h" #ifdef WITH_INOTIFY -#include "inotify.h" +# include "inotify.h" #endif @@ -95,12 +81,6 @@ struct settings settings = { bool no_output = false; -/* -| The config file loaded by Lsyncd. -*/ -char * lsyncd_config_file = NULL; - - /* | False after first time Lsyncd started up. | @@ -376,69 +356,9 @@ main1( int argc, char *argv[] ) if( lua_pcall( L, 2, 1, -4 ) ) exit( -1 ); - if( first_time ) - { - // If not first time, simply retains the config file given - s = lua_tostring( L, -1 ); - - if( s ) lsyncd_config_file = s_strdup( s ); - } - lua_pop( L, 2 ); } - // checks existence of the config file - if( lsyncd_config_file ) - { - struct stat st; - - // gets the absolute path to the config file - // so in case of HUPing the daemon, it finds it again - char * apath = get_realpath( lsyncd_config_file ); - if( !apath ) - { - printlogf( L, "Error", "Cannot find config file at '%s'.", lsyncd_config_file ); - - exit( -1 ); - } - - free( lsyncd_config_file ); - - lsyncd_config_file = apath; - - if( stat( lsyncd_config_file, &st ) ) - { - printlogf( L, "Error", "Cannot find config file at '%s'.", lsyncd_config_file ); - - exit( -1 ); - } - - // loads and executes the config file - if( luaL_loadfile( L, lsyncd_config_file ) ) - { - printlogf( - L, "Error", - "error loading %s: %s", lsyncd_config_file, lua_tostring( L, -1 ) - ); - - exit( -1 ); - } - - // loads the user enivornment - lua_getglobal( L, "userenv" ); - lua_setupvalue( L, -2, 1 ); - - if( lua_pcall( L, 0, LUA_MULTRET, 0) ) - { - printlogf( - L, "Error", - "error preparing %s: %s", lsyncd_config_file, lua_tostring( L, -1 ) - ); - - exit( -1 ); - } - } - // runs initializations from mantle // it will set the configuration and add watches { diff --git a/core/mci.c b/core/mci.c index a75a8f1..573c0f7 100644 --- a/core/mci.c +++ b/core/mci.c @@ -33,6 +33,7 @@ #include "observe.h" #include "pipe.h" #include "signal.h" +#include "stdin.h" #include "time.h" #include "userobs.h" #include "util.h" @@ -673,6 +674,7 @@ static const luaL_Reg corelib[ ] = { "realdir", l_realdir }, { "softreset", l_softreset }, { "stackdump", l_stackdump }, + { "stdin", l_stdin }, { "terminate", l_terminate }, { NULL, NULL } }; diff --git a/core/stdin.c b/core/stdin.c new file mode 100644 index 0000000..30205c8 --- /dev/null +++ b/core/stdin.c @@ -0,0 +1,102 @@ +/* +| stdin.c from Lsyncd -- the Live (Mirror) Syncing Demon +| +| Reads a config file from stdin and buffers it. +| +| On every run of Lsyncd a config file from stdin is +| read only once, in case of a HUP soft reset the buffered +| version is used. +| +| License: GPLv2 (see COPYING) or any later version +| Authors: Axel Kittenberger +*/ +#include "feature.h" + +#include +#include +#include + +#define LUA_USE_APICHECK 1 +#include +#include +#include + +#include "log.h" +#include "mem.h" + + +/* +| Stdin read buffer. +*/ +static char * buf = NULL; + + +/* +| Size of stdin buffer. +*/ +static size_t bsize = 0; + + +/* +| Bytes read from stdin +*/ +static size_t bread = 0; + + +/* +| Reads a config file from stdin. +| Or returns an already read file. +*/ +char const * +read_stdin( + lua_State *L +) +{ + if( buf ) return buf; + + bsize = 1024; + buf = s_malloc( bsize ); + + while( true ) + { + bread += fread( buf + bread, 1, bsize - bread - 1, stdin ); + + if( ferror( stdin ) ) + { + printlogf( L, "Error", "Failure reading stdin" ); + + exit( -1 ); + } + + if( feof( stdin ) ) break; + + if( bsize - bread < 1024 ) buf = s_realloc( buf, bsize *= 2 ); + } + + buf[ bread ] = 0; + + return buf; +} + + +/* +| Lua wrapper to read_stdin( ). +| +| Params on Lua stack: +| none +| +| Returns on Lua stack: +| the config file +*/ +int +l_stdin( + lua_State *L +) +{ + char const * b = read_stdin( L ); + + lua_pushstring( L, b ); + + return 1; +} + diff --git a/core/stdin.h b/core/stdin.h new file mode 100644 index 0000000..2cb8879 --- /dev/null +++ b/core/stdin.h @@ -0,0 +1,30 @@ +/* +| stdinh from Lsyncd -- the Live (Mirror) Syncing Demon +| +| Reads a config file from stdin and buffers it. +| +| On every run of Lsyncd a config file from stdin is +| read only once, in case of a HUP soft reset the buffered +| version is used. +| +| License: GPLv2 (see COPYING) or any later version +| Authors: Axel Kittenberger +*/ +#ifndef LSYNCD_STDIN_H +#define LSYNCD_STDIN_H + + +/* +| Reads a config file from stdin. +| Or returns an already read file. +*/ +extern char const * read_stdin( lua_State *L ); + + +/* +| Lua wrapper to read_stdin( ). +*/ +extern int l_stdin( lua_State *L ); + + +#endif diff --git a/default/proto.lua b/default/proto.lua index b6c0ec8..a79dec4 100644 --- a/default/proto.lua +++ b/default/proto.lua @@ -115,17 +115,14 @@ proto.collect = function return 'ok' elseif rc == 'again' then - if settings( 'insist' ) - then - log( - 'Normal', - 'Retrying startup of ', - agent.source, ' -> ', agent.target, - ': ', exitcode - ) + log( + 'Normal', + 'Retrying startup of ', + agent.source, ' -> ', agent.target, + ': ', exitcode + ) - return 'again' - end + return 'again' elseif rc == 'die' then log( diff --git a/default/signal.lua b/default/signal.lua index ec9348d..eb3bf6f 100644 --- a/default/signal.lua +++ b/default/signal.lua @@ -94,7 +94,6 @@ init = local hup = getsignal( 'HUP' ) local int = getsignal( 'INT' ) local term = getsignal( 'TERM' ) - local usr1 = getsignal( 'USR1' ) if hup ~= false then @@ -103,7 +102,7 @@ init = if int ~= false then - int = makeSignalHandler( 'INT', 'INT', 'terminating', finishInt ) + int = makeSignalHandler( 'INT', nil, 'terminating', finishInt ) end if term ~= false @@ -111,11 +110,6 @@ init = term = makeSignalHandler( 'TERM', 'TERM', 'terminating', finishTerm ) end - if usr1 ~= false - then - usr1 = makeSignalHandler( 'USR1', nil, 'terminating', finishTerm ) - end - - onsignal( 'HUP', hup, 'INT', int, 'TERM', term, 'USR1', usr1 ) + onsignal( 'HUP', hup, 'INT', int, 'TERM', term ) end diff --git a/mantle/mci.lua b/mantle/mci.lua index 10b06e0..8ee7bf5 100644 --- a/mantle/mci.lua +++ b/mantle/mci.lua @@ -222,14 +222,15 @@ function mci.help( ) [[ USAGE: - lsyncd [OPTIONS] [CONFIG-FILE] + lsyncd [OPTIONS] [CONFIG-FILE(S)] OPTIONS: - -delay SECS Overrides default delay times + -c STRING Executes STRING as Lua config + -delay SECS Overrides default delay times -help Shows this - -log all Logs everything (debug) - -log scarce Logs errors only - -log [Category] Turns on logging for a debug category + -log all Logs everything (debug) + -log scarce Logs errors only + -log CATEGORY Turns on logging for a debug category -logfile FILE Writes log to FILE (DEFAULT: uses syslog) -version Prints versions and exits @@ -253,11 +254,19 @@ end -- terminates on invalid arguments. -- function mci.configure( - args, -- arguments given by user + args, -- command line arguments monitors -- list of monitors the core can do ) Monitor.initialize( monitors ) + -- confs is filled with + -- all config file + -- stdin read requests + -- inline configs + local confs = { } + + local i = 1 + -- -- a list of all valid options -- @@ -269,62 +278,41 @@ function mci.configure( -- local options = { - -- log is handled by core already. + c = + { 1, function( string ) table.insert( confs, { command = string, n = i } ) end }, delay = - { - 1, - function( secs ) - clSettings.delay = secs + 0 - end - }, + { 1, function( secs ) clSettings.delay = secs + 0 end }, - log = { 1, nil }, + -- log is handled by core already. + log = + { 1, nil }, logfile = - { - 1, - function( file ) - clSettings.logfile = file - end - }, + { 1, function( file ) clSettings.logfile = file end }, version = - { - 0, - function( ) - io.stdout:write( 'Version: ', lsyncd_version, '\n' ) - - os.exit( 0 ) - end - } + { 0, function( ) io.stdout:write( 'Version: ', lsyncd_version, '\n' ) os.exit( 0 ) end } } - -- non-opts is filled with all args that were no part dash options - local nonopts = { } - - local i = 1 - while i <= #args do local a = args[ i ] if a:sub( 1, 1 ) ~= '-' then - table.insert( nonopts, args[ i ] ) + table.insert( confs, { file = args[ i ] } ) + elseif a == '-' + then + table.insert( confs, { stdin = true } ) else - if a:sub( 1, 2 ) == '--' - then - a = a:sub( 3 ) - else - a = a:sub( 2 ) - end + if a:sub( 1, 2 ) == '--' then a = a:sub( 3 ) else a = a:sub( 2 ) end local o = options[ a ] if not o then - log( 'Error', 'unknown option command line option ', args[ i ] ) + log( 'Error', 'unknown command line option ', args[ i ] ) os.exit( -1 ) end @@ -346,33 +334,53 @@ function mci.configure( o[ 2 ]( ) elseif o[ 1 ] == 1 then - o[ 2 ]( args[ i + 1] ) + o[ 2 ]( args[ i + 1 ] ) elseif o[ 1 ] == 2 then - o[ 2 ]( args[ i + 1], args[ i + 2] ) + o[ 2 ]( args[ i + 1 ], args[ i + 2 ] ) elseif o[ 1 ] == 3 - then - o[ 2 ]( args[ i + 1], args[ i + 2], args[ i + 3] ) + then + o[ 2 ]( args[ i + 1 ], args[ i + 2 ], args[ i + 3 ] ) end end - i = i + o[1] + i = i + o[ 1 ] end i = i + 1 end - if #nonopts == 0 - then - mci.help( args[ 0 ] ) - elseif #nonopts == 1 - then - return nonopts[ 1 ] - else - -- TODO make this possible - log( 'Error', 'There can only be one config file in the command line.' ) + if #confs == 0 then mci.help( args[ 0 ] ) end - os.exit( -1 ) + for _, conf in ipairs( confs ) + do + local f, err, status + + if conf.stdin + then + f, err = load( core.stdin( ), 'stdin', 't', userenv ) + elseif conf.command + then + f, err = load( conf.command, 'arg: '..conf.n, 't', userenv ) + else + f, err = loadfile( conf.file, 't', userenv ) + end + + if not f + then + log( 'Error', err ) + + os.exit( -1 ) + end + + status, err = pcall( f ) + + if not status + then + log( 'Error', err ) + + os.exit( -1 ) + end end end