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 .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 .SH DESCRIPTION
@ -33,8 +33,10 @@ instances without risking the system's stability.
.SH OPTIONS .SH OPTIONS
.TP .TP
\fB\-f <configuration file>\fP \fB\-f <configuration file|dir>\fP
Specify configuration file path. 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 .TP
\fB\-L <name>\fP \fB\-L <name>\fP

View File

@ -124,26 +124,30 @@ enforce some settings without touching the configuration files. The current
list of options is : list of options is :
-- <cfgfile>* : all the arguments following "--" are paths to configuration -- <cfgfile>* : all the arguments following "--" are paths to configuration
file to be loaded and processed in the declaration order. It is mostly file/directory to be loaded and processed in the declaration order. It is
useful when relying on the shell to load many files that are numerically mostly useful when relying on the shell to load many files that are
ordered. See also "-f". The difference between "--" and "-f" is that one numerically ordered. See also "-f". The difference between "--" and "-f" is
"-f" must be placed before each file name, while a single "--" is needed that one "-f" must be placed before each file name, while a single "--" is
before all file names. Both options can be used together, the command line needed before all file names. Both options can be used together, the
ordering still applies. When more than one file is specified, each file command line ordering still applies. When more than one file is specified,
must start on a section boundary, so the first keyword of each file must be each file must start on a section boundary, so the first keyword of each
one of "global", "defaults", "peers", "listen", "frontend", "backend", and file must be one of "global", "defaults", "peers", "listen", "frontend",
so on. A file cannot contain just a server list for example. "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 -f <cfgfile|cfgdir> : adds <cfgfile> to the list of configuration files to be
loaded. Configuration files are loaded and processed in their declaration loaded. If <cfgdir> is a directory, all the files (and only files) it
order. This option may be specified multiple times to load multiple files. containes are added in lexical order (respecting LC_COLLATE) to the list of
See also "--". The difference between "--" and "-f" is that one "-f" must configuration files to be loaded ; only files with ".cfg" extension are
be placed before each file name, while a single "--" is needed before all added, only non hidden files (not prefixed with ".") are added.
file names. Both options can be used together, the command line ordering Configuration files are loaded and processed in their declaration order.
still applies. When more than one file is specified, each file must start This option may be specified multiple times to load multiple files. See
on a section boundary, so the first keyword of each file must be one of also "--". The difference between "--" and "-f" is that one "-f" must be
"global", "defaults", "peers", "listen", "frontend", "backend", and so placed before each file name, while a single "--" is needed before all file
on. A file cannot contain just a server list for example. 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 -C <dir> : changes to directory <dir> before loading configuration
files. This is useful when using relative paths. Warning when using files. This is useful when using relative paths. Warning when using

View File

@ -31,6 +31,9 @@
#include <unistd.h> #include <unistd.h>
#include <string.h> #include <string.h>
#include <ctype.h> #include <ctype.h>
#include <dirent.h>
#include <locale.h>
#include <sys/stat.h>
#include <sys/time.h> #include <sys/time.h>
#include <sys/types.h> #include <sys/types.h>
#include <sys/socket.h> #include <sys/socket.h>
@ -423,7 +426,7 @@ void usage(char *name)
{ {
display_version(); display_version();
fprintf(stderr, fprintf(stderr,
"Usage : %s [-f <cfgfile>]* [ -vdV" "Usage : %s [-f <cfgfile|cfgdir>]* [ -vdV"
"D ] [ -n <maxconn> ] [ -N <maxpconn> ]\n" "D ] [ -n <maxconn> ] [ -N <maxpconn> ]\n"
" [ -p <pidfile> ] [ -m <max megs> ] [ -C <dir> ] [-- <cfgfile>*]\n" " [ -p <pidfile> ] [ -m <max megs> ] [ -C <dir> ] [-- <cfgfile>*]\n"
" -v displays version ; -vv shows known build options.\n" " -v displays version ; -vv shows known build options.\n"
@ -551,6 +554,99 @@ void dump(struct sig_handler *sh)
pool_gc2(); 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 * This function initializes all the necessary variables. It only returns
* if everything is OK. If something fails, it exits. * 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 (arg_mode & (MODE_DAEMON | MODE_SYSTEMD | MODE_FOREGROUND | MODE_VERBOSE
| MODE_QUIET | MODE_CHECK | MODE_DEBUG)); | MODE_QUIET | MODE_CHECK | MODE_DEBUG));
if (LIST_ISEMPTY(&cfg_cfgfiles))
usage(progname);
if (change_dir && chdir(change_dir) < 0) { if (change_dir && chdir(change_dir) < 0) {
Alert("Could not change to directory %s : %s\n", change_dir, strerror(errno)); Alert("Could not change to directory %s : %s\n", change_dir, strerror(errno));
exit(1); 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 */ global.maxsock = 10; /* reserve 10 fds ; will be incremented by socket eaters */
init_default_instance(); init_default_instance();
@ -1653,6 +1752,9 @@ int main(int argc, char **argv)
char errmsg[100]; char errmsg[100];
int pidfd = -1; int pidfd = -1;
/* get the locale from the environment variables */
setlocale(LC_ALL, "");
init(argc, argv); init(argc, argv);
signal_register_fct(SIGQUIT, dump, SIGQUIT); signal_register_fct(SIGQUIT, dump, SIGQUIT);
signal_register_fct(SIGUSR1, sig_soft_stop, SIGUSR1); signal_register_fct(SIGUSR1, sig_soft_stop, SIGUSR1);