If -f argument is a directory add all the files (and only files) it
containes to the config files list.
These files are added in lexical order (man alphasort).
Only files with ".cfg" extension are added.
Only non hidden files (not prefixed with ".") are added.
Symlink are followed.
The -f order is still respected:

        $ tree -a rootdir
        rootdir
        ├── dir1
        │   ├── 1.cfg
        │   ├── 2
        │   ├── 3.cfg
        │   ├── 4.cfg -> 1.cfg
        │   ├── 5 -> 1.cfg
        │   ├── .6.cfg
        │   ├── 7.cfg -> .
        │   └── dir4
        │       └── 8.cfg
        ├── dir2
        │   ├── 10.cfg
        │   └── 9.cfg
        ├── dir3
        │   └── 11.cfg
        ├── link -> dir3/
        ├── root1
        ├── root2
        └── root3

        $ ./haproxy -C rootdir -f root2 -f dir2 -f root3 -f dir1 \
                               -f link -f root1
        root2
        dir2/10.cfg
        dir2/9.cfg
        root3
        dir1/1.cfg
        dir1/3.cfg
        dir1/4.cfg
        link/11.cfg
        root1

This can be useful on systemd where you can't change the haproxy
commande line options on service reload.
---
 doc/haproxy.1             |   8 +--
 doc/management.txt        |  44 ++++++++--------
 include/common/standard.h |   8 +++
 src/haproxy.c             | 128 +++++++++++++++++++++++++++++++++++++++++-----
 src/standard.c            |  32 ++++++++++++
 5 files changed, 183 insertions(+), 37 deletions(-)

diff --git a/doc/haproxy.1 b/doc/haproxy.1
index a836d5d..08ea9df 100644
--- a/doc/haproxy.1
+++ b/doc/haproxy.1
@@ -6,7 +6,7 @@ HAProxy \- fast and reliable http reverse proxy and load 
balancer
 
 .SH SYNOPSIS
 
-haproxy \-f <configuration\ file> [\-L\ <name>] [\-n\ maxconn] [\-N\ maxconn] 
[\-C\ <dir>] [\-v|\-vv] [\-d] [\-D] [\-q] [\-V] [\-c] [\-p\ <pidfile>] [\-dk] 
[\-ds] [\-de] [\-dp] [\-db] [\-dM[<byte>]] [\-m\ <megs>] [{\-sf|\-st}\ 
pidlist...]
+haproxy \-f <configuration\ file|dir> [\-L\ <name>] [\-n\ maxconn] [\-N\ 
maxconn] [\-C\ <dir>] [\-v|\-vv] [\-d] [\-D] [\-q] [\-V] [\-c] [\-p\ <pidfile>] 
[\-dk] [\-ds] [\-de] [\-dp] [\-db] [\-dM[<byte>]] [\-m\ <megs>] [{\-sf|\-st}\ 
pidlist...]
 
 .SH DESCRIPTION
 
@@ -33,8 +33,10 @@ instances without risking the system's stability.
 .SH OPTIONS
 
 .TP
-\fB\-f <configuration file>\fP
-Specify configuration file path.
+\fB\-f <configuration file|dir>\fP
+Specify configuration file or directory path. If the argument is a directory
+the files (and only files) it containes are added in lexical order (man
+alphasort) ; only non hidden files with ".cfg" extension are added.
 
 .TP
 \fB\-L <name>\fP
diff --git a/doc/management.txt b/doc/management.txt
index e0469aa..69b3c18 100644
--- a/doc/management.txt
+++ b/doc/management.txt
@@ -124,26 +124,30 @@ enforce some settings without touching the configuration 
files. The current
 list of options is :
 
   -- <cfgfile>* : all the arguments following "--" are paths to configuration
