From dad36a3ee3289b55787c4c8003cd968e8a9b9c51 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Mon, 11 Mar 2013 01:20:04 +0100 Subject: [PATCH] MAJOR: tools: support environment variables in addresses Now that all addresses are parsed using str2sa_range(), it becomes easy to add support for environment variables and use them everywhere an address is needed. Environment variables are used as $VAR or ${VAR} as in shell. Any number of variables may compose an address, allowing various fantasies such as "fd@${FD_HTTP}" or "${LAN_DC1}.1:80". These ones are usable in logs, bind, servers, peers, stats socket, source, dispatch, and check address. --- doc/configuration.txt | 34 ++++++++++++++++ include/common/standard.h | 8 ++++ src/standard.c | 83 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 122 insertions(+), 3 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 86d88a9b0..525452765 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -562,6 +562,11 @@ log
[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. + must be one of the 24 standard syslog facilities : kern user mail daemon auth syslog lpr news @@ -1021,6 +1026,10 @@ peer : 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. + Example: peers mypeers peer haproxy1 192.168.0.1:1024 @@ -1619,6 +1628,10 @@ bind / [, ...] [param*] - 'fd@' -> use file descriptor 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. is either a unique TCP port, or a port range for which the proxy will accept connections for the IP address specified @@ -1675,6 +1688,9 @@ bind / [, ...] [param*] bind ipv4@public_ssl:443 ssl crt /etc/haproxy/site.pem bind unix@ssl-frontend.sock user root mode 600 accept-proxy + listen external_bind_app1 + bind fd@${FD_APP1} + See also : "source", "option forwardfor", "unix-bind" and the PROXY protocol documentation, and section 5 about bind options. @@ -2829,6 +2845,11 @@ 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. + must be one of the 24 standard syslog facilities : kern user mail daemon auth syslog lpr news @@ -2862,6 +2883,8 @@ 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-format Allows you to custom a log line. @@ -5094,6 +5117,10 @@ server
[:[port]] [param*] - 'ipv4@' -> address is always IPv4 - 'ipv6@' -> address is always IPv6 - 'unix@' -> address is a path to a local unix socket + 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. is an optional port specification. If set, all connections will be sent to this port. If unset, the same port the client @@ -5109,6 +5136,9 @@ server
[:[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 See also: "default-server", "http-send-name-header" and section 5 about server options @@ -5133,6 +5163,10 @@ source [:] [interface ] - 'ipv4@' -> address is always IPv4 - 'ipv6@' -> address is always IPv6 - 'unix@' -> address is a path to a local unix socket + 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. is an optional port. It is normally not needed but may be useful in some very specific contexts. The default value of zero means diff --git a/include/common/standard.h b/include/common/standard.h index f9f21b06a..8e88ee547 100644 --- a/include/common/standard.h +++ b/include/common/standard.h @@ -732,6 +732,14 @@ char *memprintf(char **out, const char *format, ...) */ char *indent_msg(char **out, int level); +/* Convert occurrences of environment variables in the input string to their + * corresponding value. A variable is identified as a series of alphanumeric + * characters or underscores following a '$' sign. The string must be + * free()able. NULL returns NULL. The resulting string might be reallocated if + * some expansion is made. + */ +char *env_expand(char *in); + /* debugging macro to emit messages using write() on fd #-1 so that strace sees * them. */ diff --git a/src/standard.c b/src/standard.c index cc22ba7fc..7a6ca1129 100644 --- a/src/standard.c +++ b/src/standard.c @@ -655,7 +655,7 @@ struct sockaddr_storage *str2sa_range(const char *str, int *low, int *high, char portl = porth = porta = 0; - str2 = back = strdup(str); + str2 = back = env_expand(strdup(str)); if (str2 == NULL) { memprintf(err, "out of memory in '%s'\n", __FUNCTION__); goto out; @@ -688,7 +688,7 @@ struct sockaddr_storage *str2sa_range(const char *str, int *low, int *high, char ((struct sockaddr_in *)&ss)->sin_addr.s_addr = strtol(str2, &endptr, 10); if (!*str2 || *endptr) { - memprintf(err, "file descriptor '%s' is not a valid integer\n", str2); + memprintf(err, "file descriptor '%s' is not a valid integer in '%s'\n", str2, str); goto out; } @@ -750,7 +750,7 @@ struct sockaddr_storage *str2sa_range(const char *str, int *low, int *high, char porta = porth; } else if (*port1) { /* other any unexpected char */ - memprintf(err, "invalid character '%c' in port number '%s'\n", *port1, port1); + memprintf(err, "invalid character '%c' in port number '%s' in '%s'\n", *port1, port1, str); goto out; } set_host_port(&ss, porta); @@ -1996,6 +1996,83 @@ char *indent_msg(char **out, int level) return ret; } +/* Convert occurrences of environment variables in the input string to their + * corresponding value. A variable is identified as a series of alphanumeric + * characters or underscores following a '$' sign. The string must be + * free()able. NULL returns NULL. The resulting string might be reallocated if + * some expansion is made. Variable names may also be enclosed into braces if + * needed (eg: to concatenate alphanum characters). + */ +char *env_expand(char *in) +{ + char *txt_beg; + char *out; + char *txt_end; + char *var_beg; + char *var_end; + char *value; + char *next; + int out_len; + int val_len; + + if (!in) + return in; + + value = out = NULL; + out_len = 0; + + txt_beg = in; + do { + /* look for next '$' sign in */ + for (txt_end = txt_beg; *txt_end && *txt_end != '$'; txt_end++); + + if (!*txt_end && !out) /* end and no expansion performed */ + return in; + + val_len = 0; + next = txt_end; + if (*txt_end == '$') { + char save; + + var_beg = txt_end + 1; + if (*var_beg == '{') + var_beg++; + + var_end = var_beg; + while (isalnum((int)(unsigned char)*var_end) || *var_end == '_') { + var_end++; + } + + next = var_end; + if (*var_end == '}' && (var_beg > txt_end + 1)) + next++; + + /* get value of the variable name at this location */ + save = *var_end; + *var_end = '\0'; + value = getenv(var_beg); + *var_end = save; + val_len = value ? strlen(value) : 0; + } + + out = realloc(out, out_len + (txt_end - txt_beg) + val_len + 1); + if (txt_end > txt_beg) { + memcpy(out + out_len, txt_beg, txt_end - txt_beg); + out_len += txt_end - txt_beg; + } + if (val_len) { + memcpy(out + out_len, value, val_len); + out_len += val_len; + } + out[out_len] = 0; + txt_beg = next; + } while (*txt_beg); + + /* here we know that was allocated and that we don't need anymore */ + free(in); + return out; +} + /* * Local variables: * c-indent-level: 8