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).
The -f order is still respected:

        $ tree rootdir
        rootdir
        ├── root1
        ├── root2
        ├── root3
        ├── superdir1
        │   ├── aaa1
        │   ├── aaa2
        │   ├── aaa3
        │   ├── bbb1 -> aaa1
        │   └── wrong
        │       └── wrongfile
        └── superdir2
            ├── ccc1
            └── wronglink -> .

        $ ./haproxy -C rootdir -f root2 -f superdir2 \
                               -f root3 -f superdir1 -f root1
        root2
        superdir2/ccc1
        root3
        superdir1/aaa1
        superdir1/aaa2
        superdir1/aaa3
        superdir1/bbb1
        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        | 23 ++++++------
 include/common/defaults.h |  7 ++++
 src/haproxy.c             | 89 +++++++++++++++++++++++++++++++++++++++++++----
 4 files changed, 108 insertions(+), 19 deletions(-)

diff --git a/doc/haproxy.1 b/doc/haproxy.1
index a836d5d..6017efa 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).
 
 .TP
 \fB\-L <name>\fP
diff --git a/doc/management.txt b/doc/management.txt
index e0469aa..3e51d49 100644
--- a/doc/management.txt
+++ b/doc/management.txt
@@ -134,16 +134,19 @@ list of options is :
     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.
+  -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. 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/defaults.h b/include/common/defaults.h
index 1c971d9..fefa30f 100644
--- a/include/common/defaults.h
+++ b/include/common/defaults.h
@@ -66,6 +66,13 @@
 #define MAX_SYSLOG_LEN          1024
 #endif
 
+/* maximum "directoryname/filename" string size when CLI
+ * -f argument is a directory (named "directoryname" here)
+ */
+#ifndef MAX_CLI_DIRFILE_NAME
+#define MAX_CLI_DIRFILE_NAME   1024
+#endif
+
 // maximum line size when parsing config
 #ifndef LINESIZE
 #define LINESIZE       2048
diff --git a/src/haproxy.c b/src/haproxy.c
index 0c223e5..29cd315 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"
@@ -561,7 +563,7 @@ void init(int argc, char **argv)
        char *tmp;
        char *cfg_pidfile = NULL;
        int err_code = 0;
-       struct wordlist *wl;
+       struct wordlist *wl, *wlb;
        char *progname;
        char *change_dir = NULL;
        struct proxy *px;
@@ -738,7 +740,7 @@ void init(int argc, char **argv)
                                case 'f' :
                                        wl = calloc(1, sizeof(*wl));
                                        if (!wl) {
-                                               Alert("Cannot load 
configuration file %s : out of memory.\n", *argv);
+                                               Alert("Cannot load 
configuration file/directory %s : out of memory.\n", *argv);
                                                exit(1);
                                        }
                                        wl->s = *argv;
@@ -758,14 +760,89 @@ 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 */
+       list_for_each_entry_safe(wl, wlb, &cfg_cfgfiles, list) {
+               struct stat file_stat;
+               struct dirent **dir_entries;
+               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;
+
+               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 (dir_entries_it = 0; dir_entries_it < dir_entries_nb; 
dir_entries_it++) {
+                       char filename[MAX_CLI_DIRFILE_NAME];
+                       struct dirent *dir_entry = dir_entries[dir_entries_it];
+
+                       strcpy(filename, wl->s);
+                       strcat(filename, "/");
+                       strcat(filename, dir_entry->d_name);
+
+                       if (stat(filename, &file_stat)) {
+                               Alert("Cannot open configuration file %s : 
%s\n",
+                                     filename,
+                                     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)) {
+                               struct wordlist *wlt;
+
+                               wlt = calloc(1, sizeof(*wlt));
+                               if (!wlt) {
+                                       Alert("Cannot load configuration files 
%s : out of memory.\n",
+                                             filename);
+                                       exit(1);
+                               }
+
+                               wlt->s = calloc(strlen(filename) + 1, 
sizeof(*wlt->s));
+                               if (!wlt->s) {
+                                       Alert("Cannot load configuration files 
%s : out of memory.\n",
+                                             filename);
+                                       exit(1);
+                               }
+                               strcpy(wlt->s, filename);
+
+                               /* add wlt (a file) before wl (a directory)
+                                * in cfg_cfgfiles
+                                */
+                               LIST_ADDQ(&wl->list, &wlt->list);
+                       }
+                       free(dir_entry);
+               }
+               free(dir_entries);
+
+               /* remove the current directory (wl) from cfg_cfgfiles */
+               LIST_DEL(&wl->list);
+               free(wl);
+       }
+
+       if (LIST_ISEMPTY(&cfg_cfgfiles))
+               usage(progname);
+
        global.maxsock = 10; /* reserve 10 fds ; will be incremented by socket 
eaters */
 
        init_default_instance();
-- 
2.8.2


Reply via email to