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