-    file to be loaded and processed in the declaration order. It is mostly
-    useful when relying on the shell to load many files that are numerically
-    ordered. See also "-f". The difference between "--" and "-f" is that one
-    "-f" must be placed before each file name, while a single "--" is needed
-    before all file names. Both options can be used together, the command line
-    ordering still applies. When more than one file is specified, each file
-    must start on a section boundary, so the first keyword of each file must be
-    one of "global", "defaults", "peers", "listen", "frontend", "backend", and
-    so on. A file cannot contain just a server list for example.
-
-  -f <cfgfile> : adds <cfgfile> to the list of configuration files to be
-    loaded. Configuration files are loaded and processed in their declaration
-    order. This option may be specified multiple times to load multiple files.
-    See also "--". The difference between "--" and "-f" is that one "-f" must
-    be placed before each file name, while a single "--" is needed before all
-    file names. Both options can be used together, the command line ordering
-    still applies. When more than one file is specified, each file must start
-    on a section boundary, so the first keyword of each file must be one of
-    "global", "defaults", "peers", "listen", "frontend", "backend", and so
-    on. A file cannot contain just a server list for example.
+    file/directory to be loaded and processed in the declaration order. It is
+    mostly useful when relying on the shell to load many files that are
+    numerically ordered. See also "-f". The difference between "--" and "-f" is
+    that one "-f" must be placed before each file name, while a single "--" is
+    needed before all file names. Both options can be used together, the
+    command line ordering still applies. When more than one file is specified,
+    each file must start on a section boundary, so the first keyword of each
+    file must be one of "global", "defaults", "peers", "listen", "frontend",
+    "backend", and so on. A file cannot contain just a server list for example.
+
+  -f <cfgfile|cfgdir> : adds <cfgfile> to the list of configuration files to be
+    loaded. If <cfgdir> is a directory, all the files (and only files) it
+    containes are added in lexical order (man alphasort) to the list of
+    configuration files to be loaded ; only files with ".cfg" extension are
+    added, only non hidden files (not prefixed with ".") are added.
+    Configuration files are loaded and processed in their declaration order.
+    This option may be specified multiple times to load multiple files. See
+    also "--". The difference between "--" and "-f" is that one "-f" must be
+    placed before each file name, while a single "--" is needed before all file
+    names. Both options can be used together, the command line ordering still
+    applies. When more than one file is specified, each file must start on a
+    section boundary, so the first keyword of each file must be one of
+    "global", "defaults", "peers", "listen", "frontend", "backend", and so on.
+    A file cannot contain just a server list for example.
 
   -C <dir> : changes to directory <dir> before loading configuration
     files. This is useful when using relative paths. Warning when using
diff --git a/include/common/standard.h b/include/common/standard.h
index cd2208c..f123f1a 100644
--- a/include/common/standard.h
+++ b/include/common/standard.h
@@ -1089,4 +1089,12 @@ static inline unsigned long long rdtsc()
 }
 #endif
 
+/* append a copy of string <str> (in a wordlist) at the end of the list <li>
+ * On failure : return 0 and <err> filled with an error message.
+ * The caller is responsible for freeing the <err> and <str> copy
+ * memory area using free()
+ */
+struct list;
+int list_append_word(struct list *li, const char *str, char **err);
+
 #endif /* _COMMON_STANDARD_H */
diff --git a/src/haproxy.c b/src/haproxy.c
index 0c223e5..2cf36f5 100644
--- a/src/haproxy.c
+++ b/src/haproxy.c
@@ -33,10 +33,12 @@
 #include <ctype.h>
 #include <sys/time.h>
 #include <sys/types.h>
+#include <sys/stat.h>
 #include <sys/socket.h>
 #include <netinet/tcp.h>
 #include <netinet/in.h>
 #include <arpa/inet.h>
+#include <dirent.h>
 #include <netdb.h>
 #include <fcntl.h>
 #include <errno.h>
@@ -423,7 +425,7 @@ void usage(char *name)
 {
        display_version();
        fprintf(stderr,
-               "Usage : %s [-f <cfgfile>]* [ -vdV"
+               "Usage : %s [-f <cfgfile|cfgdir>]* [ -vdV"
                "D ] [ -n <maxconn> ] [ -N <maxpconn> ]\n"
                "        [ -p <pidfile> ] [ -m <max megs> ] [ -C <dir> ] [-- 
<cfgfile>*]\n"
                "        -v displays version ; -vv shows known build options.\n"
@@ -551,6 +553,99 @@ void dump(struct sig_handler *sh)
        pool_gc2();
 }
 
