]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: cfgparse: expand environment variables
authorWilliam Lallemand <wlallemand@haproxy.com>
Tue, 12 May 2015 12:27:13 +0000 (14:27 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 12 May 2015 13:28:20 +0000 (15:28 +0200)
Environment variables were expandables only in adresses.
Now there are expandables everywhere in the configuration file within
double quotes.

This patch breaks compatibility with the previous behavior of
environment variables in adresses, you must enclose adresses with double
quotes to make it work.

doc/configuration.txt
src/cfgparse.c

index 9c009432a9cf01de2bc0e3fef1708bc6cbb16fd9..c1e2741fb105d02ae8dc7e2542b34a2f69a8937b 100644 (file)
@@ -40,8 +40,9 @@ Summary
 2.    Configuring HAProxy
 2.1.      Configuration file format
 2.2.      Quoting and escaping
-2.3.      Time format
-2.4.      Examples
+2.3.      Environment variables
+2.4.      Time format
+2.5.      Examples
 
 3.    Global parameters
 3.1.      Process management and security
@@ -397,7 +398,11 @@ the interpretation of:
   '    single quote as a strong quoting delimiter
   #    hash as a comment start
 
-But interpretation of escaping and special characters are not prevented by weak
+Weak quoting permits the interpretation of variables, if you want to use a non
+-interpreted dollar within a double quoted string, you should escape it with a
+backslash ("\$"), it does not work outside weak quoting.
+
+Interpretation of escaping and special characters are not prevented by weak
 quoting.
 
 Strong quoting is achieved by using single quotes (''). Inside single quotes,
@@ -421,7 +426,26 @@ equivalent, it allows you to perform concatenation.
       reqrep "^([^ :]*)\ /static/(.*)"     "\1\ /\2"
 
 
-2.3. Time format
+2.3. Environment variables
+--------------------------
+
+HAProxy's configuration supports environment variables. Those variables are
+interpreted only within double quotes. Variables are expanded during the
+configuration parsing. Variable names must be preceded by a dollar ("$") and
+optionally enclosed with braces ("{}") similarly to what is done in Bourne
+shell. Variable names can contain alphanumerical characters or the character
+underscore ("_") but should not start with a digit.
+
+  Example:
+
+      bind "fd@${FD_APP1}"
+
+      log "${LOCAL_SYSLOG}:514" local0 notice   # send to local server
+
+      user "$HAPROXY_USER"
+
+
+2.4. Time format
 ----------------
 
 Some parameters involve values representing time, such as timeouts. These
@@ -640,10 +664,8 @@ log <address> [len <length>] <facility> [max level [min level]]
           the chroot) and uid/gid (be sure the path is appropriately
           writeable).
 
-        Any part of the address string may reference any number of environment
-        variables by preceding their name with a dollar sign ('$') and
-        optionally enclosing them with braces ('{}'), similarly to what is done
-        in Bourne shell.
+        You may want to reference some environment variables in the address
+        parameter, see section 2.3 about environment variables.
 
   <length> is an optional maximum line length. Log lines larger than this value
            will be truncated before being sent. The reason is that syslog
@@ -1318,9 +1340,8 @@ peer <peername> <ip>:<port>
   peer name. This makes it easier to maintain coherent configuration files
   across all peers.
 
-  Any part of the address string may reference any number of environment
-  variables by preceding their name with a dollar sign ('$') and optionally
-  enclosing them with braces ('{}'), similarly to what is done in Bourne shell.
+  You may want to reference some environment variables in the address
+  parameter, see section 2.3 about environment variables.
 
   Example:
     peers mypeers
@@ -2011,10 +2032,9 @@ bind /<path> [, ...] [param*]
                     - 'fd@<n>' -> use file descriptor <n> inherited from the
                       parent. The fd must be bound and may or may not already
                       be listening.
-                  Any part of the address string may reference any number of
-                  environment variables by preceding their name with a dollar
-                  sign ('$') and optionally enclosing them with braces ('{}'),
-                  similarly to what is done in Bourne shell.
+                  You may want to reference some environment variables in the
+                  address parameter, see section 2.3 about environment
+                  variables.
 
     <port_range>  is either a unique TCP port, or a port range for which the
                   proxy will accept connections for the IP address specified
