Hello :)
This patch (applies to 1.5-dev3) adds "include" configuration statement to
haproxy configuration parser. I wrote this patch becouse my haproxy
configuration became too big to be simply maintainable and becouse i really
like "conf.d" configuration style.
Usage:
include glob_pattern
Include statement support absolute (/path/to/*.cfg) or relative (dir/*.cfg)
glob(3) patterns. Current parsed config file's directory is used as base
directory in case of relative glob patterns. Include directive also works in
included configuration fragments.
Example (/etc/haproxy/haproxy.conf):
--- snip ---
global
log 127.0.0.1 local0 info
maxconn 10000
user nobody
group nobody
daemon
# include defaults
# will include /etc/haproxy/conf.d/defaults.cfg
include conf.d/defaults.cfg
# include listen directives
include conf.d/listen-*.cfg
# include frontend declarations
include conf.d/frontend-*.cfg
# include backend declarations
include conf.d/backend-*.cfg
# include full proxy declarations
include conf.d/proxy-*.cfg
# EOF
-- snip ---
Best regards, Brane
diff --git a/include/common/cfgparse.h b/include/common/cfgparse.h
index b43f899..6e46c21 100644
--- a/include/common/cfgparse.h
+++ b/include/common/cfgparse.h
@@ -35,6 +35,10 @@
#define CFG_USERLIST 3
#define CFG_PEERS 4
+
+/* maximum include recursion level */
+#define INCLUDE_RECURSION_LEVEL_MAX 10
+
struct cfg_keyword {
int section; /* section type for this keyword */
const char *kw; /* the keyword itself */
@@ -63,7 +67,7 @@ extern int cfg_maxconn;
int cfg_parse_global(const char *file, int linenum, char **args, int inv);
int cfg_parse_listen(const char *file, int linenum, char **args, int inv);
-int readcfgfile(const char *file);
+int readcfgfile(const char *file, int recdepth);
void cfg_register_keywords(struct cfg_kw_list *kwl);
void cfg_unregister_keywords(struct cfg_kw_list *kwl);
void init_default_instance();
diff --git a/src/cfgparse.c b/src/cfgparse.c
index d3223ff..29b8b91 100644
--- a/src/cfgparse.c
+++ b/src/cfgparse.c
@@ -22,6 +22,8 @@
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
+#include <glob.h>
+#include <libgen.h>
#include <netinet/tcp.h>
@@ -5193,6 +5195,99 @@ out:
return err_code;
}
+/**
+ * This function takes glob(3) pattern and tries to resolve
+ * that pattern to files and tries to include them.
+ *
+ * See readcfgfile() for return values.
+ */
+int cfgfile_include (char *pattern, char *dir, int recdepth) {
+ if (pattern == NULL) {
+ Alert("Config file include pattern == NULL; This should never happen.\n");
+ return ERR_ABORT;
+ }
+ if (recdepth >= INCLUDE_RECURSION_LEVEL_MAX) {
+ Alert(
+ "Refusing to include filename pattern: '%s': too deep recursion level: %d.\n",
+ pattern,
+ recdepth
+ );
+ return ERR_ABORT;
+ }
+
+ /** don't waste time with empty strings */
+ if (strlen(pattern) < 1) return 0;
+
+ /** we want to support relative to include file glob patterns */
+ int buf_len = 3;
+ if (dir != NULL)
+ buf_len += strlen(dir);
+ buf_len += strlen(pattern);
+ char *real_pattern = malloc(buf_len);
+ if (real_pattern == NULL) {
+ Alert("Error allocating memory for glob pattern: %s\n", strerror(errno));
+ return ERR_ABORT;
+ }
+ memset(real_pattern, '\0', buf_len);
+ if (dir != NULL && pattern[0] != '/') {
+ strcat(real_pattern, dir);
+ strcat(real_pattern, "/");
+ }
+ strcat(real_pattern, pattern);
+
+ /* file inclusion result */
+ int result = 0;
+
+ /** glob the pattern */
+ glob_t res;
+ int rv = glob(
+ real_pattern,
+ (GLOB_NOESCAPE | GLOB_BRACE | GLOB_TILDE),
+ NULL,
+ &res
+ );
+ /* check for glob(3) injuries */
+ switch (rv) {
+ case GLOB_NOMATCH:
+ /* nothing was found */
+ break;
+
+ case GLOB_ABORTED:
+ Alert("Error globbing pattern '%s': read error.\n", real_pattern);
+ result = ERR_ABORT;
+ break;
+
+ case GLOB_NOSPACE:
+ Alert("Error globbing pattern '%s': out of memory.\n", real_pattern);
+ result = ERR_ABORT;
+ break;
+
+ default:
+ ;
+ int i = 0;
+ for (i = 0; i < res.gl_pathc; i++) {
+ char *file = res.gl_pathv[i];
+
+ /* parse configuration fragment */
+ int r = readcfgfile(file, recdepth);
+
+ /* check for injuries */
+ if (r != 0) {
+ result = r;
+ goto outta_cfgfile_include;
+ }
+ }
+ }
+
+ outta_cfgfile_include:
+
+ /** free glob result. */
+ globfree(&res);
+ free(real_pattern);
+
+ return result;
+}
+
/*
* This function reads and parses the configuration file given in the argument.
* Returns the error code, 0 if OK, or any combination of :
@@ -5203,7 +5298,7 @@ out:
* Only the two first ones can stop processing, the two others are just
* indicators.
*/
-int readcfgfile(const char *file)
+int readcfgfile(const char *file, int recdepth)
{
char thisline[LINESIZE];
FILE *f;
@@ -5211,8 +5306,10 @@ int readcfgfile(const char *file)
int confsect = CFG_NONE;
int err_code = 0;
- if ((f=fopen(file,"r")) == NULL)
+ if ((f=fopen(file,"r")) == NULL) {
+ Alert("Error opening configuration file %s: %s\n", file, strerror(errno));
return -1;
+ }
while (fgets(thisline, sizeof(thisline), f) != NULL) {
int arg, kwm = KWM_STD;
@@ -5343,6 +5440,40 @@ int readcfgfile(const char *file)
err_code |= ERR_ALERT | ERR_FATAL;
}
+ /* include statement? */
+ if (strcmp(args[0], "include") == 0) {
+ if (args[1] == NULL || strlen(args[1]) < 1) {
+ Alert("parsing [%s:%d]: include statement requires file glob pattern.\n",
+ file, linenum);
+ err_code = ERR_ABORT;
+ break;
+ }
+ /**
+ * compute file's dirname - this is necessary becouse
+ * dirname(3) returns shared buffer address
+ */
+ int buf_len = strlen(file) + 1;
+ char *file_dir = malloc(buf_len);
+ if (file_dir == NULL) {
+ Alert("Unable to allocate memory for config file dirname.");
+ err_code = ERR_ABORT;
+ break;
+ }
+ memset(file_dir, '\0', buf_len);
+ strcpy(file_dir, file);
+ strcpy(file_dir, dirname(file_dir));
+
+ /* include pattern */
+ int r = cfgfile_include(args[1], file_dir, (recdepth + 1));
+ free(file_dir);
+ /* check for injuries */
+ if (r != 0) {
+ err_code = r;
+ break;
+ }
+ continue;
+ }
+
if (!strcmp(args[0], "listen") ||
!strcmp(args[0], "frontend") ||
!strcmp(args[0], "backend") ||
diff --git a/src/haproxy.c b/src/haproxy.c
index c5aa3cc..320a089 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -515,7 +515,7 @@ void init(int argc, char **argv)
list_for_each_entry(wl, &cfg_cfgfiles, list) {
int ret;
- ret = readcfgfile(wl->s);
+ ret = readcfgfile(wl->s, 0);
if (ret == -1) {
Alert("Could not open configuration file %s : %s\n",
wl->s, strerror(errno));