+/* This function check if cfg_cfgfiles containes directories.
+ * If it find one, it add all the files (and only files) it containes
+ * in cfg_cfgfiles in place of the directory (and remove the directory).
+ * It add the files in lexical order.
+ * It add only files with .cfg extension.
+ * It doesn't add files with name starting with '.'
+ */
+void cfgfiles_expand_directories(void)
+{
+       struct wordlist *wl, *wlb;
+       char *err = NULL;
+
+       list_for_each_entry_safe(wl, wlb, &cfg_cfgfiles, list) {
+               struct stat file_stat;
+               struct dirent **dir_entries = NULL;
+               int dir_entries_nb;
+               int dir_entries_it;
+
+               if (stat(wl->s, &file_stat)) {
+                       Alert("Cannot open configuration file/directory %s : 
%s\n",
+                             wl->s,
+                             strerror(errno));
+                       exit(1);
+               }
+
+               if (!S_ISDIR(file_stat.st_mode))
+                       continue;
+
+               /* from this point wl->s is a directory */
+
+               dir_entries_nb = scandir(wl->s, &dir_entries, NULL, alphasort);
+               if (dir_entries_nb < 0) {
+                       Alert("Cannot open configuration directory %s : %s\n",
+                             wl->s,
+                             strerror(errno));
+                       exit(1);
+               }
+
+               /* for each element in the directory wl->s */
+               for (dir_entries_it = 0; dir_entries_it < dir_entries_nb; 
dir_entries_it++) {
+                       struct dirent *dir_entry = dir_entries[dir_entries_it];
+                       char *filename = NULL;
+                       char *d_name_cfgext = strstr(dir_entry->d_name, ".cfg");
+
+                       /* don't add filename that begin with .
+                        * only add filename with .cfg extention
+                        */
+                       if (dir_entry->d_name[0] == '.' ||
+                           !(d_name_cfgext && d_name_cfgext[4] == '\0'))
+                               goto next_dir_entry;
+
+                       if (!memprintf(&filename, "%s/%s", wl->s, 
dir_entry->d_name)) {
+                               Alert("Cannot load configuration files %s : out 
of memory.\n",
+                                     filename);
+                               exit(1);
+                       }
+
+                       if (stat(filename, &file_stat)) {
+                               Alert("Cannot open configuration file %s : 
%s\n",
+                                     wl->s,
+                                     strerror(errno));
+                               exit(1);
+                       }
+
+                       /* don't add anything else than regular file in 
cfg_cfgfiles
+                        * this way we avoid loops
+                        */
+                       if (!S_ISREG(file_stat.st_mode))
+                               goto next_dir_entry;
+
+                       if (!list_append_word(&wl->list, filename, &err)) {
+                               Alert("Cannot load configuration files %s : 
%s\n",
+                                     filename,
+                                     err);
+                               exit(1);
+                       }
+
+next_dir_entry:
+                       free(filename);
+                       free(dir_entry);
+               }
+
+               free(dir_entries);
+
+               /* remove the current directory (wl) from cfg_cfgfiles */
+               free(wl->s);
+               LIST_DEL(&wl->list);
+               free(wl);
+       }
+
+       free(err);
+}
+
 /*
  * This function initializes all the necessary variables. It only returns
  * if everything is OK. If something fails, it exits.
@@ -561,6 +656,7 @@ void init(int argc, char **argv)
        char *tmp;
        char *cfg_pidfile = NULL;
        int err_code = 0;
+       char *err_msg = NULL;
        struct wordlist *wl;
        char *progname;
        char *change_dir = NULL;
@@ -713,13 +809,12 @@ void init(int argc, char **argv)
                                /* now that's a cfgfile list */
                                argv++; argc--;
                                while (argc > 0) {
-                                       wl = calloc(1, sizeof(*wl));
-                                       if (!wl) {
-                                               Alert("Cannot load 
configuration file %s : out of memory.\n", *argv);
+                                       if (!list_append_word(&cfg_cfgfiles, 
*argv, &err_msg)) {
+                                               Alert("Cannot load 
configuration file/directory %s : %s\n",
+                                                     *argv,
+                                                     err_msg);
                                                exit(1);
                                        }
-                                       wl->s = *argv;
-                                       LIST_ADDQ(&cfg_cfgfiles, &wl->list);
                                        argv++; argc--;
                                }
                                break;
@@ -736,13 +831,12 @@ void init(int argc, char **argv)
                                case 'N' : cfg_maxpconn = atol(*argv); break;
                                case 'L' : strncpy(localpeer, *argv, 
sizeof(localpeer) - 1); break;
                                case 'f' :
-                                       wl = calloc(1, sizeof(*wl));
-                                       if (!wl) {
-                                               Alert("Cannot load 
configuration file %s : out of memory.\n", *argv);
+                                       if (!list_append_word(&cfg_cfgfiles, 
*argv, &err_msg)) {
+                                               Alert("Cannot load 
configuration file/directory %s : %s\n",
+                                                     *argv,
+                                                     err_msg);
                                                exit(1);
                                        }
-                                       wl->s = *argv;
-                                       LIST_ADDQ(&cfg_cfgfiles, &wl->list);
                                        break;
                                case 'p' : cfg_pidfile = *argv; break;
                                default: usage(progname);
@@ -758,14 +852,17 @@ void init(int argc, char **argv)
                (arg_mode & (MODE_DAEMON | MODE_SYSTEMD | MODE_FOREGROUND | 
MODE_VERBOSE
                             | MODE_QUIET | MODE_CHECK | MODE_DEBUG));
 
-       if (LIST_ISEMPTY(&cfg_cfgfiles))
-               usage(progname);
-
        if (change_dir && chdir(change_dir) < 0) {
                Alert("Could not change to directory %s : %s\n", change_dir, 
strerror(errno));
                exit(1);
        }
 
+       /* handle cfgfiles that are actualy directories */
+       cfgfiles_expand_directories();
+
+       if (LIST_ISEMPTY(&cfg_cfgfiles))
+               usage(progname);
+
        global.maxsock = 10; /* reserve 10 fds ; will be incremented by socket 
eaters */
 
        init_default_instance();
@@ -1160,6 +1257,8 @@ void init(int argc, char **argv)
        /* initialize structures for name resolution */
        if (!dns_init_resolvers())
                exit(1);
+
+       free(err_msg);
 }
 
 static void deinit_acl_cond(struct acl_cond *cond)
@@ -1550,6 +1649,7 @@ void deinit(void)
                        free(log);
                }
        list_for_each_entry_safe(wl, wlb, &cfg_cfgfiles, list) {
+               free(wl->s);
                LIST_DEL(&wl->list);
                free(wl);
        }
diff --git a/src/standard.c b/src/standard.c
index a4d2097..cfed94d 100644
--- a/src/standard.c
+++ b/src/standard.c
@@ -3439,6 +3439,38 @@ unsigned char utf8_next(const char *s, int len, unsigned 
int *c)
        return code | ((p-(unsigned char *)s)&0x0f);
 }
 
+/* append a copy of string <str> (in a wordlist) at the end of the list <li>
+ * On failure : return 0 and <err> filled with an error message.
+ * The caller is responsible for freeing the <err> and <str> copy
+ * memory area using free()
+ */
+int list_append_word(struct list *li, const char *str, char **err)
+{
+       struct wordlist *wl;
+
+       wl = calloc(1, sizeof(*wl));
+       if (!wl) {
+               memprintf(err, "out of memory");
+               goto fail_wl;
+       }
+
+       wl->s = strdup(str);
+       if (!wl->s) {
+               memprintf(err, "out of memory");
+               goto fail_wl_s;
+       }
+
+       LIST_ADDQ(li, &wl->list);
+
+       return 1;
+
+fail_wl_s:
+       free(wl->s);
+fail_wl:
+       free(wl);
+       return 0;
+}
+
 /*
  * Local variables:
  *  c-indent-level: 8
-- 
2.8.2


Reply via email to