]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: init: allow directory as argument of -f
authorMaxime de Roucy <maxime.deroucy@gmail.com>
Fri, 13 May 2016 21:52:56 +0000 (23:52 +0200)
committerWilly Tarreau <w@1wt.eu>
Sat, 14 May 2016 05:09:33 +0000 (07:09 +0200)
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
doc/management.txt
src/haproxy.c

index a836d5d1a9db60450a46e29dcfe40b8e68645673..cc9c702b3a0fe03b443d4ea782b839c638c046dc 100644 (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
index e0469aa622352d9ee18aee4f2b1e075d0c4eeb39..4f0af1062bc6096508b4daf69a695b65550beb1b 100644 (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.
-
-  -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 (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
index 3166bbaf9fdbcbb3fef0aa23891941d37eb2b581..582ad8203460abc1b1c4b2b9c1e644b7831a8c73 100644 (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);