blob: 03aade21f7832defd1a4084332b1d4c06d027e69 [file] [log] [blame]
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001#include <ctype.h>
2
Willy Tarreau4c7e4b72020-05-27 12:58:42 +02003#include <haproxy/api.h>
Willy Tarreaub2551052020-06-09 09:07:15 +02004#include <haproxy/arg.h>
Willy Tarreauc35eb382021-03-26 14:51:31 +01005#include <haproxy/buf.h>
Willy Tarreau6be78492020-06-05 00:00:29 +02006#include <haproxy/cfgparse.h>
Willy Tarreau4aa573d2020-06-04 18:21:56 +02007#include <haproxy/check.h>
Willy Tarreauc35eb382021-03-26 14:51:31 +01008#include <haproxy/cli.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +02009#include <haproxy/global.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020010#include <haproxy/http.h>
Willy Tarreauc761f842020-06-04 11:40:28 +020011#include <haproxy/http_rules.h>
Willy Tarreau853b2972020-05-27 18:01:47 +020012#include <haproxy/list.h>
Willy Tarreau36979d92020-06-05 17:27:29 +020013#include <haproxy/log.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020014#include <haproxy/sample.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020015#include <haproxy/stream-t.h>
Willy Tarreau8b550af2020-06-04 17:42:48 +020016#include <haproxy/tcp_rules.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020017#include <haproxy/tcpcheck.h>
Willy Tarreaua1718922020-06-04 16:25:31 +020018#include <haproxy/vars.h>
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020019
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020020
21/* This contains a pool of struct vars */
Willy Tarreau8ceae722018-11-26 11:58:30 +010022DECLARE_STATIC_POOL(var_pool, "vars", sizeof(struct var));
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020023
24/* This array contain all the names of all the HAProxy vars.
25 * This permits to identify two variables name with
26 * only one pointer. It permits to not using strdup() for
27 * each variable name used during the runtime.
28 */
29static char **var_names = NULL;
30static int var_names_nb = 0;
31
32/* This array of int contains the system limits per context. */
33static unsigned int var_global_limit = 0;
34static unsigned int var_global_size = 0;
Christopher Fauletff2613e2016-11-09 11:36:17 +010035static unsigned int var_proc_limit = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020036static unsigned int var_sess_limit = 0;
37static unsigned int var_txn_limit = 0;
38static unsigned int var_reqres_limit = 0;
Gaetan Rivet13a50432020-02-21 18:13:44 +010039static unsigned int var_check_limit = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020040
Willy Tarreau86abe442018-11-25 20:12:18 +010041__decl_rwlock(var_names_rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +020042
Willy Tarreauf37b1402019-06-04 16:27:36 +020043/* returns the struct vars pointer for a session, stream and scope, or NULL if
44 * it does not exist.
45 */
46static inline struct vars *get_vars(struct session *sess, struct stream *strm, enum vars_scope scope)
47{
48 switch (scope) {
49 case SCOPE_PROC:
50 return &global.vars;
51 case SCOPE_SESS:
Willy Tarreaua07d61b2021-03-26 11:27:59 +010052 return sess ? &sess->vars : NULL;
Gaetan Rivet13a50432020-02-21 18:13:44 +010053 case SCOPE_CHECK: {
Christopher Faulet0fca7ed2020-04-21 11:53:32 +020054 struct check *check = objt_check(sess->origin);
Gaetan Rivet13a50432020-02-21 18:13:44 +010055
Christopher Faulet0fca7ed2020-04-21 11:53:32 +020056 return check ? &check->vars : NULL;
Gaetan Rivet13a50432020-02-21 18:13:44 +010057 }
Willy Tarreauf37b1402019-06-04 16:27:36 +020058 case SCOPE_TXN:
59 return strm ? &strm->vars_txn : NULL;
60 case SCOPE_REQ:
61 case SCOPE_RES:
62 default:
63 return strm ? &strm->vars_reqres : NULL;
64 }
65}
66
Willy Tarreau72330982015-06-19 11:21:56 +020067/* This function adds or remove memory size from the accounting. The inner
68 * pointers may be null when setting the outer ones only.
69 */
Miroslav Zagorac6deab792020-12-09 16:34:29 +010070void var_accounting_diff(struct vars *vars, struct session *sess, struct stream *strm, int size)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020071{
72 switch (vars->scope) {
73 case SCOPE_REQ:
74 case SCOPE_RES:
Willy Tarreauf37b1402019-06-04 16:27:36 +020075 if (strm)
76 _HA_ATOMIC_ADD(&strm->vars_reqres.size, size);
Willy Tarreau6204cd92016-03-10 16:33:04 +010077 /* fall through */
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020078 case SCOPE_TXN:
Willy Tarreauf37b1402019-06-04 16:27:36 +020079 if (strm)
80 _HA_ATOMIC_ADD(&strm->vars_txn.size, size);
Gaetan Rivet13a50432020-02-21 18:13:44 +010081 goto scope_sess;
82 case SCOPE_CHECK: {
Christopher Faulet0fca7ed2020-04-21 11:53:32 +020083 struct check *check = objt_check(sess->origin);
Gaetan Rivet13a50432020-02-21 18:13:44 +010084
Christopher Faulet0fca7ed2020-04-21 11:53:32 +020085 if (check)
86 _HA_ATOMIC_ADD(&check->vars.size, size);
Gaetan Rivet13a50432020-02-21 18:13:44 +010087 }
Willy Tarreau6204cd92016-03-10 16:33:04 +010088 /* fall through */
Gaetan Rivet13a50432020-02-21 18:13:44 +010089scope_sess:
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020090 case SCOPE_SESS:
Olivier Houchard25ad13f2019-03-08 18:55:38 +010091 _HA_ATOMIC_ADD(&sess->vars.size, size);
Christopher Fauletff2613e2016-11-09 11:36:17 +010092 /* fall through */
93 case SCOPE_PROC:
Olivier Houchard25ad13f2019-03-08 18:55:38 +010094 _HA_ATOMIC_ADD(&global.vars.size, size);
95 _HA_ATOMIC_ADD(&var_global_size, size);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020096 }
97}
98
99/* This function returns 1 if the <size> is available in the var
Joseph Herlant07676892018-11-15 09:19:50 -0800100 * pool <vars>, otherwise returns 0. If the space is available,
Willy Tarreau72330982015-06-19 11:21:56 +0200101 * the size is reserved. The inner pointers may be null when setting
Willy Tarreau6204cd92016-03-10 16:33:04 +0100102 * the outer ones only. The accounting uses either <sess> or <strm>
103 * depending on the scope. <strm> may be NULL when no stream is known
104 * and only the session exists (eg: tcp-request connection).
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200105 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100106static int var_accounting_add(struct vars *vars, struct session *sess, struct stream *strm, int size)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200107{
108 switch (vars->scope) {
109 case SCOPE_REQ:
110 case SCOPE_RES:
Willy Tarreauf37b1402019-06-04 16:27:36 +0200111 if (var_reqres_limit && strm && strm->vars_reqres.size + size > var_reqres_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200112 return 0;
Willy Tarreau6204cd92016-03-10 16:33:04 +0100113 /* fall through */
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200114 case SCOPE_TXN:
Willy Tarreauf37b1402019-06-04 16:27:36 +0200115 if (var_txn_limit && strm && strm->vars_txn.size + size > var_txn_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200116 return 0;
Gaetan Rivet13a50432020-02-21 18:13:44 +0100117 goto scope_sess;
118 case SCOPE_CHECK: {
Christopher Faulet0fca7ed2020-04-21 11:53:32 +0200119 struct check *check = objt_check(sess->origin);
Gaetan Rivet13a50432020-02-21 18:13:44 +0100120
Christopher Faulet0fca7ed2020-04-21 11:53:32 +0200121 if (var_check_limit && check && check->vars.size + size > var_check_limit)
Gaetan Rivet13a50432020-02-21 18:13:44 +0100122 return 0;
123 }
Willy Tarreau6204cd92016-03-10 16:33:04 +0100124 /* fall through */
Gaetan Rivet13a50432020-02-21 18:13:44 +0100125scope_sess:
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200126 case SCOPE_SESS:
Willy Tarreau6204cd92016-03-10 16:33:04 +0100127 if (var_sess_limit && sess->vars.size + size > var_sess_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200128 return 0;
Christopher Fauletff2613e2016-11-09 11:36:17 +0100129 /* fall through */
130 case SCOPE_PROC:
131 if (var_proc_limit && global.vars.size + size > var_proc_limit)
132 return 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200133 if (var_global_limit && var_global_size + size > var_global_limit)
134 return 0;
135 }
Willy Tarreau6204cd92016-03-10 16:33:04 +0100136 var_accounting_diff(vars, sess, strm, size);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200137 return 1;
138}
139
Christopher Faulet85d79c92016-11-09 16:54:56 +0100140/* This fnuction remove a variable from the list and free memory it used */
141unsigned int var_clear(struct var *var)
142{
143 unsigned int size = 0;
144
145 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
Willy Tarreau5b52b002021-02-26 21:19:53 +0100146 ha_free(&var->data.u.str.area);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200147 size += var->data.u.str.data;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100148 }
Christopher Fauletd02210c2017-07-24 16:24:39 +0200149 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
Willy Tarreau5b52b002021-02-26 21:19:53 +0100150 ha_free(&var->data.u.meth.str.area);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200151 size += var->data.u.meth.str.data;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100152 }
Willy Tarreau2b718102021-04-21 07:32:39 +0200153 LIST_DELETE(&var->l);
Willy Tarreaubafbe012017-11-24 17:34:44 +0100154 pool_free(var_pool, var);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100155 size += sizeof(struct var);
156 return size;
157}
158
Joseph Herlant07676892018-11-15 09:19:50 -0800159/* This function free all the memory used by all the variables
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200160 * in the list.
161 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100162void vars_prune(struct vars *vars, struct session *sess, struct stream *strm)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200163{
164 struct var *var, *tmp;
Willy Tarreau72330982015-06-19 11:21:56 +0200165 unsigned int size = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200166
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100167 HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200168 list_for_each_entry_safe(var, tmp, &vars->head, l) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100169 size += var_clear(var);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200170 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100171 HA_RWLOCK_WRUNLOCK(VARS_LOCK, &vars->rwlock);
Willy Tarreau6204cd92016-03-10 16:33:04 +0100172 var_accounting_diff(vars, sess, strm, -size);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200173}
174
Willy Tarreauebcd4842015-06-19 11:59:02 +0200175/* This function frees all the memory used by all the session variables in the
176 * list starting at <vars>.
177 */
178void vars_prune_per_sess(struct vars *vars)
179{
180 struct var *var, *tmp;
181 unsigned int size = 0;
182
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100183 HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock);
Willy Tarreauebcd4842015-06-19 11:59:02 +0200184 list_for_each_entry_safe(var, tmp, &vars->head, l) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100185 size += var_clear(var);
Willy Tarreauebcd4842015-06-19 11:59:02 +0200186 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100187 HA_RWLOCK_WRUNLOCK(VARS_LOCK, &vars->rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200188
Olivier Houchard25ad13f2019-03-08 18:55:38 +0100189 _HA_ATOMIC_SUB(&vars->size, size);
190 _HA_ATOMIC_SUB(&global.vars.size, size);
191 _HA_ATOMIC_SUB(&var_global_size, size);
Willy Tarreauebcd4842015-06-19 11:59:02 +0200192}
193
Ilya Shipitsin46a030c2020-07-05 16:36:08 +0500194/* This function init a list of variables. */
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200195void vars_init(struct vars *vars, enum vars_scope scope)
196{
197 LIST_INIT(&vars->head);
198 vars->scope = scope;
199 vars->size = 0;
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100200 HA_RWLOCK_INIT(&vars->rwlock);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200201}
202
203/* This function declares a new variable name. It returns a pointer
204 * on the string identifying the name. This function assures that
205 * the same name exists only once.
206 *
207 * This function check if the variable name is acceptable.
208 *
209 * The function returns NULL if an error occurs, and <err> is filled.
210 * In this case, the HAProxy must be stopped because the structs are
211 * left inconsistent. Otherwise, it returns the pointer on the global
212 * name.
213 */
Christopher Faulet09c9df22016-10-31 11:05:37 +0100214static char *register_name(const char *name, int len, enum vars_scope *scope,
215 int alloc, char **err)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200216{
217 int i;
Hubert Verstraete831962e2016-06-28 22:44:26 +0200218 char **var_names2;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200219 const char *tmp;
Christopher Faulete95f2c32017-07-24 16:30:34 +0200220 char *res = NULL;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200221
222 /* Check length. */
223 if (len == 0) {
224 memprintf(err, "Empty variable name cannot be accepted");
Christopher Fauleteb3e2762017-12-08 09:17:39 +0100225 return res;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200226 }
227
228 /* Check scope. */
Christopher Fauletff2613e2016-11-09 11:36:17 +0100229 if (len > 5 && strncmp(name, "proc.", 5) == 0) {
230 name += 5;
231 len -= 5;
232 *scope = SCOPE_PROC;
233 }
234 else if (len > 5 && strncmp(name, "sess.", 5) == 0) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200235 name += 5;
236 len -= 5;
237 *scope = SCOPE_SESS;
238 }
239 else if (len > 4 && strncmp(name, "txn.", 4) == 0) {
240 name += 4;
241 len -= 4;
242 *scope = SCOPE_TXN;
243 }
244 else if (len > 4 && strncmp(name, "req.", 4) == 0) {
245 name += 4;
246 len -= 4;
247 *scope = SCOPE_REQ;
248 }
249 else if (len > 4 && strncmp(name, "res.", 4) == 0) {
250 name += 4;
251 len -= 4;
252 *scope = SCOPE_RES;
253 }
Gaetan Rivet13a50432020-02-21 18:13:44 +0100254 else if (len > 6 && strncmp(name, "check.", 6) == 0) {
255 name += 6;
256 len -= 6;
257 *scope = SCOPE_CHECK;
258 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200259 else {
260 memprintf(err, "invalid variable name '%s'. A variable name must be start by its scope. "
Gaetan Rivet13a50432020-02-21 18:13:44 +0100261 "The scope can be 'proc', 'sess', 'txn', 'req', 'res' or 'check'", name);
Christopher Fauleteb3e2762017-12-08 09:17:39 +0100262 return res;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200263 }
264
Christopher Faulete95f2c32017-07-24 16:30:34 +0200265 if (alloc)
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100266 HA_RWLOCK_WRLOCK(VARS_LOCK, &var_names_rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200267 else
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100268 HA_RWLOCK_RDLOCK(VARS_LOCK, &var_names_rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200269
270
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200271 /* Look for existing variable name. */
272 for (i = 0; i < var_names_nb; i++)
Christopher Faulete95f2c32017-07-24 16:30:34 +0200273 if (strncmp(var_names[i], name, len) == 0 && var_names[i][len] == '\0') {
274 res = var_names[i];
275 goto end;
276 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200277
Christopher Faulete95f2c32017-07-24 16:30:34 +0200278 if (!alloc) {
279 res = NULL;
280 goto end;
281 }
Christopher Faulet09c9df22016-10-31 11:05:37 +0100282
Hubert Verstraete831962e2016-06-28 22:44:26 +0200283 /* Store variable name. If realloc fails, var_names remains valid */
284 var_names2 = realloc(var_names, (var_names_nb + 1) * sizeof(*var_names));
285 if (!var_names2) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200286 memprintf(err, "out of memory error");
Christopher Faulete95f2c32017-07-24 16:30:34 +0200287 res = NULL;
288 goto end;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200289 }
Hubert Verstraete831962e2016-06-28 22:44:26 +0200290 var_names_nb++;
291 var_names = var_names2;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200292 var_names[var_names_nb - 1] = malloc(len + 1);
293 if (!var_names[var_names_nb - 1]) {
294 memprintf(err, "out of memory error");
Christopher Faulete95f2c32017-07-24 16:30:34 +0200295 res = NULL;
296 goto end;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200297 }
298 memcpy(var_names[var_names_nb - 1], name, len);
299 var_names[var_names_nb - 1][len] = '\0';
300
301 /* Check variable name syntax. */
302 tmp = var_names[var_names_nb - 1];
303 while (*tmp) {
Willy Tarreau90807112020-02-25 08:16:33 +0100304 if (!isalnum((unsigned char)*tmp) && *tmp != '_' && *tmp != '.') {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200305 memprintf(err, "invalid syntax at char '%s'", tmp);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200306 res = NULL;
307 goto end;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200308 }
309 tmp++;
310 }
Christopher Faulete95f2c32017-07-24 16:30:34 +0200311 res = var_names[var_names_nb - 1];
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200312
Christopher Faulete95f2c32017-07-24 16:30:34 +0200313 end:
314 if (alloc)
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100315 HA_RWLOCK_WRUNLOCK(VARS_LOCK, &var_names_rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200316 else
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100317 HA_RWLOCK_RDUNLOCK(VARS_LOCK, &var_names_rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200318
319 return res;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200320}
321
322/* This function returns an existing variable or returns NULL. */
323static inline struct var *var_get(struct vars *vars, const char *name)
324{
325 struct var *var;
326
327 list_for_each_entry(var, &vars->head, l)
328 if (var->name == name)
329 return var;
330 return NULL;
331}
332
333/* Returns 0 if fails, else returns 1. */
334static int smp_fetch_var(const struct arg *args, struct sample *smp, const char *kw, void *private)
335{
336 const struct var_desc *var_desc = &args[0].data.var;
Christopher Faulete95f2c32017-07-24 16:30:34 +0200337
Dragan Dosenec0a6042021-02-22 17:39:02 +0100338 return vars_get_by_desc(var_desc, smp);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200339}
340
341/* This function search in the <head> a variable with the same
342 * pointer value that the <name>. If the variable doesn't exists,
343 * create it. The function stores a copy of smp> if the variable.
344 * It returns 0 if fails, else returns 1.
345 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100346static int sample_store(struct vars *vars, const char *name, struct sample *smp)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200347{
348 struct var *var;
349
350 /* Look for existing variable name. */
351 var = var_get(vars, name);
352
353 if (var) {
354 /* free its used memory. */
355 if (var->data.type == SMP_T_STR ||
356 var->data.type == SMP_T_BIN) {
Willy Tarreau5b52b002021-02-26 21:19:53 +0100357 ha_free(&var->data.u.str.area);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200358 var_accounting_diff(vars, smp->sess, smp->strm,
359 -var->data.u.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200360 }
Christopher Fauletd02210c2017-07-24 16:24:39 +0200361 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
Willy Tarreau5b52b002021-02-26 21:19:53 +0100362 ha_free(&var->data.u.meth.str.area);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200363 var_accounting_diff(vars, smp->sess, smp->strm,
364 -var->data.u.meth.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200365 }
366 } else {
367
Joseph Herlant07676892018-11-15 09:19:50 -0800368 /* Check memory available. */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100369 if (!var_accounting_add(vars, smp->sess, smp->strm, sizeof(struct var)))
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200370 return 0;
371
372 /* Create new entry. */
Willy Tarreaubafbe012017-11-24 17:34:44 +0100373 var = pool_alloc(var_pool);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200374 if (!var)
375 return 0;
Willy Tarreau2b718102021-04-21 07:32:39 +0200376 LIST_APPEND(&vars->head, &var->l);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200377 var->name = name;
378 }
379
380 /* Set type. */
Thierry FOURNIER8c542ca2015-08-19 09:00:18 +0200381 var->data.type = smp->data.type;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200382
383 /* Copy data. If the data needs memory, the function can fail. */
384 switch (var->data.type) {
385 case SMP_T_BOOL:
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200386 case SMP_T_SINT:
Thierry FOURNIER136f9d32015-08-19 09:07:19 +0200387 var->data.u.sint = smp->data.u.sint;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200388 break;
389 case SMP_T_IPV4:
Thierry FOURNIER136f9d32015-08-19 09:07:19 +0200390 var->data.u.ipv4 = smp->data.u.ipv4;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200391 break;
392 case SMP_T_IPV6:
Thierry FOURNIER136f9d32015-08-19 09:07:19 +0200393 var->data.u.ipv6 = smp->data.u.ipv6;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200394 break;
395 case SMP_T_STR:
396 case SMP_T_BIN:
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200397 if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.str.data)) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200398 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
399 return 0;
400 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200401 var->data.u.str.area = malloc(smp->data.u.str.data);
402 if (!var->data.u.str.area) {
403 var_accounting_diff(vars, smp->sess, smp->strm,
404 -smp->data.u.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200405 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
406 return 0;
407 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200408 var->data.u.str.data = smp->data.u.str.data;
409 memcpy(var->data.u.str.area, smp->data.u.str.area,
410 var->data.u.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200411 break;
412 case SMP_T_METH:
Christopher Fauletd02210c2017-07-24 16:24:39 +0200413 var->data.u.meth.meth = smp->data.u.meth.meth;
414 if (smp->data.u.meth.meth != HTTP_METH_OTHER)
415 break;
416
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200417 if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.meth.str.data)) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200418 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
419 return 0;
420 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200421 var->data.u.meth.str.area = malloc(smp->data.u.meth.str.data);
422 if (!var->data.u.meth.str.area) {
423 var_accounting_diff(vars, smp->sess, smp->strm,
424 -smp->data.u.meth.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200425 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
426 return 0;
427 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200428 var->data.u.meth.str.data = smp->data.u.meth.str.data;
429 var->data.u.meth.str.size = smp->data.u.meth.str.data;
430 memcpy(var->data.u.meth.str.area, smp->data.u.meth.str.area,
431 var->data.u.meth.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200432 break;
433 }
434 return 1;
435}
436
Willy Tarreau620408f2016-10-21 16:37:51 +0200437/* Returns 0 if fails, else returns 1. Note that stream may be null for SCOPE_SESS. */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100438static inline int sample_store_stream(const char *name, enum vars_scope scope, struct sample *smp)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200439{
440 struct vars *vars;
Christopher Faulete95f2c32017-07-24 16:30:34 +0200441 int ret;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200442
Willy Tarreauf37b1402019-06-04 16:27:36 +0200443 vars = get_vars(smp->sess, smp->strm, scope);
444 if (!vars || vars->scope != scope)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200445 return 0;
Christopher Faulete95f2c32017-07-24 16:30:34 +0200446
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100447 HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200448 ret = sample_store(vars, name, smp);
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100449 HA_RWLOCK_WRUNLOCK(VARS_LOCK, &vars->rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200450 return ret;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200451}
452
Christopher Faulet85d79c92016-11-09 16:54:56 +0100453/* Returns 0 if fails, else returns 1. Note that stream may be null for SCOPE_SESS. */
454static inline int sample_clear_stream(const char *name, enum vars_scope scope, struct sample *smp)
455{
456 struct vars *vars;
457 struct var *var;
458 unsigned int size = 0;
459
Willy Tarreauf37b1402019-06-04 16:27:36 +0200460 vars = get_vars(smp->sess, smp->strm, scope);
461 if (!vars || vars->scope != scope)
Christopher Faulet85d79c92016-11-09 16:54:56 +0100462 return 0;
463
464 /* Look for existing variable name. */
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100465 HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100466 var = var_get(vars, name);
467 if (var) {
468 size = var_clear(var);
469 var_accounting_diff(vars, smp->sess, smp->strm, -size);
470 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100471 HA_RWLOCK_WRUNLOCK(VARS_LOCK, &vars->rwlock);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100472 return 1;
473}
474
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200475/* Returns 0 if fails, else returns 1. */
476static int smp_conv_store(const struct arg *args, struct sample *smp, void *private)
477{
Christopher Faulet0099a8c2016-11-09 16:15:32 +0100478 return sample_store_stream(args[0].data.var.name, args[0].data.var.scope, smp);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200479}
480
Christopher Faulet85d79c92016-11-09 16:54:56 +0100481/* Returns 0 if fails, else returns 1. */
482static int smp_conv_clear(const struct arg *args, struct sample *smp, void *private)
483{
484 return sample_clear_stream(args[0].data.var.name, args[0].data.var.scope, smp);
485}
486
Joseph Herlant07676892018-11-15 09:19:50 -0800487/* This functions check an argument entry and fill it with a variable
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200488 * type. The argumen must be a string. If the variable lookup fails,
Joseph Herlant07676892018-11-15 09:19:50 -0800489 * the function returns 0 and fill <err>, otherwise it returns 1.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200490 */
491int vars_check_arg(struct arg *arg, char **err)
492{
493 char *name;
494 enum vars_scope scope;
495
496 /* Check arg type. */
497 if (arg->type != ARGT_STR) {
498 memprintf(err, "unexpected argument type");
499 return 0;
500 }
501
502 /* Register new variable name. */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200503 name = register_name(arg->data.str.area, arg->data.str.data, &scope,
504 1,
505 err);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200506 if (!name)
507 return 0;
508
Tim Duesterhusa6cc7e82019-05-13 10:53:29 +0200509 /* properly destroy the chunk */
510 chunk_destroy(&arg->data.str);
511
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200512 /* Use the global variable name pointer. */
513 arg->type = ARGT_VAR;
514 arg->data.var.name = name;
515 arg->data.var.scope = scope;
516 return 1;
517}
518
Christopher Faulet09c9df22016-10-31 11:05:37 +0100519/* This function store a sample in a variable if it was already defined.
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200520 * Returns zero on failure and non-zero otherwise. The variable not being
521 * defined is treated as a failure.
Christopher Faulet09c9df22016-10-31 11:05:37 +0100522 */
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200523int vars_set_by_name_ifexist(const char *name, size_t len, struct sample *smp)
Christopher Faulet09c9df22016-10-31 11:05:37 +0100524{
525 enum vars_scope scope;
526
527 /* Resolve name and scope. */
528 name = register_name(name, len, &scope, 0, NULL);
529 if (!name)
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200530 return 0;
Christopher Faulet09c9df22016-10-31 11:05:37 +0100531
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200532 return sample_store_stream(name, scope, smp);
Christopher Faulet09c9df22016-10-31 11:05:37 +0100533}
534
535
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200536/* This function store a sample in a variable.
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200537 * Returns zero on failure and non-zero otherwise.
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200538 */
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200539int vars_set_by_name(const char *name, size_t len, struct sample *smp)
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200540{
541 enum vars_scope scope;
542
543 /* Resolve name and scope. */
Christopher Faulet09c9df22016-10-31 11:05:37 +0100544 name = register_name(name, len, &scope, 1, NULL);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200545 if (!name)
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200546 return 0;
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200547
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200548 return sample_store_stream(name, scope, smp);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200549}
550
Christopher Faulet85d79c92016-11-09 16:54:56 +0100551/* This function unset a variable if it was already defined.
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200552 * Returns zero on failure and non-zero otherwise.
Christopher Faulet85d79c92016-11-09 16:54:56 +0100553 */
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200554int vars_unset_by_name_ifexist(const char *name, size_t len, struct sample *smp)
Christopher Faulet85d79c92016-11-09 16:54:56 +0100555{
556 enum vars_scope scope;
557
558 /* Resolve name and scope. */
559 name = register_name(name, len, &scope, 0, NULL);
560 if (!name)
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200561 return 0;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100562
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200563 return sample_clear_stream(name, scope, smp);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100564}
565
566
Dragan Dosen14518f22021-02-22 17:20:01 +0100567/* This function fills a sample with the variable content.
568 *
569 * Keep in mind that a sample content is duplicated by using smp_dup()
570 * and it therefore uses a pre-allocated trash chunk as returned by
571 * get_trash_chunk().
572 *
573 * Returns 1 if the sample is filled, otherwise it returns 0.
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200574 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100575int vars_get_by_name(const char *name, size_t len, struct sample *smp)
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200576{
577 struct vars *vars;
578 struct var *var;
579 enum vars_scope scope;
580
581 /* Resolve name and scope. */
Christopher Faulet09c9df22016-10-31 11:05:37 +0100582 name = register_name(name, len, &scope, 1, NULL);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200583 if (!name)
584 return 0;
585
586 /* Select "vars" pool according with the scope. */
Willy Tarreauf37b1402019-06-04 16:27:36 +0200587 vars = get_vars(smp->sess, smp->strm, scope);
588 if (!vars || vars->scope != scope)
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200589 return 0;
590
591 /* Get the variable entry. */
Dragan Dosen14518f22021-02-22 17:20:01 +0100592 HA_RWLOCK_RDLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200593 var = var_get(vars, name);
Dragan Dosen14518f22021-02-22 17:20:01 +0100594 if (!var) {
595 HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200596 return 0;
Dragan Dosen14518f22021-02-22 17:20:01 +0100597 }
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200598
599 /* Copy sample. */
Thierry FOURNIER5cc18d42015-08-19 09:02:36 +0200600 smp->data = var->data;
Dragan Dosen14518f22021-02-22 17:20:01 +0100601 smp_dup(smp);
602
603 HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200604 return 1;
605}
606
Dragan Dosen14518f22021-02-22 17:20:01 +0100607/* This function fills a sample with the content of the variable described
608 * by <var_desc>.
609 *
610 * Keep in mind that a sample content is duplicated by using smp_dup()
611 * and it therefore uses a pre-allocated trash chunk as returned by
612 * get_trash_chunk().
613 *
614 * Returns 1 if the sample is filled, otherwise it returns 0.
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200615 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100616int vars_get_by_desc(const struct var_desc *var_desc, struct sample *smp)
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200617{
618 struct vars *vars;
619 struct var *var;
620
621 /* Select "vars" pool according with the scope. */
Willy Tarreauf37b1402019-06-04 16:27:36 +0200622 vars = get_vars(smp->sess, smp->strm, var_desc->scope);
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200623
Joseph Herlant07676892018-11-15 09:19:50 -0800624 /* Check if the scope is available a this point of processing. */
Willy Tarreauf37b1402019-06-04 16:27:36 +0200625 if (!vars || vars->scope != var_desc->scope)
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200626 return 0;
627
628 /* Get the variable entry. */
Dragan Dosen14518f22021-02-22 17:20:01 +0100629 HA_RWLOCK_RDLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200630 var = var_get(vars, var_desc->name);
Dragan Dosen14518f22021-02-22 17:20:01 +0100631 if (!var) {
632 HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200633 return 0;
Dragan Dosen14518f22021-02-22 17:20:01 +0100634 }
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200635
636 /* Copy sample. */
Thierry FOURNIER5cc18d42015-08-19 09:02:36 +0200637 smp->data = var->data;
Dragan Dosen14518f22021-02-22 17:20:01 +0100638 smp_dup(smp);
639
640 HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200641 return 1;
642}
643
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200644/* Always returns ACT_RET_CONT even if an error occurs. */
645static enum act_return action_store(struct act_rule *rule, struct proxy *px,
Willy Tarreau658b85b2015-09-27 10:00:49 +0200646 struct session *sess, struct stream *s, int flags)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200647{
648 struct sample smp;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200649 int dir;
650
651 switch (rule->from) {
Willy Tarreau620408f2016-10-21 16:37:51 +0200652 case ACT_F_TCP_REQ_SES: dir = SMP_OPT_DIR_REQ; break;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200653 case ACT_F_TCP_REQ_CNT: dir = SMP_OPT_DIR_REQ; break;
654 case ACT_F_TCP_RES_CNT: dir = SMP_OPT_DIR_RES; break;
655 case ACT_F_HTTP_REQ: dir = SMP_OPT_DIR_REQ; break;
656 case ACT_F_HTTP_RES: dir = SMP_OPT_DIR_RES; break;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +0100657 case ACT_F_TCP_CHK: dir = SMP_OPT_DIR_REQ; break;
Willy Tarreau01d580a2021-03-26 11:11:34 +0100658 case ACT_F_CFG_PARSER: dir = SMP_OPT_DIR_REQ; break; /* not used anyway */
Willy Tarreau2f836de2021-03-26 15:36:44 +0100659 case ACT_F_CLI_PARSER: dir = SMP_OPT_DIR_REQ; break; /* not used anyway */
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200660 default:
661 send_log(px, LOG_ERR, "Vars: internal error while execute action store.");
662 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Christopher Faulet767a84b2017-11-24 16:50:31 +0100663 ha_alert("Vars: internal error while execute action store.\n");
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200664 return ACT_RET_CONT;
665 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200666
667 /* Process the expression. */
668 memset(&smp, 0, sizeof(smp));
Willy Tarreau108a8fd2016-10-21 17:13:24 +0200669 if (!sample_process(px, sess, s, dir|SMP_OPT_FINAL,
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200670 rule->arg.vars.expr, &smp))
Thierry FOURNIER24ff6c62015-08-06 08:52:53 +0200671 return ACT_RET_CONT;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200672
673 /* Store the sample, and ignore errors. */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100674 sample_store_stream(rule->arg.vars.name, rule->arg.vars.scope, &smp);
Thierry FOURNIER24ff6c62015-08-06 08:52:53 +0200675 return ACT_RET_CONT;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200676}
677
Christopher Faulet85d79c92016-11-09 16:54:56 +0100678/* Always returns ACT_RET_CONT even if an error occurs. */
679static enum act_return action_clear(struct act_rule *rule, struct proxy *px,
680 struct session *sess, struct stream *s, int flags)
681{
682 struct sample smp;
683
684 memset(&smp, 0, sizeof(smp));
685 smp_set_owner(&smp, px, sess, s, SMP_OPT_FINAL);
686
687 /* Clear the variable using the sample context, and ignore errors. */
688 sample_clear_stream(rule->arg.vars.name, rule->arg.vars.scope, &smp);
689 return ACT_RET_CONT;
690}
691
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200692static void release_store_rule(struct act_rule *rule)
693{
694 release_sample_expr(rule->arg.vars.expr);
695}
696
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200697/* This two function checks the variable name and replace the
698 * configuration string name by the global string name. its
699 * the same string, but the global pointer can be easy to
700 * compare.
701 *
702 * The first function checks a sample-fetch and the second
703 * checks a converter.
704 */
705static int smp_check_var(struct arg *args, char **err)
706{
707 return vars_check_arg(&args[0], err);
708}
709
710static int conv_check_var(struct arg *args, struct sample_conv *conv,
711 const char *file, int line, char **err_msg)
712{
713 return vars_check_arg(&args[0], err_msg);
714}
715
716/* This function is a common parser for using variables. It understands
717 * the format:
718 *
719 * set-var(<variable-name>) <expression>
Willy Tarreau4b7531f2019-06-04 16:43:29 +0200720 * unset-var(<variable-name>)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200721 *
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200722 * It returns ACT_RET_PRS_ERR if fails and <err> is filled with an error
723 * message. Otherwise, it returns ACT_RET_PRS_OK and the variable <expr>
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100724 * is filled with the pointer to the expression to execute. The proxy is
725 * only used to retrieve the ->conf entries.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200726 */
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200727static enum act_parse_ret parse_store(const char **args, int *arg, struct proxy *px,
728 struct act_rule *rule, char **err)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200729{
730 const char *var_name = args[*arg-1];
731 int var_len;
Thierry FOURNIER48a9cd12015-07-28 19:00:28 +0200732 const char *kw_name;
Willy Tarreaue3658152016-11-24 21:23:28 +0100733 int flags, set_var = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200734
Christopher Faulet85d79c92016-11-09 16:54:56 +0100735 if (!strncmp(var_name, "set-var", 7)) {
736 var_name += 7;
737 set_var = 1;
738 }
739 if (!strncmp(var_name, "unset-var", 9)) {
740 var_name += 9;
741 set_var = 0;
742 }
743
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200744 if (*var_name != '(') {
Willy Tarreau3d1d1782021-03-12 11:53:19 +0100745 memprintf(err, "invalid or incomplete action '%s'. Expects 'set-var(<var-name>)' or 'unset-var(<var-name>)'",
Christopher Faulet85d79c92016-11-09 16:54:56 +0100746 args[*arg-1]);
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200747 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200748 }
749 var_name++; /* jump the '(' */
750 var_len = strlen(var_name);
751 var_len--; /* remove the ')' */
752 if (var_name[var_len] != ')') {
Willy Tarreau3d1d1782021-03-12 11:53:19 +0100753 memprintf(err, "incomplete expression after action '%s'. Expects 'set-var(<var-name>)' or 'unset-var(<var-name>)'",
Christopher Faulet85d79c92016-11-09 16:54:56 +0100754 args[*arg-1]);
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200755 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200756 }
757
Christopher Faulet09c9df22016-10-31 11:05:37 +0100758 rule->arg.vars.name = register_name(var_name, var_len, &rule->arg.vars.scope, 1, err);
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200759 if (!rule->arg.vars.name)
760 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200761
Christopher Faulet85d79c92016-11-09 16:54:56 +0100762 /* There is no fetch method when variable is unset. Just set the right
763 * action and return. */
764 if (!set_var) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100765 rule->action = ACT_CUSTOM;
766 rule->action_ptr = action_clear;
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200767 rule->release_ptr = release_store_rule;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100768 return ACT_RET_PRS_OK;
769 }
770
Thierry FOURNIER48a9cd12015-07-28 19:00:28 +0200771 kw_name = args[*arg-1];
772
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200773 rule->arg.vars.expr = sample_parse_expr((char **)args, arg, px->conf.args.file,
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100774 px->conf.args.line, err, &px->conf.args, NULL);
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200775 if (!rule->arg.vars.expr)
Thierry FOURNIERafa80492015-08-19 09:04:15 +0200776 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200777
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200778 switch (rule->from) {
Willy Tarreau620408f2016-10-21 16:37:51 +0200779 case ACT_F_TCP_REQ_SES: flags = SMP_VAL_FE_SES_ACC; break;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200780 case ACT_F_TCP_REQ_CNT: flags = SMP_VAL_FE_REQ_CNT; break;
781 case ACT_F_TCP_RES_CNT: flags = SMP_VAL_BE_RES_CNT; break;
782 case ACT_F_HTTP_REQ: flags = SMP_VAL_FE_HRQ_HDR; break;
783 case ACT_F_HTTP_RES: flags = SMP_VAL_BE_HRS_HDR; break;
Gaetan Rivet707b52f2020-02-21 18:14:59 +0100784 case ACT_F_TCP_CHK: flags = SMP_VAL_BE_CHK_RUL; break;
Willy Tarreau01d580a2021-03-26 11:11:34 +0100785 case ACT_F_CFG_PARSER: flags = SMP_VAL_CFG_PARSER; break;
Willy Tarreau2f836de2021-03-26 15:36:44 +0100786 case ACT_F_CLI_PARSER: flags = SMP_VAL_CLI_PARSER; break;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200787 default:
788 memprintf(err,
789 "internal error, unexpected rule->from=%d, please report this bug!",
790 rule->from);
Thierry FOURNIERafa80492015-08-19 09:04:15 +0200791 return ACT_RET_PRS_ERR;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200792 }
793 if (!(rule->arg.vars.expr->fetch->val & flags)) {
794 memprintf(err,
795 "fetch method '%s' extracts information from '%s', none of which is available here",
796 kw_name, sample_src_names(rule->arg.vars.expr->fetch->use));
797 free(rule->arg.vars.expr);
Thierry FOURNIERafa80492015-08-19 09:04:15 +0200798 return ACT_RET_PRS_ERR;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200799 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200800
Thierry FOURNIER42148732015-09-02 17:17:33 +0200801 rule->action = ACT_CUSTOM;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200802 rule->action_ptr = action_store;
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200803 rule->release_ptr = release_store_rule;
Thierry FOURNIERafa80492015-08-19 09:04:15 +0200804 return ACT_RET_PRS_OK;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200805}
806
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100807
808/* parses a global "set-var" directive. It will create a temporary rule and
809 * expression that are parsed, processed, and released on the fly so that we
810 * respect the real set-var syntax. These directives take the following format:
811 * set-var <name> <expression>
812 * Note that parse_store() expects "set-var(name) <expression>" so we have to
813 * temporarily replace the keyword here.
814 */
815static int vars_parse_global_set_var(char **args, int section_type, struct proxy *curpx,
816 const struct proxy *defpx, const char *file, int line,
817 char **err)
818{
819 struct proxy px = {
820 .id = "CLI",
821 .conf.args.file = file,
822 .conf.args.line = line,
823 };
824 struct act_rule rule = {
825 .arg.vars.scope = SCOPE_PROC,
826 .from = ACT_F_CFG_PARSER,
827 };
828 enum act_parse_ret p_ret;
829 char *old_arg1;
830 char *tmp_arg1;
831 int arg = 2; // variable name
832 int ret = -1;
833
834 LIST_INIT(&px.conf.args.list);
835
836 if (!*args[1] || !*args[2]) {
837 memprintf(err, "'%s' requires a process-wide variable name ('proc.<name>') and a sample expression.", args[0]);
838 goto end;
839 }
840
841 tmp_arg1 = NULL;
842 if (!memprintf(&tmp_arg1, "set-var(%s)", args[1]))
843 goto end;
844
845 /* parse_store() will always return a message in <err> on error */
846 old_arg1 = args[1]; args[1] = tmp_arg1;
847 p_ret = parse_store((const char **)args, &arg, &px, &rule, err);
848 free(args[1]); args[1] = old_arg1;
849
850 if (p_ret != ACT_RET_PRS_OK)
851 goto end;
852
853 if (rule.arg.vars.scope != SCOPE_PROC) {
854 memprintf(err, "'%s': cannot set variable '%s', only scope 'proc' is permitted in the global section.", args[0], args[1]);
855 goto end;
856 }
857
858 if (smp_resolve_args(&px, err) != 0) {
859 release_sample_expr(rule.arg.vars.expr);
860 indent_msg(err, 2);
861 goto end;
862 }
863
864 action_store(&rule, &px, NULL, NULL, 0);
865 release_sample_expr(rule.arg.vars.expr);
866
867 ret = 0;
868 end:
869 return ret;
870}
871
Willy Tarreauc35eb382021-03-26 14:51:31 +0100872/* parse CLI's "get var <name>" */
873static int vars_parse_cli_get_var(char **args, char *payload, struct appctx *appctx, void *private)
874{
875 struct vars *vars;
Willy Tarreau374edc72021-04-01 17:01:43 +0200876 struct sample smp = { };
Willy Tarreauc35eb382021-03-26 14:51:31 +0100877 int i;
878
879 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
880 return 1;
881
882 if (!*args[2])
883 return cli_err(appctx, "Missing process-wide variable identifier.\n");
884
885 vars = get_vars(NULL, NULL, SCOPE_PROC);
886 if (!vars || vars->scope != SCOPE_PROC)
887 return 0;
888
889 if (!vars_get_by_name(args[2], strlen(args[2]), &smp))
890 return cli_err(appctx, "Variable not found.\n");
891
892 /* the sample returned by vars_get_by_name() is allocated into a trash
893 * chunk so we have no constraint to manipulate it.
894 */
895 chunk_printf(&trash, "%s: type=%s value=", args[2], smp_to_type[smp.data.type]);
896
897 if (!sample_casts[smp.data.type][SMP_T_STR] ||
898 !sample_casts[smp.data.type][SMP_T_STR](&smp)) {
899 chunk_appendf(&trash, "(undisplayable)");
900 } else {
901 /* Display the displayable chars*. */
902 b_putchr(&trash, '<');
903 for (i = 0; i < smp.data.u.str.data; i++) {
904 if (isprint((unsigned char)smp.data.u.str.area[i]))
905 b_putchr(&trash, smp.data.u.str.area[i]);
906 else
907 b_putchr(&trash, '.');
908 }
909 b_putchr(&trash, '>');
910 b_putchr(&trash, 0);
911 }
912 return cli_msg(appctx, LOG_INFO, trash.area);
913}
914
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +0100915/* parse CLI's "set var <name> <expression>" */
916static int vars_parse_cli_set_var(char **args, char *payload, struct appctx *appctx, void *private)
917{
918 struct proxy px = {
919 .id = "CLI",
920 .conf.args.file = "CLI",
921 .conf.args.line = 0,
922 };
923 struct act_rule rule = {
924 .arg.vars.scope = SCOPE_PROC,
925 .from = ACT_F_CLI_PARSER,
926 };
927 enum act_parse_ret p_ret;
928 char *old_arg2;
929 char *tmp_arg2;
930 char *err = NULL;
931 int arg = 2; // variable name
932 int nberr;
933
934 LIST_INIT(&px.conf.args.list);
935
936 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
937 return 1;
938
939 if (!*args[2] || !*args[3])
940 return cli_err(appctx, "Missing process-wide variable identifier and expression.\n");
941
942 tmp_arg2 = NULL;
943 if (!memprintf(&tmp_arg2, "set-var(%s)", args[2])) {
944 memprintf(&err, "memory allocation error.");
945 goto fail;
946 }
947
948 /* parse_store() will always return a message in <err> on error */
949 old_arg2 = args[2]; args[2] = tmp_arg2;
950 p_ret = parse_store((const char **)(args + 1), &arg, &px, &rule, &err);
951 free(args[2]); args[2] = old_arg2;
952
953 if (p_ret != ACT_RET_PRS_OK)
954 goto fail;
955
956 if (rule.arg.vars.scope != SCOPE_PROC) {
957 memprintf(&err, "'%s %s': cannot set variable '%s', only scope 'proc' is permitted in the global section.", args[0], args[1], args[2]);
958 goto fail;
959 }
960
961 err = NULL;
962 nberr = smp_resolve_args(&px, &err);
963 if (nberr) {
964 release_sample_expr(rule.arg.vars.expr);
965 indent_msg(&err, 2);
966 goto fail;
967 }
968
969 action_store(&rule, &px, NULL, NULL, 0);
970 release_sample_expr(rule.arg.vars.expr);
971 appctx->st0 = CLI_ST_PROMPT;
972 return 0;
973 fail:
974 return cli_dynerr(appctx, err);
975}
976
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200977static int vars_max_size(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +0100978 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200979 char **err, unsigned int *limit)
980{
981 char *error;
982
983 *limit = strtol(args[1], &error, 10);
984 if (*error != 0) {
985 memprintf(err, "%s: '%s' is an invalid size", args[0], args[1]);
986 return -1;
987 }
988 return 0;
989}
990
991static int vars_max_size_global(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +0100992 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200993 char **err)
994{
995 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_global_limit);
996}
997
Christopher Fauletff2613e2016-11-09 11:36:17 +0100998static int vars_max_size_proc(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +0100999 const struct proxy *defpx, const char *file, int line,
Christopher Fauletff2613e2016-11-09 11:36:17 +01001000 char **err)
1001{
1002 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_proc_limit);
1003}
1004
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001005static int vars_max_size_sess(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001006 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001007 char **err)
1008{
1009 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_sess_limit);
1010}
1011
1012static int vars_max_size_txn(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001013 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001014 char **err)
1015{
1016 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_txn_limit);
1017}
1018
1019static int vars_max_size_reqres(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001020 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001021 char **err)
1022{
1023 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_reqres_limit);
1024}
1025
Gaetan Rivet13a50432020-02-21 18:13:44 +01001026static int vars_max_size_check(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001027 const struct proxy *defpx, const char *file, int line,
Gaetan Rivet13a50432020-02-21 18:13:44 +01001028 char **err)
1029{
1030 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_check_limit);
1031}
1032
Tim Duesterhusbbdd5b82020-07-04 11:53:25 +02001033static void vars_deinit()
1034{
1035 while (var_names_nb-- > 0)
1036 free(var_names[var_names_nb]);
1037 free(var_names);
1038}
1039
1040REGISTER_POST_DEINIT(vars_deinit);
1041
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001042static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
1043
Willy Tarreau0209c972021-03-26 12:03:11 +01001044 { "var", smp_fetch_var, ARG1(1,STR), smp_check_var, SMP_T_STR, SMP_USE_CONST },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001045 { /* END */ },
1046}};
1047
Willy Tarreau0108d902018-11-25 19:14:37 +01001048INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);
1049
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001050static struct sample_conv_kw_list sample_conv_kws = {ILH, {
Christopher Faulet85d79c92016-11-09 16:54:56 +01001051 { "set-var", smp_conv_store, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
1052 { "unset-var", smp_conv_clear, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001053 { /* END */ },
1054}};
1055
Willy Tarreau0108d902018-11-25 19:14:37 +01001056INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws);
1057
Willy Tarreau620408f2016-10-21 16:37:51 +02001058static struct action_kw_list tcp_req_sess_kws = { { }, {
Christopher Faulet85d79c92016-11-09 16:54:56 +01001059 { "set-var", parse_store, 1 },
1060 { "unset-var", parse_store, 1 },
Willy Tarreau620408f2016-10-21 16:37:51 +02001061 { /* END */ }
1062}};
1063
Willy Tarreau0108d902018-11-25 19:14:37 +01001064INITCALL1(STG_REGISTER, tcp_req_sess_keywords_register, &tcp_req_sess_kws);
1065
Willy Tarreau620408f2016-10-21 16:37:51 +02001066static struct action_kw_list tcp_req_cont_kws = { { }, {
Christopher Faulet85d79c92016-11-09 16:54:56 +01001067 { "set-var", parse_store, 1 },
1068 { "unset-var", parse_store, 1 },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001069 { /* END */ }
1070}};
1071
Willy Tarreau0108d902018-11-25 19:14:37 +01001072INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_cont_kws);
1073
Thierry FOURNIER36481b82015-08-19 09:01:53 +02001074static struct action_kw_list tcp_res_kws = { { }, {
Christopher Faulet85d79c92016-11-09 16:54:56 +01001075 { "set-var", parse_store, 1 },
1076 { "unset-var", parse_store, 1 },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001077 { /* END */ }
1078}};
1079
Willy Tarreau0108d902018-11-25 19:14:37 +01001080INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_kws);
1081
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001082static struct action_kw_list tcp_check_kws = {ILH, {
1083 { "set-var", parse_store, 1 },
1084 { "unset-var", parse_store, 1 },
1085 { /* END */ }
1086}};
1087
1088INITCALL1(STG_REGISTER, tcp_check_keywords_register, &tcp_check_kws);
1089
Thierry FOURNIER36481b82015-08-19 09:01:53 +02001090static struct action_kw_list http_req_kws = { { }, {
Christopher Faulet85d79c92016-11-09 16:54:56 +01001091 { "set-var", parse_store, 1 },
1092 { "unset-var", parse_store, 1 },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001093 { /* END */ }
1094}};
1095
Willy Tarreau0108d902018-11-25 19:14:37 +01001096INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_kws);
1097
Thierry FOURNIER36481b82015-08-19 09:01:53 +02001098static struct action_kw_list http_res_kws = { { }, {
Christopher Faulet85d79c92016-11-09 16:54:56 +01001099 { "set-var", parse_store, 1 },
1100 { "unset-var", parse_store, 1 },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001101 { /* END */ }
1102}};
1103
Willy Tarreau0108d902018-11-25 19:14:37 +01001104INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_kws);
1105
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01001106static struct action_kw_list http_after_res_kws = { { }, {
1107 { "set-var", parse_store, 1 },
1108 { "unset-var", parse_store, 1 },
1109 { /* END */ }
1110}};
1111
1112INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_kws);
1113
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001114static struct cfg_kw_list cfg_kws = {{ },{
Willy Tarreau13d2ba22021-03-26 11:38:08 +01001115 { CFG_GLOBAL, "set-var", vars_parse_global_set_var },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001116 { CFG_GLOBAL, "tune.vars.global-max-size", vars_max_size_global },
Christopher Fauletff2613e2016-11-09 11:36:17 +01001117 { CFG_GLOBAL, "tune.vars.proc-max-size", vars_max_size_proc },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001118 { CFG_GLOBAL, "tune.vars.sess-max-size", vars_max_size_sess },
1119 { CFG_GLOBAL, "tune.vars.txn-max-size", vars_max_size_txn },
1120 { CFG_GLOBAL, "tune.vars.reqres-max-size", vars_max_size_reqres },
Gaetan Rivet13a50432020-02-21 18:13:44 +01001121 { CFG_GLOBAL, "tune.vars.check-max-size", vars_max_size_check },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001122 { /* END */ }
1123}};
1124
Willy Tarreau0108d902018-11-25 19:14:37 +01001125INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Willy Tarreauc35eb382021-03-26 14:51:31 +01001126
1127
1128/* register cli keywords */
1129static struct cli_kw_list cli_kws = {{ },{
1130 { { "get", "var", NULL }, "get var <name> : retrieve contents of a process-wide variable", vars_parse_cli_get_var, NULL },
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001131 { { "set", "var", NULL }, "set var <name> <expr> : set variable from an expression", vars_parse_cli_set_var, NULL, NULL, NULL, ACCESS_EXPERIMENTAL },
Willy Tarreauc35eb382021-03-26 14:51:31 +01001132 { { NULL }, NULL, NULL, NULL }
1133}};
1134INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);