From 379d9c7c14e684ab1dcdb6467a6bf189153c2b1d Mon Sep 17 00:00:00 2001 From: Maxime de Roucy Date: Fri, 13 May 2016 23:52:56 +0200 Subject: [PATCH] MEDIUM: init: allow directory as argument of -f MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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. --- doc/haproxy.1 | 8 ++-- doc/management.txt | 44 +++++++++--------- src/haproxy.c | 110 +++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 135 insertions(+), 27 deletions(-) diff --git a/doc/haproxy.1 b/doc/haproxy.1 index a836d5d1a9..cc9c702b3a 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 [\-L\ ] [\-n\ maxconn] [\-N\ maxconn] [\-C\ ] [\-v|\-vv] [\-d] [\-D] [\-q] [\-V] [\-c] [\-p\ ] [\-dk] [\-ds] [\-de] [\-dp] [\-db] [\-dM[]] [\-m\ ] [{\-sf|\-st}\ pidlist...] +haproxy \-f [\-L\ ] [\-n\ maxconn] [\-N\ maxconn] [\-C\ ] [\-v|\-vv] [\-d] [\-D] [\-q] [\-V] [\-c] [\-p\ ] [\-dk] [\-ds] [\-de] [\-dp] [\-db] [\-dM[]] [\-m\ ] [{\-sf|\-st}\ pidlist...] .SH DESCRIPTION @@ -33,8 +33,10 @@ instances without risking the system's stability. .SH OPTIONS .TP -\fB\-f \fP -Specify configuration file path. +\fB\-f \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 \fP diff --git a/doc/management.txt b/doc/management.txt index e0469aa622..4f0af1062b 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 : -- * : 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 : adds 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 : adds to the list of configuration files to be + loaded. If 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 : changes to directory 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 3166bbaf9f..582ad82034 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -31,6 +31,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -423,7 +426,7 @@ void usage(char *name) { display_version(); fprintf(stderr, - "Usage : %s [-f ]* [ -vdV" + "Usage : %s [-f ]* [ -vdV" "D ] [ -n ] [ -N ]\n" " [ -p ] [ -m ] [ -C ] [-- *]\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); -- 2.39.5