@@ -2072,7 +2092,7 @@ bind /<path> [, ...] [param*]
             bind unix@ssl-frontend.sock user root mode 600 accept-proxy
 
         listen external_bind_app1
-            bind fd@${FD_APP1}
+            bind "fd@${FD_APP1}"
 
   See also : "source", "option forwardfor", "unix-bind" and the PROXY protocol
              documentation, and section 5 about bind options.
@@ -3846,10 +3866,8 @@ no log
                  inside the chroot) and uid/gid (be sure the path is
                  appropriately writeable).
 
-               Any part of the address string may reference any number of
-               environment variables by preceding their name with a dollar
-               sign ('$') and optionally enclosing them with braces ('{}'),
-               similarly to what is done in Bourne shell.
+              You may want to reference some environment variables in the
+              address parameter, see section 2.3 about environment variables.
 
     <length>   is an optional maximum line length. Log lines larger than this
                value will be truncated before being sent. The reason is that
@@ -3896,7 +3914,7 @@ no log
     log global
     log 127.0.0.1:514 local0 notice         # only send important events
     log 127.0.0.1:514 local0 notice notice  # same but limit output level
-    log ${LOCAL_SYSLOG}:514 local0 notice   # send to local server
+    log "${LOCAL_SYSLOG}:514" local0 notice   # send to local server
 
 
 log-format <string>
@@ -6525,10 +6543,9 @@ server <name> <address>[:[port]] [param*]
                     - 'ipv6@'  -> address is always IPv6
                     - 'unix@'  -> address is a path to a local unix socket
                     - 'abns@'  -> address is in abstract namespace (Linux only)
-              Any part of the address string may reference any number of
-              environment variables by preceding their name with a dollar
-              sign ('$') and optionally enclosing them with braces ('{}'),
-              similarly to what is done in Bourne shell.
+              You may want to reference some environment variables in the
+              address parameter, see section 2.3 about environment
+              variables.
 
     <port>    is an optional port specification. If set, all connections will
               be sent to this port. If unset, the same port the client
@@ -6544,9 +6561,9 @@ server <name> <address>[:[port]] [param*]
         server first  10.1.1.1:1080 cookie first  check inter 1000
         server second 10.1.1.2:1080 cookie second check inter 1000
         server transp ipv4@
-        server backup ${SRV_BACKUP}:1080 backup
-        server www1_dc1 ${LAN_DC1}.101:80
-        server www1_dc2 ${LAN_DC2}.101:80
+        server backup "${SRV_BACKUP}:1080" backup
+        server www1_dc1 "${LAN_DC1}.101:80"
+        server www1_dc2 "${LAN_DC2}.101:80"
 
   See also: "default-server", "http-send-name-header" and section 5 about
              server options
@@ -6572,10 +6589,8 @@ source <addr>[:<port>] [interface <name>]
                 - 'ipv6@' -> address is always IPv6
                 - 'unix@' -> address is a path to a local unix socket
                 - 'abns@' -> address is in abstract namespace (Linux only)
-              Any part of the address string may reference any number of
-              environment variables by preceding their name with a dollar
-              sign ('$') and optionally enclosing them with braces ('{}'),
-              similarly to what is done in Bourne shell.
+              You may want to reference some environment variables in the address
+              parameter, see section 2.3 about environment variables.
 
     <port>    is an optional port. It is normally not needed but may be useful
               in some very specific contexts. The default value of zero means
index 04cbd1d599599ffb0ceef9e5d57bac2eb28e0212..c18628a8f45856aa1e6e0280279f44a7b83ad967 100644 (file)
@@ -6284,7 +6284,7 @@ int readcfgfile(const char *file)
        int readbytes = 0;
 
        if ((thisline = malloc(sizeof(*thisline) * linesize)) == NULL) {
-               Alert("parsing [%s:%d] : out of memory.\n", file, linenum);
+               Alert("parsing [%s] : out of memory.\n", file);
                return -1;
        }
 
