From: Wayne Davison Date: Sun, 27 Jul 2008 03:03:45 +0000 (-0700) Subject: Added &include and &merge config-file directives that allow the X-Git-Url: https://mattmccutchen.net/rsync/rsync.git/commitdiff_plain/8a3ddcfc8179233d007a4ee88f22c17d28e8ea4e Added &include and &merge config-file directives that allow the daemon's config file incorporate the contents of other files. --- diff --git a/loadparm.c b/loadparm.c index c5408409..4b3741f9 100644 --- a/loadparm.c +++ b/loadparm.c @@ -154,6 +154,11 @@ typedef struct { BOOL write_only; } section; +typedef struct { + global g; + section s; +} global_and_section; + /* This is a default section used to prime a sections structure. In order * to make these easy to keep sorted in the same way as the variables * above, use the variable name in the leading comment, including a @@ -205,6 +210,7 @@ static section sDefault = { /* local variables */ static item_list section_list = EMPTY_ITEM_LIST; +static item_list section_stack = EMPTY_ITEM_LIST; static int iSectionIndex = -1; static BOOL bInGlobalSection = True; @@ -676,7 +682,29 @@ static BOOL do_parameter(char *parmname, char *parmvalue) * Returns True on success, False on failure. */ static BOOL do_section(char *sectionname) { - BOOL isglobal = strwicmp(sectionname, GLOBAL_NAME) == 0; + BOOL isglobal; + + if (*sectionname == ']') { /* A special push/pop/reset directive from params.c */ + bInGlobalSection = 1; + if (strcmp(sectionname+1, "push") == 0) { + global_and_section *gs = EXPAND_ITEM_LIST(§ion_stack, global_and_section, 2); + memcpy(&gs->g, &Globals, sizeof Globals); + memcpy(&gs->s, &sDefault, sizeof sDefault); + } else if (strcmp(sectionname+1, "pop") == 0 + || strcmp(sectionname+1, "reset") == 0) { + global_and_section *gs = ((global_and_section *)section_stack.items) + section_stack.count - 1; + if (!section_stack.count) + return False; + memcpy(&Globals, &gs->g, sizeof Globals); + memcpy(&sDefault, &gs->s, sizeof sDefault); + if (sectionname[1] == 'p') + section_stack.count--; + } else + return False; + return True; + } + + isglobal = strwicmp(sectionname, GLOBAL_NAME) == 0; /* if we were in a global section then do the local inits */ if (bInGlobalSection && !isglobal) @@ -714,7 +742,7 @@ static BOOL do_section(char *sectionname) /* Load the modules from the config file. Return True on success, * False on failure. */ -BOOL lp_load(char *pszFname, int globals_only) +int lp_load(char *pszFname, int globals_only) { pstring n2; diff --git a/params.c b/params.c index 7610ac78..5d7b00de 100644 --- a/params.c +++ b/params.c @@ -93,6 +93,8 @@ static char *bufr = NULL; static int bSize = 0; +static BOOL (*the_sfunc)(char *); +static BOOL (*the_pfunc)(char *, char *); /* -------------------------------------------------------------------------- ** * Functions... @@ -406,7 +408,71 @@ static BOOL Parameter( FILE *InFile, BOOL (*pfunc)(char *, char *), int c ) return( pfunc( bufr, &bufr[vstart] ) ); /* Pass name & value to pfunc(). */ } /* Parameter */ -static BOOL Parse( FILE *InFile, +static int name_cmp(const void *n1, const void *n2) +{ + return strcmp(*(char * const *)n1, *(char * const *)n2); +} + +static int include_config(char *include, int manage_globals) +{ + item_list conf_list; + struct dirent *di; + char buf[MAXPATHLEN], **bpp; + int ret = 1; + size_t j; + DIR *d; + + memset(&conf_list, 0, sizeof conf_list); + + if ((d = opendir(include)) != NULL) { + while ((di = readdir(d)) != NULL) { + char *dname = d_name(di); + if (!wildmatch("*.conf", dname)) + continue; + bpp = EXPAND_ITEM_LIST(&conf_list, char *, 32); + pathjoin(buf, sizeof buf, include, dname); + *bpp = strdup(buf); + } + closedir(d); + } else { + STRUCT_STAT sb; + if (stat(include, &sb) < 0) + return 0; + bpp = EXPAND_ITEM_LIST(&conf_list, char *, 1); + *bpp = strdup(include); + } + + if (conf_list.count > 1) + qsort(conf_list.items, conf_list.count, sizeof (char *), name_cmp); + + bpp = conf_list.items; + for (j = 0; j < conf_list.count; j++) { + if (manage_globals && the_sfunc) + the_sfunc(j == 0 ? "]push" : "]reset"); + if ((ret = pm_process(bpp[j], the_sfunc, the_pfunc)) != 1) + break; + } + + if (manage_globals && the_sfunc && conf_list.count) + the_sfunc("]pop"); + + for (j = 0; j < conf_list.count; j++) + free(bpp[j]); + + return ret; +} + +static int parse_directives(char *name, char *val) +{ + if (strcasecmp(name, "include") == 0) + return include_config(val, 1); + if (strcasecmp(name, "merge") == 0) + return include_config(val, 0); + rprintf(FLOG, "Unknown directive: &%s.\n", name); + return 0; +} + +static int Parse( FILE *InFile, BOOL (*sfunc)(char *), BOOL (*pfunc)(char *, char *) ) /* ------------------------------------------------------------------------ ** @@ -418,7 +484,8 @@ static BOOL Parse( FILE *InFile, * pfunc - Function to be called when a parameter is scanned. * See Parameter(). * - * Output: True if the file was successfully scanned, else False. + * Output: 1 if the file was successfully scanned, 2 if the file was + * scanned until a section header with no section function, else 0. * * Notes: The input can be viewed in terms of 'lines'. There are four * types of lines: @@ -427,7 +494,7 @@ static BOOL Parse( FILE *InFile, * The remainder of the line is ignored. * Section - First non-whitespace character is a '['. * Parameter - The default case. - * + * * ------------------------------------------------------------------------ ** */ { @@ -448,24 +515,35 @@ static BOOL Parse( FILE *InFile, break; case '[': /* Section Header. */ - if (!sfunc) return True; - if( !Section( InFile, sfunc ) ) - return( False ); - c = EatWhitespace( InFile ); - break; + if (!sfunc) + return 2; + if( !Section( InFile, sfunc ) ) + return 0; + c = EatWhitespace( InFile ); + break; case '\\': /* Bogus backslash. */ c = EatWhitespace( InFile ); break; + case '&': /* Handle directives */ + the_sfunc = sfunc; + the_pfunc = pfunc; + c = EatWhitespace( InFile ); + c = Parameter( InFile, parse_directives, c ); + if (c != 1) + return c; + c = EatWhitespace( InFile ); + break; + default: /* Parameter line. */ if( !Parameter( InFile, pfunc, c ) ) - return( False ); + return 0; c = EatWhitespace( InFile ); break; } } - return( True ); + return 1; } /* Parse */ static FILE *OpenConfFile( char *FileName ) @@ -499,7 +577,7 @@ static FILE *OpenConfFile( char *FileName ) return( OpenedFile ); } /* OpenConfFile */ -BOOL pm_process( char *FileName, +int pm_process( char *FileName, BOOL (*sfunc)(char *), BOOL (*pfunc)(char *, char *) ) /* ------------------------------------------------------------------------ ** @@ -511,7 +589,8 @@ BOOL pm_process( char *FileName, * pfunc - A pointer to a function that will be called when * a parameter name and value are discovered. * - * Output: TRUE if the file was successfully parsed, else FALSE. + * Output: 1 if the file was successfully parsed, 2 if parsing ended at a + * section header w/o a section function, else 0. * * ------------------------------------------------------------------------ ** */ @@ -549,10 +628,10 @@ BOOL pm_process( char *FileName, if( !result ) /* Generic failure. */ { rprintf(FLOG, "%s Failed. Error returned from params.c:parse().\n", func); - return( False ); + return 0; } - return( True ); /* Generic success. */ + return result; } /* pm_process */ /* -------------------------------------------------------------------------- */ diff --git a/rsyncd.conf.yo b/rsyncd.conf.yo index b3ebd87e..9c94bbfa 100644 --- a/rsyncd.conf.yo +++ b/rsyncd.conf.yo @@ -616,6 +616,49 @@ module's uid/gid setting) without any chroot restrictions. enddit() +manpagesection(CONFIG DIRECTIVES) + +There are currently two config directives available that allow a config file to +incorporate the contents of other files: bf(&include) and bf(&merge). Both +allow a reference to either a file or a directory. They differ in how +segregated the file's contents are considered to be. The bf(&include) +directive treats each file as more distinct, with each one inheriting the +defaults of the parent file, and starting the parameter parsing as +globals/defaults. The bf(&merge) directive, on the other hand, treats the +file's contents as if it were simply inserted in place of the directive, and +thus it can contain parameters that can be set inside a parent file's module +settings, or whatever you like. + +When an bf(&include) or bf(&merge) directive refers to a directory, it will read +in all the bf(*.conf) files contained inside that directory (without any +recursive scanning), with the files sorted into alpha order. So, if you have a +directory named "rsyncd.d" with the files "foo.conf", "bar.conf", and +"baz.conf" inside it, this directive: + +verb( &include = /path/rsyncd.d ) + +would be the same as this set of directives: + +verb( &include = /path/rsyncd.d/bar.conf + &include = /path/rsyncd.d/baz.conf + &include = /path/rsyncd.d/foo.conf ) + +except that it adjusts as files are added and removed from the directory. + +The advantage of the bf(&include) directive is that you can define one or more +modules in a separate file with only the defaults you set in the parent file +affecting it, so you don't need to worry about the settings of a prior include +file changing a default. For instance, this is a useful /etc/rsyncd.conf file: + +verb( port = 873 + log file = /path/rsync.log + pid file = /var/lock/rsync.lock + + &include /etc/rsyncd.d ) + +The advantage of the bf(&merge) directive is that you can load config snippets +that can be included into multiple module definitions. + manpagesection(AUTHENTICATION STRENGTH) The authentication protocol used in rsync is a 128 bit MD4 based