From 82fc4b1f47e3de4900aed6b06168fdf8d81ea9de Mon Sep 17 00:00:00 2001
From: Joe Gooch <mrwizard@k12system.com>
Date: Tue, 2 Feb 2010 11:33:59 -0500
Subject: IncludeDir directive - includes a directory of config files (a la conf.d)

---
 config.c |   66 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
 pound.8  |    9 ++++++++
 pound.h  |    1 +
 3 files changed, 75 insertions(+), 1 deletions(-)

diff --git a/config.c b/config.c
index 347ed67..722caca 100644
--- a/config.c
+++ b/config.c
@@ -78,7 +78,7 @@ static regex_t  Service, ServiceName, URL, HeadRequire, HeadDeny, BackEnd, Emerg
 static regex_t  Redirect, RedirectN, TimeOut, Session, Type, TTL, ID, DynScale;
 static regex_t  ClientCert, AddHeader, Ciphers, CAlist, VerifyList, CRLlist, NoHTTPS11;
 static regex_t  ForceHTTP10, SSLUncleanShutdown;
-static regex_t  Grace, Include, ConnTO, IgnoreCase, HTTPS, HTTPSCert;
+static regex_t  Grace, Include, IncludeDir, ConnTO, IgnoreCase, HTTPS, HTTPSCert;
 
 static regmatch_t   matches[5];
 
@@ -128,6 +128,63 @@ conf_err(const char *msg)
     exit(1);
 }
 
+static void include_dir(const char *conf_path) {
+    DIR * dp;
+    struct dirent *de;
+
+    char buf[512];
+    char *files[200], *cp;
+    int filecnt = 0;
+    int idx,use;
+
+    logmsg(LOG_DEBUG, "Including Dir %s", conf_path);
+
+    if((dp = opendir(conf_path)) == NULL) {
+        conf_err("can't open IncludeDir directory");
+        exit(1);
+    }
+
+    while((de = readdir(dp))!=NULL) {
+        if (de->d_name[0] == '.') continue;
+        if ( (strlen(de->d_name) >= 5 && !strncmp(de->d_name + strlen(de->d_name) - 4, ".cfg", 4)) ||
+             (strlen(de->d_name) >= 6 && !strncmp(de->d_name + strlen(de->d_name) - 5, ".conf", 5))
+           ){
+            snprintf(buf, sizeof(buf), "%s%s%s", conf_path, (conf_path[strlen(conf_path)-1]=='/')?"":"/", de->d_name);
+            buf[sizeof(buf)-1] = 0;
+            if (filecnt == sizeof(files)/sizeof(*files)) {
+                conf_err("Max config files per directory reached");
+            }
+            if ((files[filecnt++] = strdup(buf)) == NULL) {
+                conf_err("IncludeDir out of memory");
+            }
+            continue;
+        }
+    }
+    /* We order the list, and include in reverse order, because include_file adds to the top of the list */
+    while(filecnt) {
+        use = 0;
+        for(idx = 1; idx<filecnt; idx++)
+            if (strcmp(files[use], files[idx])<0)
+                use=idx;
+
+        logmsg(LOG_DEBUG, " I==> %s", files[use]);
+
+        // Copied from Include logic
+        if(cur_fin == (MAX_FIN - 1))
+            conf_err("Include nesting too deep");
+        cur_fin++;
+        f_name[cur_fin] = files[use];
+        if((f_in[cur_fin] = fopen(files[use], "rt")) == NULL) {
+            logmsg(LOG_ERR, "%s line %d: Can't open included file %s", f_name[cur_fin], n_lin[cur_fin], files[use]);
+            exit(1);
+        }
+        n_lin[cur_fin] = 0;
+        files[use] = files[--filecnt];
+    }
+
+    closedir(dp);
+}
+
 static char *
 conf_fgets(char *buf, const int max)
 {
@@ -163,6 +220,11 @@ conf_fgets(char *buf, const int max)
             n_lin[cur_fin] = 0;
             continue;
         }
+        if(!regexec(&IncludeDir, buf, 4, matches, 0)) {
+            buf[matches[1].rm_eo] = '\0';
+            include_dir(buf + matches[1].rm_so);
+            continue;
+        }
         return buf;
     }
 }
@@ -1195,6 +1257,7 @@ config_parse(const int argc, char **const argv)
     || regcomp(&ForceHTTP10, "^[ \t]*ForceHTTP10[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
     || regcomp(&SSLUncleanShutdown, "^[ \t]*SSLUncleanShutdown[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
     || regcomp(&Include, "^[ \t]*Include[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
+    || regcomp(&IncludeDir, "^[ \t]*IncludeDir[ \t]+\"(.+)\"[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
     || regcomp(&ConnTO, "^[ \t]*ConnTO[ \t]+([1-9][0-9]*)[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
     || regcomp(&IgnoreCase, "^[ \t]*IgnoreCase[ \t]+([01])[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
     || regcomp(&HTTPS, "^[ \t]*HTTPS[ \t]*$", REG_ICASE | REG_NEWLINE | REG_EXTENDED)
@@ -1352,6 +1415,7 @@ config_parse(const int argc, char **const argv)
     regfree(&ForceHTTP10);
     regfree(&SSLUncleanShutdown);
     regfree(&Include);
+    regfree(&IncludeDir);
     regfree(&ConnTO);
     regfree(&IgnoreCase);
     regfree(&HTTPS);
diff --git a/pound.8 b/pound.8
index 930050c..c2235f4 100644
--- a/pound.8
+++ b/pound.8
@@ -190,6 +190,15 @@ directives (they affect only a specific group of requests).
 Global directives may appear anywhere within the configuration file, though it is
 customary for them to be at the start. They may appear in any order.
 .TP
+\fBInclude\fR "file"
+Includes another configuration file inline, as if it were part of the top config file.
+This directive can be used in any block... but the result must be syntactically correct.
+.TP
+\fBIncludeDir\fR "path"
+Looks for files with conf or cfg extensions in "path", and includes all files, in sorted
+order, inline in the configuration as if it were part of the to pconfiguration file.
+This directive can be used in any block... but the result must be syntactically correct.
+.TP
 \fBUser\fR "user_name"
 Specify the user
 .B Pound
diff --git a/pound.h b/pound.h
index 10365d6..371bdb8 100644
--- a/pound.h
+++ b/pound.h
@@ -29,6 +29,7 @@
 #include    <stdio.h>
 #include    <math.h>
 
+#include    <dirent.h>
 #if HAVE_STDLIB_H
 #include    <stdlib.h>
 #else
-- 
1.6.0.2