@@ -6304,6 +6304,7 @@ int readcfgfile(const char *file)
        if ((f=fopen(file,"r")) == NULL)
                return -1;
 
+next_line:
        while (fgets(thisline + readbytes, linesize - readbytes, f) != NULL) {
                int arg, kwm = KWM_STD;
                char *end;
@@ -6404,6 +6405,9 @@ int readcfgfile(const char *file)
                                } else if (line[1] == '\'') {
                                        *line = '\'';
                                        skip = 1;
+                               } else if (line[1] == '$' && dquote) { /* escaping of $ only inside double quotes */
+                                       *line = '$';
+                                       skip = 1;
                                }
                                if (skip) {
                                        memmove(line + 1, line + 1 + skip, end - (line + skip));
@@ -6423,10 +6427,95 @@ int readcfgfile(const char *file)
                                        line++;
                                args[++arg] = line;
                        }
+                       else if (dquote && *line == '$') {
+                               /* environment variables are evaluated inside double quotes */
+                               char *var_beg;
+                               char *var_end;
+                               char save_char;
+                               char *value;
+                               int val_len;
+                               int newlinesize;
+                               int braces = 0;
+
+                               var_beg = line + 1;
+                               var_end = var_beg;
+
+                               if (*var_beg == '{') {
+                                       var_beg++;
+                                       var_end++;
+                                       braces = 1;
+                               }
+
+                               if (!isalpha((int)(unsigned char)*var_beg) && *var_beg != '_') {
+                                       Alert("parsing [%s:%d] : Variable expansion: Unrecognized character '%c' in variable name.\n", file, linenum, *var_beg);
+                                       err_code |= ERR_ALERT | ERR_FATAL;
+                                       goto next_line; /* skip current line */
+                               }
+
+                               while (isalnum((int)(unsigned char)*var_end) || *var_end == '_')
+                                       var_end++;
+
+                               save_char = *var_end;
+                               *var_end = '\0';
+                               value = getenv(var_beg);
+                               *var_end = save_char;
+                               val_len = value ? strlen(value) : 0;
+
+                               if (braces) {
+                                       if (*var_end == '}') {
+                                               var_end++;
+                                               braces = 0;
+                                       } else {
+                                               Alert("parsing [%s:%d] : Variable expansion: Mismatched braces.\n", file, linenum);
+                                               err_code |= ERR_ALERT | ERR_FATAL;
+                                               goto next_line; /* skip current line */
+                                       }
+                               }
+
+                               newlinesize = (end - thisline) - (var_end - line) + val_len + 1;
+
+                               /* if not enough space in thisline */
+                               if (newlinesize  > linesize) {
+                                       char *newline;
+
+                                       newline = realloc(thisline, newlinesize * sizeof(*thisline));
+                                       if (newline == NULL) {
+                                               Alert("parsing [%s:%d] : Variable expansion: Not enough memory.\n", file, linenum);
+                                               err_code |= ERR_ALERT | ERR_FATAL;
+                                               goto next_line; /* slip current line */
+                                       }
+                                       /* recompute pointers if realloc returns a new pointer */
+                                       if (newline != thisline) {
+                                               int i;
+                                               int diff;
+
+                                               for (i = 0; i <= arg; i++) {
+                                                       diff = args[i] - thisline;
+                                                       args[i] = newline + diff;
+                                               }
+
+                                               diff = var_end - thisline;
+                                               var_end = newline + diff;
+                                               diff = end - thisline;
+                                               end = newline + diff;
+                                               diff = line - thisline;
+                                               line = newline + diff;
+                                               thisline = newline;
+                                       }
+                                       linesize = newlinesize;
+                               }
+
+                               /* insert value inside the line */
+                               memmove(line + val_len, var_end, end - var_end + 1);
+                               memcpy(line, value, val_len);
+                               end += val_len - (var_end - line);
+                               line += val_len;
+                       }
                        else {
                                line++;
                        }
                }
+
                if (dquote) {
                        Alert("parsing [%s:%d] : Mismatched double quotes.\n", file, linenum);
                        err_code |= ERR_ALERT | ERR_FATAL;