MEDIUM: init: allow directory as argument of -f

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 (respecting LC_COLLATE).
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
        ||-- .6.cfg
        ||-- 1.cfg
        ||-- 2
        ||-- 3.cfg
        ||-- 4.cfg -> 1.cfg
        ||-- 5 -> 1.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.
This commit is contained in:
Maxime de Roucy 2016-05-13 23:52:56 +02:00 committed by Willy Tarreau
parent 0f503925f0
commit 379d9c7c14
3 changed files with 134 additions and 26 deletions

View File

@ -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 (respecting
LC_COLLATE) ; only non hidden files with ".cfg" extension are added.
.TP
\fB\-L <name>\fP

View File

@ -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.
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> : 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 (respecting LC_COLLATE) 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

View File

@ -31,6 +31,9 @@
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <dirent.h>
#include <locale.h>
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/socket.h>
@ -423,7 +426,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 +554,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.
@ -757,14 +853,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();
@ -1653,6 +1752,9 @@ int main(int argc, char **argv)
char errmsg[100];
int pidfd = -1;
/* get the locale from the environment variables */
setlocale(LC_ALL, "");
init(argc, argv);
signal_register_fct(SIGQUIT, dump, SIGQUIT);
signal_register_fct(SIGUSR1, sig_soft_stop, SIGUSR1);