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 ++++++------ src/haproxy.c | 103 ++++++++++++++++++++++++++++++++++++++++++++++++++--- 3 files changed, 116 insertions(+), 18 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/src/haproxy.c b/src/haproxy.c index 0c223e5..9824b7c 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,94 @@ 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. + */ +void cfgfiles_expand_directories(void) +{ + struct wordlist *wl, *wlb; + + 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++) { + struct dirent *dir_entry = dir_entries[dir_entries_it]; + char *filename; + int filename_size = strlen(wl->s) + + 1 /* for '/' */ + + strlen(dir_entry->d_name); + + filename = calloc(filename_size + 1 /* for \0 */, sizeof(*filename)); + if (!filename) { + Alert("Cannot load configuration files %s : out of memory.\n", + filename); + exit(1); + } + + sprintf(filename, "%s/%s", wl->s, dir_entry->d_name); + + 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)) { + struct wordlist *wlt; + char *tmp_str; + + wlt = calloc(1, sizeof(*wlt)); + tmp_str = strdup(filename); + if (!wlt || !tmp_str) { + Alert("Cannot load configuration files %s : out of memory.\n", + filename); + exit(1); + } + + wlt->s = tmp_str; + + /* 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); + } +} + /* * This function initializes all the necessary variables. It only returns * if everything is OK. If something fails, it exits. @@ -738,7 +828,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 +848,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(); -- 2.8.2