Added &include and &merge config-file directives that allow the
authorWayne Davison <wayned@samba.org>
Sun, 27 Jul 2008 03:03:45 +0000 (20:03 -0700)
committerWayne Davison <wayned@samba.org>
Sun, 27 Jul 2008 03:03:45 +0000 (20:03 -0700)
daemon's config file incorporate the contents of other files.

loadparm.c
params.c
rsyncd.conf.yo

index c540840..4b3741f 100644 (file)
@@ -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(&section_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;
 
index 7610ac7..5d7b00d 100644 (file)
--- 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 */
 
 /* -------------------------------------------------------------------------- */
index b3ebd87..9c94bbf 100644 (file)
@@ -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