blob: 47267864bd3113db4d533d4a0a219bf4febefdf9 [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 Tarreau753d4db2021-09-03 09:02:47 +020015#include <haproxy/session.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020016#include <haproxy/stream-t.h>
Willy Tarreau8b550af2020-06-04 17:42:48 +020017#include <haproxy/tcp_rules.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020018#include <haproxy/tcpcheck.h>
Willy Tarreau67046bf2021-05-08 13:56:31 +020019#include <haproxy/tools.h>
Willy Tarreaua1718922020-06-04 16:25:31 +020020#include <haproxy/vars.h>
Willy Tarreaub555eb12021-10-06 17:11:51 +020021#include <haproxy/xxhash.h>
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020022
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020023
24/* This contains a pool of struct vars */
Willy Tarreau8ceae722018-11-26 11:58:30 +010025DECLARE_STATIC_POOL(var_pool, "vars", sizeof(struct var));
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020026
Willy Tarreaucfc4f242021-05-08 11:41:28 +020027/* list of variables for the process scope. */
28struct vars proc_vars THREAD_ALIGNED(64);
29
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020030/* This array of int contains the system limits per context. */
31static unsigned int var_global_limit = 0;
Christopher Fauletff2613e2016-11-09 11:36:17 +010032static unsigned int var_proc_limit = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020033static unsigned int var_sess_limit = 0;
34static unsigned int var_txn_limit = 0;
35static unsigned int var_reqres_limit = 0;
Gaetan Rivet13a50432020-02-21 18:13:44 +010036static unsigned int var_check_limit = 0;
Willy Tarreau2c897d92021-08-31 08:48:55 +020037static uint64_t var_name_hash_seed = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020038
Remi Tricot-Le Breton51899d22021-12-16 17:14:37 +010039/* Structure and array matching set-var conditions to their respective flag
40 * value.
41 */
42struct var_set_condition {
43 const char *cond_str;
44 uint flag;
45};
46
47static struct var_set_condition conditions_array[] = {
48 { "ifexists", VF_COND_IFEXISTS },
49 { "ifnotexists", VF_COND_IFNOTEXISTS },
50 { "ifempty", VF_COND_IFEMPTY },
51 { "ifnotempty", VF_COND_IFNOTEMPTY },
52 { "ifset", VF_COND_IFSET },
53 { "ifnotset", VF_COND_IFNOTSET },
54 { "ifgt", VF_COND_IFGT },
55 { "iflt", VF_COND_IFLT },
56 { NULL, 0 }
57};
58
Willy Tarreauf37b1402019-06-04 16:27:36 +020059/* returns the struct vars pointer for a session, stream and scope, or NULL if
60 * it does not exist.
61 */
62static inline struct vars *get_vars(struct session *sess, struct stream *strm, enum vars_scope scope)
63{
64 switch (scope) {
65 case SCOPE_PROC:
Willy Tarreaucfc4f242021-05-08 11:41:28 +020066 return &proc_vars;
Willy Tarreauf37b1402019-06-04 16:27:36 +020067 case SCOPE_SESS:
Willy Tarreaua07d61b2021-03-26 11:27:59 +010068 return sess ? &sess->vars : NULL;
Gaetan Rivet13a50432020-02-21 18:13:44 +010069 case SCOPE_CHECK: {
Christopher Fauletc4439f72021-06-02 11:48:42 +020070 struct check *check = sess ? objt_check(sess->origin) : NULL;
Gaetan Rivet13a50432020-02-21 18:13:44 +010071
Christopher Faulet0fca7ed2020-04-21 11:53:32 +020072 return check ? &check->vars : NULL;
Gaetan Rivet13a50432020-02-21 18:13:44 +010073 }
Willy Tarreauf37b1402019-06-04 16:27:36 +020074 case SCOPE_TXN:
75 return strm ? &strm->vars_txn : NULL;
76 case SCOPE_REQ:
77 case SCOPE_RES:
78 default:
79 return strm ? &strm->vars_reqres : NULL;
80 }
81}
82
Willy Tarreau72330982015-06-19 11:21:56 +020083/* This function adds or remove memory size from the accounting. The inner
84 * pointers may be null when setting the outer ones only.
85 */
Miroslav Zagorac6deab792020-12-09 16:34:29 +010086void var_accounting_diff(struct vars *vars, struct session *sess, struct stream *strm, int size)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020087{
88 switch (vars->scope) {
89 case SCOPE_REQ:
90 case SCOPE_RES:
Willy Tarreau55f8a832021-09-08 15:51:06 +020091 if (var_reqres_limit && strm)
Willy Tarreauf37b1402019-06-04 16:27:36 +020092 _HA_ATOMIC_ADD(&strm->vars_reqres.size, size);
Willy Tarreau6204cd92016-03-10 16:33:04 +010093 /* fall through */
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020094 case SCOPE_TXN:
Willy Tarreau55f8a832021-09-08 15:51:06 +020095 if (var_txn_limit && strm)
Willy Tarreauf37b1402019-06-04 16:27:36 +020096 _HA_ATOMIC_ADD(&strm->vars_txn.size, size);
Gaetan Rivet13a50432020-02-21 18:13:44 +010097 goto scope_sess;
Willy Tarreau55f8a832021-09-08 15:51:06 +020098 case SCOPE_CHECK:
99 if (var_check_limit) {
Christopher Faulet0fca7ed2020-04-21 11:53:32 +0200100 struct check *check = objt_check(sess->origin);
Gaetan Rivet13a50432020-02-21 18:13:44 +0100101
Christopher Faulet0fca7ed2020-04-21 11:53:32 +0200102 if (check)
103 _HA_ATOMIC_ADD(&check->vars.size, size);
Gaetan Rivet13a50432020-02-21 18:13:44 +0100104 }
Willy Tarreau6204cd92016-03-10 16:33:04 +0100105 /* fall through */
Gaetan Rivet13a50432020-02-21 18:13:44 +0100106scope_sess:
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200107 case SCOPE_SESS:
Willy Tarreau55f8a832021-09-08 15:51:06 +0200108 if (var_sess_limit)
109 _HA_ATOMIC_ADD(&sess->vars.size, size);
Christopher Fauletff2613e2016-11-09 11:36:17 +0100110 /* fall through */
111 case SCOPE_PROC:
Willy Tarreau55f8a832021-09-08 15:51:06 +0200112 if (var_proc_limit || var_global_limit)
113 _HA_ATOMIC_ADD(&proc_vars.size, size);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200114 }
115}
116
117/* This function returns 1 if the <size> is available in the var
Joseph Herlant07676892018-11-15 09:19:50 -0800118 * pool <vars>, otherwise returns 0. If the space is available,
Willy Tarreau72330982015-06-19 11:21:56 +0200119 * the size is reserved. The inner pointers may be null when setting
Willy Tarreau6204cd92016-03-10 16:33:04 +0100120 * the outer ones only. The accounting uses either <sess> or <strm>
121 * depending on the scope. <strm> may be NULL when no stream is known
122 * and only the session exists (eg: tcp-request connection).
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200123 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100124static int var_accounting_add(struct vars *vars, struct session *sess, struct stream *strm, int size)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200125{
126 switch (vars->scope) {
127 case SCOPE_REQ:
128 case SCOPE_RES:
Willy Tarreauf37b1402019-06-04 16:27:36 +0200129 if (var_reqres_limit && strm && strm->vars_reqres.size + size > var_reqres_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200130 return 0;
Willy Tarreau6204cd92016-03-10 16:33:04 +0100131 /* fall through */
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200132 case SCOPE_TXN:
Willy Tarreauf37b1402019-06-04 16:27:36 +0200133 if (var_txn_limit && strm && strm->vars_txn.size + size > var_txn_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200134 return 0;
Gaetan Rivet13a50432020-02-21 18:13:44 +0100135 goto scope_sess;
136 case SCOPE_CHECK: {
Christopher Faulet0fca7ed2020-04-21 11:53:32 +0200137 struct check *check = objt_check(sess->origin);
Gaetan Rivet13a50432020-02-21 18:13:44 +0100138
Christopher Faulet0fca7ed2020-04-21 11:53:32 +0200139 if (var_check_limit && check && check->vars.size + size > var_check_limit)
Gaetan Rivet13a50432020-02-21 18:13:44 +0100140 return 0;
141 }
Willy Tarreau6204cd92016-03-10 16:33:04 +0100142 /* fall through */
Gaetan Rivet13a50432020-02-21 18:13:44 +0100143scope_sess:
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200144 case SCOPE_SESS:
Willy Tarreau6204cd92016-03-10 16:33:04 +0100145 if (var_sess_limit && sess->vars.size + size > var_sess_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200146 return 0;
Christopher Fauletff2613e2016-11-09 11:36:17 +0100147 /* fall through */
148 case SCOPE_PROC:
Willy Tarreau3b78f2a2021-09-08 15:40:58 +0200149 /* note: scope proc collects all others and is currently identical to the
150 * global limit.
151 */
Willy Tarreaucfc4f242021-05-08 11:41:28 +0200152 if (var_proc_limit && proc_vars.size + size > var_proc_limit)
Christopher Fauletff2613e2016-11-09 11:36:17 +0100153 return 0;
Willy Tarreau3b78f2a2021-09-08 15:40:58 +0200154 if (var_global_limit && proc_vars.size + size > var_global_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200155 return 0;
156 }
Willy Tarreau6204cd92016-03-10 16:33:04 +0100157 var_accounting_diff(vars, sess, strm, size);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200158 return 1;
159}
160
Willy Tarreauc1c88f42021-09-08 15:03:58 +0200161/* This function removes a variable from the list and frees the memory it was
162 * using. If the variable is marked "VF_PERMANENT", the sample_data is only
163 * reset to SMP_T_ANY unless <force> is non nul. Returns the freed size.
164 */
165unsigned int var_clear(struct var *var, int force)
Christopher Faulet85d79c92016-11-09 16:54:56 +0100166{
167 unsigned int size = 0;
168
169 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
Willy Tarreau5b52b002021-02-26 21:19:53 +0100170 ha_free(&var->data.u.str.area);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200171 size += var->data.u.str.data;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100172 }
Christopher Fauletd02210c2017-07-24 16:24:39 +0200173 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
Willy Tarreau5b52b002021-02-26 21:19:53 +0100174 ha_free(&var->data.u.meth.str.area);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200175 size += var->data.u.meth.str.data;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100176 }
Willy Tarreauc1c88f42021-09-08 15:03:58 +0200177 /* wipe the sample */
178 var->data.type = SMP_T_ANY;
179
180 if (!(var->flags & VF_PERMANENT) || force) {
181 LIST_DELETE(&var->l);
182 pool_free(var_pool, var);
183 size += sizeof(struct var);
184 }
Christopher Faulet85d79c92016-11-09 16:54:56 +0100185 return size;
186}
187
Joseph Herlant07676892018-11-15 09:19:50 -0800188/* This function free all the memory used by all the variables
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200189 * in the list.
190 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100191void vars_prune(struct vars *vars, struct session *sess, struct stream *strm)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200192{
193 struct var *var, *tmp;
Willy Tarreau72330982015-06-19 11:21:56 +0200194 unsigned int size = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200195
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200196 vars_wrlock(vars);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200197 list_for_each_entry_safe(var, tmp, &vars->head, l) {
Willy Tarreauc1c88f42021-09-08 15:03:58 +0200198 size += var_clear(var, 1);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200199 }
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200200 vars_wrunlock(vars);
Willy Tarreau6204cd92016-03-10 16:33:04 +0100201 var_accounting_diff(vars, sess, strm, -size);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200202}
203
Willy Tarreauebcd4842015-06-19 11:59:02 +0200204/* This function frees all the memory used by all the session variables in the
205 * list starting at <vars>.
206 */
207void vars_prune_per_sess(struct vars *vars)
208{
209 struct var *var, *tmp;
210 unsigned int size = 0;
211
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200212 vars_wrlock(vars);
Willy Tarreauebcd4842015-06-19 11:59:02 +0200213 list_for_each_entry_safe(var, tmp, &vars->head, l) {
Willy Tarreauc1c88f42021-09-08 15:03:58 +0200214 size += var_clear(var, 1);
Willy Tarreauebcd4842015-06-19 11:59:02 +0200215 }
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200216 vars_wrunlock(vars);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200217
Willy Tarreau55f8a832021-09-08 15:51:06 +0200218 if (var_sess_limit)
219 _HA_ATOMIC_SUB(&vars->size, size);
220 if (var_proc_limit || var_global_limit)
221 _HA_ATOMIC_SUB(&proc_vars.size, size);
Willy Tarreauebcd4842015-06-19 11:59:02 +0200222}
223
Willy Tarreaub7bfcb32021-08-31 08:13:25 +0200224/* This function initializes a variables list head */
225void vars_init_head(struct vars *vars, enum vars_scope scope)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200226{
227 LIST_INIT(&vars->head);
228 vars->scope = scope;
229 vars->size = 0;
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100230 HA_RWLOCK_INIT(&vars->rwlock);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200231}
232
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200233/* This function returns a hash value and a scope for a variable name of a
234 * specified length. It makes sure that the scope is valid. It returns non-zero
235 * on success, 0 on failure. Neither hash nor scope may be NULL.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200236 */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200237static int vars_hash_name(const char *name, int len, enum vars_scope *scope,
238 uint64_t *hash, char **err)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200239{
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200240 const char *tmp;
241
242 /* Check length. */
243 if (len == 0) {
244 memprintf(err, "Empty variable name cannot be accepted");
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200245 return 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200246 }
247
248 /* Check scope. */
Christopher Fauletff2613e2016-11-09 11:36:17 +0100249 if (len > 5 && strncmp(name, "proc.", 5) == 0) {
250 name += 5;
251 len -= 5;
252 *scope = SCOPE_PROC;
253 }
254 else if (len > 5 && strncmp(name, "sess.", 5) == 0) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200255 name += 5;
256 len -= 5;
257 *scope = SCOPE_SESS;
258 }
259 else if (len > 4 && strncmp(name, "txn.", 4) == 0) {
260 name += 4;
261 len -= 4;
262 *scope = SCOPE_TXN;
263 }
264 else if (len > 4 && strncmp(name, "req.", 4) == 0) {
265 name += 4;
266 len -= 4;
267 *scope = SCOPE_REQ;
268 }
269 else if (len > 4 && strncmp(name, "res.", 4) == 0) {
270 name += 4;
271 len -= 4;
272 *scope = SCOPE_RES;
273 }
Gaetan Rivet13a50432020-02-21 18:13:44 +0100274 else if (len > 6 && strncmp(name, "check.", 6) == 0) {
275 name += 6;
276 len -= 6;
277 *scope = SCOPE_CHECK;
278 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200279 else {
Willy Tarreau1402fef2021-09-03 10:12:55 +0200280 memprintf(err, "invalid variable name '%.*s'. A variable name must be start by its scope. "
281 "The scope can be 'proc', 'sess', 'txn', 'req', 'res' or 'check'", len, name);
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200282 return 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200283 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200284
285 /* Check variable name syntax. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200286 for (tmp = name; tmp < name + len; tmp++) {
Willy Tarreau90807112020-02-25 08:16:33 +0100287 if (!isalnum((unsigned char)*tmp) && *tmp != '_' && *tmp != '.') {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200288 memprintf(err, "invalid syntax at char '%s'", tmp);
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200289 return 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200290 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200291 }
Christopher Faulete95f2c32017-07-24 16:30:34 +0200292
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200293 *hash = XXH3(name, len, var_name_hash_seed);
294 return 1;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200295}
296
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200297/* This function returns the variable from the given list that matches
298 * <name_hash> or returns NULL if not found. It's only a linked list since it
299 * is not expected to have many variables per scope (a few tens at best).
300 * The caller is responsible for ensuring that <vars> is properly locked.
301 */
302static struct var *var_get(struct vars *vars, uint64_t name_hash)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200303{
304 struct var *var;
305
306 list_for_each_entry(var, &vars->head, l)
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200307 if (var->name_hash == name_hash)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200308 return var;
309 return NULL;
310}
311
312/* Returns 0 if fails, else returns 1. */
313static int smp_fetch_var(const struct arg *args, struct sample *smp, const char *kw, void *private)
314{
315 const struct var_desc *var_desc = &args[0].data.var;
Willy Tarreau54496a62021-09-03 12:00:13 +0200316 const struct buffer *def = NULL;
Christopher Faulete95f2c32017-07-24 16:30:34 +0200317
Willy Tarreau54496a62021-09-03 12:00:13 +0200318 if (args[1].type == ARGT_STR)
319 def = &args[1].data.str;
320
321 return vars_get_by_desc(var_desc, smp, def);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200322}
323
Remi Tricot-Le Breton25fccd52021-12-16 17:14:36 +0100324/*
325 * Clear the contents of a variable so that it can be reset directly.
326 * This function is used just before a variable is filled out of a sample's
327 * content.
328 */
329static inline void var_clear_buffer(struct sample *smp, struct vars *vars, struct var *var, int var_type)
330{
331 if (var_type == SMP_T_STR || var_type == SMP_T_BIN) {
332 ha_free(&var->data.u.str.area);
333 var_accounting_diff(vars, smp->sess, smp->strm,
334 -var->data.u.str.data);
335 }
336 else if (var_type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
337 ha_free(&var->data.u.meth.str.area);
338 var_accounting_diff(vars, smp->sess, smp->strm,
339 -var->data.u.meth.str.data);
340 }
341}
342
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200343/* This function tries to create a variable whose name hash is <name_hash> in
344 * scope <scope> and store sample <smp> as its value.
345 *
346 * The stream and session are extracted from <smp>, whose stream may be NULL
347 * when scope is SCOPE_SESS. In case there wouldn't be enough memory to store
348 * the sample while the variable was already created, it would be changed to
349 * a bool (which is memory-less).
Willy Tarreau7978c5c2021-09-07 14:24:07 +0200350 *
351 * Flags is a bitfield that may contain one of the following flags:
Willy Tarreau4994b572021-09-08 11:38:25 +0200352 * - VF_CREATEONLY: do nothing if the variable already exists (success).
Willy Tarreau3dc6dc32021-09-08 11:07:32 +0200353 * - VF_PERMANENT: this flag will be passed to the variable upon creation
Willy Tarreau7978c5c2021-09-07 14:24:07 +0200354 *
Remi Tricot-Le Breton51899d22021-12-16 17:14:37 +0100355 * - VF_COND_IFEXISTS: only set variable if it already exists
356 * - VF_COND_IFNOTEXISTS: only set variable if it did not exist yet
357 * - VF_COND_IFEMPTY: only set variable if sample is empty
358 * - VF_COND_IFNOTEMPTY: only set variable if sample is not empty
359 * - VF_COND_IFSET: only set variable if its type is not SMP_TYPE_ANY
360 * - VF_COND_IFNOTSET: only set variable if its type is ANY
361 * - VF_COND_IFGT: only set variable if its value is greater than the sample's
Ilya Shipitsin5e87bcf2021-12-25 11:45:52 +0500362 * - VF_COND_IFLT: only set variable if its value is less than the sample's
Remi Tricot-Le Breton51899d22021-12-16 17:14:37 +0100363 *
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200364 * It returns 0 on failure, non-zero on success.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200365 */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200366static int var_set(uint64_t name_hash, enum vars_scope scope, struct sample *smp, uint flags)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200367{
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200368 struct vars *vars;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200369 struct var *var;
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200370 int ret = 0;
Remi Tricot-Le Breton25fccd52021-12-16 17:14:36 +0100371 int previous_type = SMP_T_ANY;
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200372
373 vars = get_vars(smp->sess, smp->strm, scope);
374 if (!vars || vars->scope != scope)
375 return 0;
376
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200377 vars_wrlock(vars);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200378
379 /* Look for existing variable name. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200380 var = var_get(vars, name_hash);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200381
382 if (var) {
Willy Tarreau4994b572021-09-08 11:38:25 +0200383 if (flags & VF_CREATEONLY) {
384 ret = 1;
385 goto unlock;
386 }
Remi Tricot-Le Breton0b9e1902021-12-16 17:14:39 +0100387
388 if (flags & VF_COND_IFNOTEXISTS)
389 goto unlock;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200390 } else {
Remi Tricot-Le Breton51899d22021-12-16 17:14:37 +0100391 if (flags & VF_COND_IFEXISTS)
Willy Tarreau7978c5c2021-09-07 14:24:07 +0200392 goto unlock;
393
Joseph Herlant07676892018-11-15 09:19:50 -0800394 /* Check memory available. */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100395 if (!var_accounting_add(vars, smp->sess, smp->strm, sizeof(struct var)))
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200396 goto unlock;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200397
398 /* Create new entry. */
Willy Tarreaubafbe012017-11-24 17:34:44 +0100399 var = pool_alloc(var_pool);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200400 if (!var)
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200401 goto unlock;
Willy Tarreau2b718102021-04-21 07:32:39 +0200402 LIST_APPEND(&vars->head, &var->l);
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200403 var->name_hash = name_hash;
Willy Tarreau3dc6dc32021-09-08 11:07:32 +0200404 var->flags = flags & VF_PERMANENT;
Remi Tricot-Le Breton1bd98052021-12-16 17:14:35 +0100405 var->data.type = SMP_T_ANY;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200406 }
407
Remi Tricot-Le Breton0b9e1902021-12-16 17:14:39 +0100408 /* A variable of type SMP_T_ANY is considered as unset (either created
409 * and never set or unset-var was called on it).
410 */
411 if ((flags & VF_COND_IFSET && var->data.type == SMP_T_ANY) ||
412 (flags & VF_COND_IFNOTSET && var->data.type != SMP_T_ANY))
413 goto unlock;
414
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200415 /* Set type. */
Remi Tricot-Le Breton25fccd52021-12-16 17:14:36 +0100416 previous_type = var->data.type;
Thierry FOURNIER8c542ca2015-08-19 09:00:18 +0200417 var->data.type = smp->data.type;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200418
Remi Tricot-Le Breton0b9e1902021-12-16 17:14:39 +0100419 if (flags & VF_COND_IFEMPTY) {
420 switch(smp->data.type) {
421 case SMP_T_ANY:
422 case SMP_T_STR:
423 case SMP_T_BIN:
424 /* The actual test on the contents of the sample will be
425 * performed later.
426 */
427 break;
428 default:
429 /* The sample cannot be empty since it has a scalar type. */
430 var->data.type = previous_type;
431 goto unlock;
432 }
433 }
434
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200435 /* Copy data. If the data needs memory, the function can fail. */
436 switch (var->data.type) {
437 case SMP_T_BOOL:
Remi Tricot-Le Breton0b9e1902021-12-16 17:14:39 +0100438 var_clear_buffer(smp, vars, var, previous_type);
439 var->data.u.sint = smp->data.u.sint;
440 break;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200441 case SMP_T_SINT:
Remi Tricot-Le Breton0b9e1902021-12-16 17:14:39 +0100442 if (previous_type == var->data.type) {
443 if (((flags & VF_COND_IFGT) && !(var->data.u.sint > smp->data.u.sint)) ||
444 ((flags & VF_COND_IFLT) && !(var->data.u.sint < smp->data.u.sint)))
445 goto unlock;
446 }
Remi Tricot-Le Breton25fccd52021-12-16 17:14:36 +0100447 var_clear_buffer(smp, vars, var, previous_type);
Thierry FOURNIER136f9d32015-08-19 09:07:19 +0200448 var->data.u.sint = smp->data.u.sint;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200449 break;
450 case SMP_T_IPV4:
Remi Tricot-Le Breton25fccd52021-12-16 17:14:36 +0100451 var_clear_buffer(smp, vars, var, previous_type);
Thierry FOURNIER136f9d32015-08-19 09:07:19 +0200452 var->data.u.ipv4 = smp->data.u.ipv4;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200453 break;
454 case SMP_T_IPV6:
Remi Tricot-Le Breton25fccd52021-12-16 17:14:36 +0100455 var_clear_buffer(smp, vars, var, previous_type);
Thierry FOURNIER136f9d32015-08-19 09:07:19 +0200456 var->data.u.ipv6 = smp->data.u.ipv6;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200457 break;
458 case SMP_T_STR:
459 case SMP_T_BIN:
Remi Tricot-Le Breton0b9e1902021-12-16 17:14:39 +0100460 if ((flags & VF_COND_IFNOTEMPTY && !smp->data.u.str.data) ||
461 (flags & VF_COND_IFEMPTY && smp->data.u.str.data)) {
462 var->data.type = previous_type;
463 goto unlock;
464 }
Remi Tricot-Le Breton25fccd52021-12-16 17:14:36 +0100465 var_clear_buffer(smp, vars, var, previous_type);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200466 if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.str.data)) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200467 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200468 goto unlock;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200469 }
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200470
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200471 var->data.u.str.area = malloc(smp->data.u.str.data);
472 if (!var->data.u.str.area) {
473 var_accounting_diff(vars, smp->sess, smp->strm,
474 -smp->data.u.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200475 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200476 goto unlock;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200477 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200478 var->data.u.str.data = smp->data.u.str.data;
479 memcpy(var->data.u.str.area, smp->data.u.str.area,
480 var->data.u.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200481 break;
482 case SMP_T_METH:
Remi Tricot-Le Breton25fccd52021-12-16 17:14:36 +0100483 var_clear_buffer(smp, vars, var, previous_type);
Christopher Fauletd02210c2017-07-24 16:24:39 +0200484 var->data.u.meth.meth = smp->data.u.meth.meth;
485 if (smp->data.u.meth.meth != HTTP_METH_OTHER)
486 break;
487
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200488 if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.meth.str.data)) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200489 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200490 goto unlock;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200491 }
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200492
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200493 var->data.u.meth.str.area = malloc(smp->data.u.meth.str.data);
494 if (!var->data.u.meth.str.area) {
495 var_accounting_diff(vars, smp->sess, smp->strm,
496 -smp->data.u.meth.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200497 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200498 goto unlock;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200499 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200500 var->data.u.meth.str.data = smp->data.u.meth.str.data;
501 var->data.u.meth.str.size = smp->data.u.meth.str.data;
502 memcpy(var->data.u.meth.str.area, smp->data.u.meth.str.area,
503 var->data.u.meth.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200504 break;
505 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200506
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200507 /* OK, now done */
508 ret = 1;
509 unlock:
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200510 vars_wrunlock(vars);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200511 return ret;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200512}
513
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200514/* Deletes a variable matching name hash <name_hash> and scope <scope> for the
515 * session and stream found in <smp>. Note that stream may be null for
516 * SCOPE_SESS. Returns 0 if the scope was not found otherwise 1.
Willy Tarreaud378eb82021-09-07 11:44:41 +0200517 */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200518static int var_unset(uint64_t name_hash, enum vars_scope scope, struct sample *smp)
Christopher Faulet85d79c92016-11-09 16:54:56 +0100519{
520 struct vars *vars;
521 struct var *var;
522 unsigned int size = 0;
523
Willy Tarreauf37b1402019-06-04 16:27:36 +0200524 vars = get_vars(smp->sess, smp->strm, scope);
525 if (!vars || vars->scope != scope)
Christopher Faulet85d79c92016-11-09 16:54:56 +0100526 return 0;
527
528 /* Look for existing variable name. */
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200529 vars_wrlock(vars);
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200530 var = var_get(vars, name_hash);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100531 if (var) {
Willy Tarreauc1c88f42021-09-08 15:03:58 +0200532 size = var_clear(var, 0);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100533 var_accounting_diff(vars, smp->sess, smp->strm, -size);
534 }
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200535 vars_wrunlock(vars);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100536 return 1;
537}
538
Remi Tricot-Le Breton51899d22021-12-16 17:14:37 +0100539
540/*
541 * Convert a string set-var condition into its numerical value.
542 * The corresponding bit is set in the <cond_bitmap> parameter if the
543 * <cond> is known.
544 * Returns 1 in case of success.
545 */
546static int vars_parse_cond_param(const struct buffer *cond, uint *cond_bitmap, char **err)
547{
548 struct var_set_condition *cond_elt = &conditions_array[0];
549
550 /* The conditions array is NULL terminated. */
551 while (cond_elt->cond_str) {
552 if (chunk_strcmp(cond, cond_elt->cond_str) == 0) {
553 *cond_bitmap |= cond_elt->flag;
554 break;
555 }
556 ++cond_elt;
557 }
558
559 if (cond_elt->cond_str == NULL && err)
560 memprintf(err, "unknown condition \"%.*s\"", (int)cond->data, cond->area);
561
562 return cond_elt->cond_str != NULL;
563}
564
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200565/* Returns 0 if fails, else returns 1. */
566static int smp_conv_store(const struct arg *args, struct sample *smp, void *private)
567{
Remi Tricot-Le Breton51899d22021-12-16 17:14:37 +0100568 uint conditions = 0;
569 int cond_idx = 1;
570
571 while (args[cond_idx].type == ARGT_STR) {
572 if (vars_parse_cond_param(&args[cond_idx++].data.str, &conditions, NULL) == 0)
573 break;
574 }
575
576 return var_set(args[0].data.var.name_hash, args[0].data.var.scope, smp, conditions);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200577}
578
Christopher Faulet85d79c92016-11-09 16:54:56 +0100579/* Returns 0 if fails, else returns 1. */
580static int smp_conv_clear(const struct arg *args, struct sample *smp, void *private)
581{
Remi Tricot-Le Bretonbb3e80e2021-11-26 18:08:39 +0100582 return var_unset(args[0].data.var.name_hash, args[0].data.var.scope, smp);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100583}
584
Joseph Herlant07676892018-11-15 09:19:50 -0800585/* This functions check an argument entry and fill it with a variable
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200586 * type. The argumen must be a string. If the variable lookup fails,
Joseph Herlant07676892018-11-15 09:19:50 -0800587 * the function returns 0 and fill <err>, otherwise it returns 1.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200588 */
589int vars_check_arg(struct arg *arg, char **err)
590{
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200591 enum vars_scope scope;
Willy Tarreaudf8eeb12021-09-08 11:07:32 +0200592 struct sample empty_smp = { };
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200593 uint64_t hash;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200594
595 /* Check arg type. */
596 if (arg->type != ARGT_STR) {
597 memprintf(err, "unexpected argument type");
598 return 0;
599 }
600
601 /* Register new variable name. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200602 if (!vars_hash_name(arg->data.str.area, arg->data.str.data, &scope, &hash, err))
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200603 return 0;
604
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200605 if (scope == SCOPE_PROC && !var_set(hash, scope, &empty_smp, VF_CREATEONLY|VF_PERMANENT))
Willy Tarreaudf8eeb12021-09-08 11:07:32 +0200606 return 0;
607
Tim Duesterhusa6cc7e82019-05-13 10:53:29 +0200608 /* properly destroy the chunk */
609 chunk_destroy(&arg->data.str);
610
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200611 /* Use the global variable name pointer. */
612 arg->type = ARGT_VAR;
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200613 arg->data.var.name_hash = hash;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200614 arg->data.var.scope = scope;
615 return 1;
616}
617
Remi Tricot-Le Breton70553012021-12-16 17:14:34 +0100618/* This function stores a sample in a variable unless it is of type "proc" and
619 * not defined yet.
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200620 * Returns zero on failure and non-zero otherwise. The variable not being
621 * defined is treated as a failure.
Christopher Faulet09c9df22016-10-31 11:05:37 +0100622 */
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200623int vars_set_by_name_ifexist(const char *name, size_t len, struct sample *smp)
Christopher Faulet09c9df22016-10-31 11:05:37 +0100624{
625 enum vars_scope scope;
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200626 uint64_t hash;
Christopher Faulet09c9df22016-10-31 11:05:37 +0100627
628 /* Resolve name and scope. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200629 if (!vars_hash_name(name, len, &scope, &hash, NULL))
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200630 return 0;
Christopher Faulet09c9df22016-10-31 11:05:37 +0100631
Remi Tricot-Le Breton70553012021-12-16 17:14:34 +0100632 /* Variable creation is allowed for all scopes apart from the PROC one. */
Remi Tricot-Le Breton51899d22021-12-16 17:14:37 +0100633 return var_set(hash, scope, smp, (scope == SCOPE_PROC) ? VF_COND_IFEXISTS : 0);
Christopher Faulet09c9df22016-10-31 11:05:37 +0100634}
635
636
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200637/* This function stores a sample in a variable.
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200638 * Returns zero on failure and non-zero otherwise.
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200639 */
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200640int vars_set_by_name(const char *name, size_t len, struct sample *smp)
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200641{
642 enum vars_scope scope;
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200643 uint64_t hash;
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200644
645 /* Resolve name and scope. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200646 if (!vars_hash_name(name, len, &scope, &hash, NULL))
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200647 return 0;
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200648
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200649 return var_set(hash, scope, smp, 0);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200650}
651
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200652/* This function unsets a variable if it was already defined.
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200653 * Returns zero on failure and non-zero otherwise.
Christopher Faulet85d79c92016-11-09 16:54:56 +0100654 */
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200655int vars_unset_by_name_ifexist(const char *name, size_t len, struct sample *smp)
Christopher Faulet85d79c92016-11-09 16:54:56 +0100656{
657 enum vars_scope scope;
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200658 uint64_t hash;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100659
660 /* Resolve name and scope. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200661 if (!vars_hash_name(name, len, &scope, &hash, NULL))
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200662 return 0;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100663
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200664 return var_unset(hash, scope, smp);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100665}
666
667
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200668/* This retrieves variable whose hash matches <name_hash> from variables <vars>,
669 * and if found and not empty, duplicates the result into sample <smp>.
670 * smp_dup() is used in order to release the variables lock ASAP (so a pre-
671 * allocated chunk is obtained via get_trash_shunk()). The variables' lock is
672 * used for reads.
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200673 *
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200674 * The function returns 0 if the variable was not found and no default
675 * value was provided in <def>, otherwise 1 with the sample filled.
676 * Default values are always returned as strings.
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200677 */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200678static int var_to_smp(struct vars *vars, uint64_t name_hash, struct sample *smp, const struct buffer *def)
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200679{
680 struct var *var;
681
682 /* Get the variable entry. */
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200683 vars_rdlock(vars);
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200684 var = var_get(vars, name_hash);
Willy Tarreau63c30662021-09-08 13:58:19 +0200685 if (!var || !var->data.type) {
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200686 if (!def) {
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200687 vars_rdunlock(vars);
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200688 return 0;
689 }
690
691 /* not found but we have a default value */
692 smp->data.type = SMP_T_STR;
693 smp->data.u.str = *def;
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200694 }
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200695 else
696 smp->data = var->data;
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200697
698 /* Copy sample. */
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200699 smp_dup(smp);
700
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200701 vars_rdunlock(vars);
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200702 return 1;
703}
704
Dragan Dosen14518f22021-02-22 17:20:01 +0100705/* This function fills a sample with the variable content.
706 *
707 * Keep in mind that a sample content is duplicated by using smp_dup()
708 * and it therefore uses a pre-allocated trash chunk as returned by
709 * get_trash_chunk().
710 *
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200711 * If the variable is not valid in this scope, 0 is always returned.
712 * If the variable is valid but not found, either the default value
713 * <def> is returned if not NULL, or zero is returned.
714 *
Dragan Dosen14518f22021-02-22 17:20:01 +0100715 * Returns 1 if the sample is filled, otherwise it returns 0.
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200716 */
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200717int vars_get_by_name(const char *name, size_t len, struct sample *smp, const struct buffer *def)
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200718{
719 struct vars *vars;
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200720 enum vars_scope scope;
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200721 uint64_t hash;
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200722
723 /* Resolve name and scope. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200724 if (!vars_hash_name(name, len, &scope, &hash, NULL))
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200725 return 0;
726
727 /* Select "vars" pool according with the scope. */
Willy Tarreauf37b1402019-06-04 16:27:36 +0200728 vars = get_vars(smp->sess, smp->strm, scope);
729 if (!vars || vars->scope != scope)
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200730 return 0;
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200731
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200732 return var_to_smp(vars, hash, smp, def);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200733}
734
Dragan Dosen14518f22021-02-22 17:20:01 +0100735/* This function fills a sample with the content of the variable described
736 * by <var_desc>.
737 *
738 * Keep in mind that a sample content is duplicated by using smp_dup()
739 * and it therefore uses a pre-allocated trash chunk as returned by
740 * get_trash_chunk().
741 *
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200742 * If the variable is not valid in this scope, 0 is always returned.
743 * If the variable is valid but not found, either the default value
744 * <def> is returned if not NULL, or zero is returned.
745 *
Dragan Dosen14518f22021-02-22 17:20:01 +0100746 * Returns 1 if the sample is filled, otherwise it returns 0.
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200747 */
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200748int vars_get_by_desc(const struct var_desc *var_desc, struct sample *smp, const struct buffer *def)
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200749{
750 struct vars *vars;
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200751
752 /* Select "vars" pool according with the scope. */
Willy Tarreauf37b1402019-06-04 16:27:36 +0200753 vars = get_vars(smp->sess, smp->strm, var_desc->scope);
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200754
Joseph Herlant07676892018-11-15 09:19:50 -0800755 /* Check if the scope is available a this point of processing. */
Willy Tarreauf37b1402019-06-04 16:27:36 +0200756 if (!vars || vars->scope != var_desc->scope)
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200757 return 0;
758
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200759 return var_to_smp(vars, var_desc->name_hash, smp, def);
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200760}
761
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200762/* Always returns ACT_RET_CONT even if an error occurs. */
763static enum act_return action_store(struct act_rule *rule, struct proxy *px,
Willy Tarreau658b85b2015-09-27 10:00:49 +0200764 struct session *sess, struct stream *s, int flags)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200765{
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200766 struct buffer *fmtstr = NULL;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200767 struct sample smp;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200768 int dir;
769
770 switch (rule->from) {
Jaroslaw Rzeszótkoc8637032021-11-02 16:56:05 +0100771 case ACT_F_TCP_REQ_CON: dir = SMP_OPT_DIR_REQ; break;
Willy Tarreau620408f2016-10-21 16:37:51 +0200772 case ACT_F_TCP_REQ_SES: dir = SMP_OPT_DIR_REQ; break;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200773 case ACT_F_TCP_REQ_CNT: dir = SMP_OPT_DIR_REQ; break;
774 case ACT_F_TCP_RES_CNT: dir = SMP_OPT_DIR_RES; break;
775 case ACT_F_HTTP_REQ: dir = SMP_OPT_DIR_REQ; break;
776 case ACT_F_HTTP_RES: dir = SMP_OPT_DIR_RES; break;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +0100777 case ACT_F_TCP_CHK: dir = SMP_OPT_DIR_REQ; break;
Willy Tarreau01d580a2021-03-26 11:11:34 +0100778 case ACT_F_CFG_PARSER: dir = SMP_OPT_DIR_REQ; break; /* not used anyway */
Willy Tarreau2f836de2021-03-26 15:36:44 +0100779 case ACT_F_CLI_PARSER: dir = SMP_OPT_DIR_REQ; break; /* not used anyway */
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200780 default:
781 send_log(px, LOG_ERR, "Vars: internal error while execute action store.");
782 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Christopher Faulet767a84b2017-11-24 16:50:31 +0100783 ha_alert("Vars: internal error while execute action store.\n");
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200784 return ACT_RET_CONT;
785 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200786
787 /* Process the expression. */
788 memset(&smp, 0, sizeof(smp));
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200789
790 if (!LIST_ISEMPTY(&rule->arg.vars.fmt)) {
791 /* a format-string is used */
792
793 fmtstr = alloc_trash_chunk();
794 if (!fmtstr) {
795 send_log(px, LOG_ERR, "Vars: memory allocation failure while processing store rule.");
796 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
797 ha_alert("Vars: memory allocation failure while processing store rule.\n");
798 return ACT_RET_CONT;
799 }
800
801 /* execute the log-format expression */
802 fmtstr->data = sess_build_logline(sess, s, fmtstr->area, fmtstr->size, &rule->arg.vars.fmt);
803
804 /* convert it to a sample of type string as it's what the vars
805 * API consumes, and store it.
806 */
807 smp_set_owner(&smp, px, sess, s, 0);
808 smp.data.type = SMP_T_STR;
809 smp.data.u.str = *fmtstr;
Remi Tricot-Le Breton0b9e1902021-12-16 17:14:39 +0100810 var_set(rule->arg.vars.name_hash, rule->arg.vars.scope, &smp, rule->arg.vars.conditions);
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200811 }
812 else {
813 /* an expression is used */
814 if (!sample_process(px, sess, s, dir|SMP_OPT_FINAL,
815 rule->arg.vars.expr, &smp))
816 return ACT_RET_CONT;
817 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200818
819 /* Store the sample, and ignore errors. */
Remi Tricot-Le Breton0b9e1902021-12-16 17:14:39 +0100820 var_set(rule->arg.vars.name_hash, rule->arg.vars.scope, &smp, rule->arg.vars.conditions);
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200821 free_trash_chunk(fmtstr);
Thierry FOURNIER24ff6c62015-08-06 08:52:53 +0200822 return ACT_RET_CONT;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200823}
824
Christopher Faulet85d79c92016-11-09 16:54:56 +0100825/* Always returns ACT_RET_CONT even if an error occurs. */
826static enum act_return action_clear(struct act_rule *rule, struct proxy *px,
827 struct session *sess, struct stream *s, int flags)
828{
829 struct sample smp;
830
831 memset(&smp, 0, sizeof(smp));
832 smp_set_owner(&smp, px, sess, s, SMP_OPT_FINAL);
833
834 /* Clear the variable using the sample context, and ignore errors. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200835 var_unset(rule->arg.vars.name_hash, rule->arg.vars.scope, &smp);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100836 return ACT_RET_CONT;
837}
838
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200839static void release_store_rule(struct act_rule *rule)
840{
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200841 struct logformat_node *lf, *lfb;
Willy Tarreauc77bad22021-09-03 10:58:07 +0200842
843 list_for_each_entry_safe(lf, lfb, &rule->arg.vars.fmt, list) {
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200844 LIST_DELETE(&lf->list);
845 release_sample_expr(lf->expr);
846 free(lf->arg);
847 free(lf);
848 }
849
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200850 release_sample_expr(rule->arg.vars.expr);
851}
852
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200853/* This two function checks the variable name and replace the
854 * configuration string name by the global string name. its
855 * the same string, but the global pointer can be easy to
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200856 * compare. They return non-zero on success, zero on failure.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200857 *
858 * The first function checks a sample-fetch and the second
859 * checks a converter.
860 */
861static int smp_check_var(struct arg *args, char **err)
862{
863 return vars_check_arg(&args[0], err);
864}
865
866static int conv_check_var(struct arg *args, struct sample_conv *conv,
867 const char *file, int line, char **err_msg)
868{
Remi Tricot-Le Breton51899d22021-12-16 17:14:37 +0100869 int cond_idx = 1;
870 uint conditions = 0;
871 int retval = vars_check_arg(&args[0], err_msg);
872
873 while (retval && args[cond_idx].type == ARGT_STR)
874 retval = vars_parse_cond_param(&args[cond_idx++].data.str, &conditions, err_msg);
875
876 return retval;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200877}
878
879/* This function is a common parser for using variables. It understands
880 * the format:
881 *
Remi Tricot-Le Bretonbb6bc952021-12-16 17:14:38 +0100882 * set-var-fmt(<variable-name>[,<cond> ...]) <format-string>
883 * set-var(<variable-name>[,<cond> ...]) <expression>
Willy Tarreau4b7531f2019-06-04 16:43:29 +0200884 * unset-var(<variable-name>)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200885 *
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200886 * It returns ACT_RET_PRS_ERR if fails and <err> is filled with an error
887 * message. Otherwise, it returns ACT_RET_PRS_OK and the variable <expr>
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100888 * is filled with the pointer to the expression to execute. The proxy is
889 * only used to retrieve the ->conf entries.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200890 */
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200891static enum act_parse_ret parse_store(const char **args, int *arg, struct proxy *px,
892 struct act_rule *rule, char **err)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200893{
894 const char *var_name = args[*arg-1];
895 int var_len;
Thierry FOURNIER48a9cd12015-07-28 19:00:28 +0200896 const char *kw_name;
Christopher Faulet7a06ffb2021-10-13 17:22:17 +0200897 int flags = 0, set_var = 0; /* 0=unset-var, 1=set-var, 2=set-var-fmt */
Willy Tarreaudf8eeb12021-09-08 11:07:32 +0200898 struct sample empty_smp = { };
Remi Tricot-Le Bretonbb6bc952021-12-16 17:14:38 +0100899 struct ist condition = IST_NULL;
900 struct ist var = IST_NULL;
901 struct ist varname_ist = IST_NULL;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200902
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200903 if (strncmp(var_name, "set-var-fmt", 11) == 0) {
904 var_name += 11;
905 set_var = 2;
906 }
907 else if (strncmp(var_name, "set-var", 7) == 0) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100908 var_name += 7;
909 set_var = 1;
910 }
Willy Tarreau28192102021-09-02 18:46:22 +0200911 else if (strncmp(var_name, "unset-var", 9) == 0) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100912 var_name += 9;
913 set_var = 0;
914 }
915
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200916 if (*var_name != '(') {
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200917 memprintf(err, "invalid or incomplete action '%s'. Expects 'set-var(<var-name>)', 'set-var-fmt(<var-name>)' or 'unset-var(<var-name>)'",
Christopher Faulet85d79c92016-11-09 16:54:56 +0100918 args[*arg-1]);
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200919 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200920 }
921 var_name++; /* jump the '(' */
922 var_len = strlen(var_name);
923 var_len--; /* remove the ')' */
924 if (var_name[var_len] != ')') {
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200925 memprintf(err, "incomplete argument after action '%s'. Expects 'set-var(<var-name>)', 'set-var-fmt(<var-name>)' or 'unset-var(<var-name>)'",
Christopher Faulet85d79c92016-11-09 16:54:56 +0100926 args[*arg-1]);
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200927 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200928 }
929
Remi Tricot-Le Bretonbb6bc952021-12-16 17:14:38 +0100930 /* Parse the optional conditions. */
931 var = ist2(var_name, var_len);
932 varname_ist = istsplit(&var, ',');
933 var_len = istlen(varname_ist);
934
935 condition = istsplit(&var, ',');
936
937 if (istlen(condition) && set_var == 0) {
938 memprintf(err, "unset-var does not expect parameters after the variable name. Only \"set-var\" and \"set-var-fmt\" manage conditions");
939 return ACT_RET_PRS_ERR;
940 }
941
942 while (istlen(condition)) {
943 struct buffer cond = {};
944
945 chunk_initlen(&cond, istptr(condition), 0, istlen(condition));
946 if (vars_parse_cond_param(&cond, &rule->arg.vars.conditions, err) == 0)
947 return ACT_RET_PRS_ERR;
948
949 condition = istsplit(&var, ',');
950 }
951
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200952 LIST_INIT(&rule->arg.vars.fmt);
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200953 if (!vars_hash_name(var_name, var_len, &rule->arg.vars.scope, &rule->arg.vars.name_hash, err))
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200954 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200955
Willy Tarreaudf8eeb12021-09-08 11:07:32 +0200956 if (rule->arg.vars.scope == SCOPE_PROC &&
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200957 !var_set(rule->arg.vars.name_hash, rule->arg.vars.scope, &empty_smp, VF_CREATEONLY|VF_PERMANENT))
Willy Tarreaudf8eeb12021-09-08 11:07:32 +0200958 return 0;
959
Christopher Faulet85d79c92016-11-09 16:54:56 +0100960 /* There is no fetch method when variable is unset. Just set the right
961 * action and return. */
962 if (!set_var) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100963 rule->action = ACT_CUSTOM;
964 rule->action_ptr = action_clear;
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200965 rule->release_ptr = release_store_rule;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100966 return ACT_RET_PRS_OK;
967 }
968
Thierry FOURNIER48a9cd12015-07-28 19:00:28 +0200969 kw_name = args[*arg-1];
970
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200971 switch (rule->from) {
Jaroslaw Rzeszótkoc8637032021-11-02 16:56:05 +0100972 case ACT_F_TCP_REQ_CON:
973 flags = SMP_VAL_FE_CON_ACC;
974 px->conf.args.ctx = ARGC_TCO;
975 break;
Willy Tarreau843096d2021-09-02 19:03:07 +0200976 case ACT_F_TCP_REQ_SES:
977 flags = SMP_VAL_FE_SES_ACC;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200978 px->conf.args.ctx = ARGC_TSE;
Willy Tarreau843096d2021-09-02 19:03:07 +0200979 break;
980 case ACT_F_TCP_REQ_CNT:
Christopher Faulet7a06ffb2021-10-13 17:22:17 +0200981 if (px->cap & PR_CAP_FE)
982 flags |= SMP_VAL_FE_REQ_CNT;
983 if (px->cap & PR_CAP_BE)
984 flags |= SMP_VAL_BE_REQ_CNT;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200985 px->conf.args.ctx = ARGC_TRQ;
Willy Tarreau843096d2021-09-02 19:03:07 +0200986 break;
987 case ACT_F_TCP_RES_CNT:
Christopher Faulet7a06ffb2021-10-13 17:22:17 +0200988 if (px->cap & PR_CAP_FE)
989 flags |= SMP_VAL_FE_RES_CNT;
990 if (px->cap & PR_CAP_BE)
991 flags |= SMP_VAL_BE_RES_CNT;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200992 px->conf.args.ctx = ARGC_TRS;
Willy Tarreau843096d2021-09-02 19:03:07 +0200993 break;
994 case ACT_F_HTTP_REQ:
Christopher Faulet7a06ffb2021-10-13 17:22:17 +0200995 if (px->cap & PR_CAP_FE)
996 flags |= SMP_VAL_FE_HRQ_HDR;
997 if (px->cap & PR_CAP_BE)
998 flags |= SMP_VAL_BE_HRQ_HDR;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200999 px->conf.args.ctx = ARGC_HRQ;
Willy Tarreau843096d2021-09-02 19:03:07 +02001000 break;
1001 case ACT_F_HTTP_RES:
Christopher Faulet7a06ffb2021-10-13 17:22:17 +02001002 if (px->cap & PR_CAP_FE)
1003 flags |= SMP_VAL_FE_HRS_HDR;
1004 if (px->cap & PR_CAP_BE)
1005 flags |= SMP_VAL_BE_HRS_HDR;
Willy Tarreau54b96d92021-09-02 19:46:08 +02001006 px->conf.args.ctx = ARGC_HRS;
Willy Tarreau843096d2021-09-02 19:03:07 +02001007 break;
1008 case ACT_F_TCP_CHK:
1009 flags = SMP_VAL_BE_CHK_RUL;
Willy Tarreau54b96d92021-09-02 19:46:08 +02001010 px->conf.args.ctx = ARGC_TCK;
Willy Tarreau843096d2021-09-02 19:03:07 +02001011 break;
1012 case ACT_F_CFG_PARSER:
1013 flags = SMP_VAL_CFG_PARSER;
Willy Tarreau54b96d92021-09-02 19:46:08 +02001014 px->conf.args.ctx = ARGC_CFG;
Willy Tarreau843096d2021-09-02 19:03:07 +02001015 break;
1016 case ACT_F_CLI_PARSER:
1017 flags = SMP_VAL_CLI_PARSER;
Willy Tarreau54b96d92021-09-02 19:46:08 +02001018 px->conf.args.ctx = ARGC_CLI;
Willy Tarreau843096d2021-09-02 19:03:07 +02001019 break;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +02001020 default:
1021 memprintf(err,
1022 "internal error, unexpected rule->from=%d, please report this bug!",
1023 rule->from);
Thierry FOURNIERafa80492015-08-19 09:04:15 +02001024 return ACT_RET_PRS_ERR;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +02001025 }
Willy Tarreau54b96d92021-09-02 19:46:08 +02001026
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001027 if (set_var == 2) { /* set-var-fmt */
1028 if (!parse_logformat_string(args[*arg], px, &rule->arg.vars.fmt, 0, flags, err))
1029 return ACT_RET_PRS_ERR;
Willy Tarreau54b96d92021-09-02 19:46:08 +02001030
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001031 (*arg)++;
1032
1033 /* for late error reporting */
1034 free(px->conf.lfs_file);
1035 px->conf.lfs_file = strdup(px->conf.args.file);
1036 px->conf.lfs_line = px->conf.args.line;
1037 } else {
1038 /* set-var */
1039 rule->arg.vars.expr = sample_parse_expr((char **)args, arg, px->conf.args.file,
Christopher Faulet6ff7de52021-10-13 15:18:36 +02001040 px->conf.args.line, err, &px->conf.args, NULL);
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001041 if (!rule->arg.vars.expr)
1042 return ACT_RET_PRS_ERR;
1043
1044 if (!(rule->arg.vars.expr->fetch->val & flags)) {
1045 memprintf(err,
1046 "fetch method '%s' extracts information from '%s', none of which is available here",
1047 kw_name, sample_src_names(rule->arg.vars.expr->fetch->use));
1048 free(rule->arg.vars.expr);
1049 return ACT_RET_PRS_ERR;
1050 }
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +02001051 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001052
Thierry FOURNIER42148732015-09-02 17:17:33 +02001053 rule->action = ACT_CUSTOM;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +02001054 rule->action_ptr = action_store;
Tim Duesterhus01a0ce32020-06-14 17:27:36 +02001055 rule->release_ptr = release_store_rule;
Thierry FOURNIERafa80492015-08-19 09:04:15 +02001056 return ACT_RET_PRS_OK;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001057}
1058
Willy Tarreau13d2ba22021-03-26 11:38:08 +01001059
1060/* parses a global "set-var" directive. It will create a temporary rule and
1061 * expression that are parsed, processed, and released on the fly so that we
1062 * respect the real set-var syntax. These directives take the following format:
1063 * set-var <name> <expression>
Willy Tarreau753d4db2021-09-03 09:02:47 +02001064 * set-var-fmt <name> <fmt>
Willy Tarreau13d2ba22021-03-26 11:38:08 +01001065 * Note that parse_store() expects "set-var(name) <expression>" so we have to
1066 * temporarily replace the keyword here.
1067 */
1068static int vars_parse_global_set_var(char **args, int section_type, struct proxy *curpx,
1069 const struct proxy *defpx, const char *file, int line,
1070 char **err)
1071{
1072 struct proxy px = {
Willy Tarreau9c204332021-09-03 08:19:43 +02001073 .id = "CFG",
Willy Tarreau13d2ba22021-03-26 11:38:08 +01001074 .conf.args.file = file,
1075 .conf.args.line = line,
1076 };
1077 struct act_rule rule = {
1078 .arg.vars.scope = SCOPE_PROC,
1079 .from = ACT_F_CFG_PARSER,
Willy Tarreauc9e48682021-10-11 09:13:07 +02001080 .conf.file = (char *)file,
1081 .conf.line = line,
Willy Tarreau13d2ba22021-03-26 11:38:08 +01001082 };
Willy Tarreau753d4db2021-09-03 09:02:47 +02001083 enum obj_type objt = OBJ_TYPE_NONE;
1084 struct session *sess = NULL;
Willy Tarreau13d2ba22021-03-26 11:38:08 +01001085 enum act_parse_ret p_ret;
1086 char *old_arg1;
1087 char *tmp_arg1;
1088 int arg = 2; // variable name
1089 int ret = -1;
Willy Tarreau753d4db2021-09-03 09:02:47 +02001090 int use_fmt = 0;
Willy Tarreau13d2ba22021-03-26 11:38:08 +01001091
1092 LIST_INIT(&px.conf.args.list);
1093
Willy Tarreau753d4db2021-09-03 09:02:47 +02001094 use_fmt = strcmp(args[0], "set-var-fmt") == 0;
1095
Willy Tarreau13d2ba22021-03-26 11:38:08 +01001096 if (!*args[1] || !*args[2]) {
Willy Tarreau753d4db2021-09-03 09:02:47 +02001097 if (use_fmt)
1098 memprintf(err, "'%s' requires a process-wide variable name ('proc.<name>') and a format string.", args[0]);
1099 else
1100 memprintf(err, "'%s' requires a process-wide variable name ('proc.<name>') and a sample expression.", args[0]);
Willy Tarreau13d2ba22021-03-26 11:38:08 +01001101 goto end;
1102 }
1103
1104 tmp_arg1 = NULL;
Willy Tarreau753d4db2021-09-03 09:02:47 +02001105 if (!memprintf(&tmp_arg1, "set-var%s(%s)", use_fmt ? "-fmt" : "", args[1]))
Willy Tarreau13d2ba22021-03-26 11:38:08 +01001106 goto end;
1107
1108 /* parse_store() will always return a message in <err> on error */
1109 old_arg1 = args[1]; args[1] = tmp_arg1;
1110 p_ret = parse_store((const char **)args, &arg, &px, &rule, err);
1111 free(args[1]); args[1] = old_arg1;
1112
1113 if (p_ret != ACT_RET_PRS_OK)
1114 goto end;
1115
1116 if (rule.arg.vars.scope != SCOPE_PROC) {
1117 memprintf(err, "'%s': cannot set variable '%s', only scope 'proc' is permitted in the global section.", args[0], args[1]);
1118 goto end;
1119 }
1120
1121 if (smp_resolve_args(&px, err) != 0) {
1122 release_sample_expr(rule.arg.vars.expr);
1123 indent_msg(err, 2);
1124 goto end;
1125 }
1126
Willy Tarreau753d4db2021-09-03 09:02:47 +02001127 if (use_fmt && !(sess = session_new(&px, NULL, &objt))) {
1128 release_sample_expr(rule.arg.vars.expr);
1129 memprintf(err, "'%s': out of memory when trying to set variable '%s' in the global section.", args[0], args[1]);
1130 goto end;
1131 }
1132
1133 action_store(&rule, &px, sess, NULL, 0);
Willy Tarreau13d2ba22021-03-26 11:38:08 +01001134 release_sample_expr(rule.arg.vars.expr);
Willy Tarreau753d4db2021-09-03 09:02:47 +02001135 if (sess)
1136 session_free(sess);
Willy Tarreau13d2ba22021-03-26 11:38:08 +01001137
1138 ret = 0;
1139 end:
1140 return ret;
1141}
1142
Willy Tarreauc35eb382021-03-26 14:51:31 +01001143/* parse CLI's "get var <name>" */
1144static int vars_parse_cli_get_var(char **args, char *payload, struct appctx *appctx, void *private)
1145{
1146 struct vars *vars;
Willy Tarreau374edc72021-04-01 17:01:43 +02001147 struct sample smp = { };
Willy Tarreauc35eb382021-03-26 14:51:31 +01001148 int i;
1149
1150 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
1151 return 1;
1152
1153 if (!*args[2])
1154 return cli_err(appctx, "Missing process-wide variable identifier.\n");
1155
1156 vars = get_vars(NULL, NULL, SCOPE_PROC);
1157 if (!vars || vars->scope != SCOPE_PROC)
1158 return 0;
1159
Willy Tarreaue352b9d2021-09-03 11:52:38 +02001160 if (!vars_get_by_name(args[2], strlen(args[2]), &smp, NULL))
Willy Tarreauc35eb382021-03-26 14:51:31 +01001161 return cli_err(appctx, "Variable not found.\n");
1162
1163 /* the sample returned by vars_get_by_name() is allocated into a trash
1164 * chunk so we have no constraint to manipulate it.
1165 */
1166 chunk_printf(&trash, "%s: type=%s value=", args[2], smp_to_type[smp.data.type]);
1167
1168 if (!sample_casts[smp.data.type][SMP_T_STR] ||
1169 !sample_casts[smp.data.type][SMP_T_STR](&smp)) {
1170 chunk_appendf(&trash, "(undisplayable)");
1171 } else {
1172 /* Display the displayable chars*. */
1173 b_putchr(&trash, '<');
1174 for (i = 0; i < smp.data.u.str.data; i++) {
1175 if (isprint((unsigned char)smp.data.u.str.area[i]))
1176 b_putchr(&trash, smp.data.u.str.area[i]);
1177 else
1178 b_putchr(&trash, '.');
1179 }
1180 b_putchr(&trash, '>');
1181 b_putchr(&trash, 0);
1182 }
1183 return cli_msg(appctx, LOG_INFO, trash.area);
1184}
1185
Willy Tarreaue93bff42021-09-03 09:47:37 +02001186/* parse CLI's "set var <name>". It accepts:
1187 * - set var <name> <expression>
1188 * - set var <name> expr <expression>
1189 * - set var <name> fmt <format>
1190 */
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001191static int vars_parse_cli_set_var(char **args, char *payload, struct appctx *appctx, void *private)
1192{
1193 struct proxy px = {
1194 .id = "CLI",
1195 .conf.args.file = "CLI",
1196 .conf.args.line = 0,
1197 };
1198 struct act_rule rule = {
1199 .arg.vars.scope = SCOPE_PROC,
1200 .from = ACT_F_CLI_PARSER,
Willy Tarreauc9e48682021-10-11 09:13:07 +02001201 .conf.file = "CLI",
1202 .conf.line = 0,
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001203 };
Willy Tarreaue93bff42021-09-03 09:47:37 +02001204 enum obj_type objt = OBJ_TYPE_NONE;
1205 struct session *sess = NULL;
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001206 enum act_parse_ret p_ret;
Willy Tarreaue93bff42021-09-03 09:47:37 +02001207 const char *tmp_args[3];
1208 int tmp_arg;
1209 char *tmp_act;
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001210 char *err = NULL;
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001211 int nberr;
Willy Tarreaue93bff42021-09-03 09:47:37 +02001212 int use_fmt = 0;
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001213
1214 LIST_INIT(&px.conf.args.list);
1215
1216 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
1217 return 1;
1218
Willy Tarreaue93bff42021-09-03 09:47:37 +02001219 if (!*args[2])
1220 return cli_err(appctx, "Missing process-wide variable identifier.\n");
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001221
Willy Tarreaue93bff42021-09-03 09:47:37 +02001222 if (!*args[3])
1223 return cli_err(appctx, "Missing either 'expr', 'fmt' or expression.\n");
1224
1225 if (*args[4]) {
1226 /* this is the long format */
1227 if (strcmp(args[3], "fmt") == 0)
1228 use_fmt = 1;
1229 else if (strcmp(args[3], "expr") != 0) {
1230 memprintf(&err, "'%s %s': arg type must be either 'expr' or 'fmt' but got '%s'.", args[0], args[1], args[3]);
1231 goto fail;
1232 }
1233 }
1234
1235 tmp_act = NULL;
1236 if (!memprintf(&tmp_act, "set-var%s(%s)", use_fmt ? "-fmt" : "", args[2])) {
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001237 memprintf(&err, "memory allocation error.");
1238 goto fail;
1239 }
1240
1241 /* parse_store() will always return a message in <err> on error */
Willy Tarreaue93bff42021-09-03 09:47:37 +02001242 tmp_args[0] = tmp_act;
1243 tmp_args[1] = (*args[4]) ? args[4] : args[3];
1244 tmp_args[2] = "";
1245 tmp_arg = 1; // must point to the first arg after the action
1246 p_ret = parse_store(tmp_args, &tmp_arg, &px, &rule, &err);
1247 free(tmp_act);
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001248
1249 if (p_ret != ACT_RET_PRS_OK)
1250 goto fail;
1251
1252 if (rule.arg.vars.scope != SCOPE_PROC) {
Willy Tarreauc767eeb2021-09-03 10:23:26 +02001253 memprintf(&err, "'%s %s': cannot set variable '%s', only scope 'proc' is permitted here.", args[0], args[1], args[2]);
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001254 goto fail;
1255 }
1256
1257 err = NULL;
1258 nberr = smp_resolve_args(&px, &err);
1259 if (nberr) {
1260 release_sample_expr(rule.arg.vars.expr);
1261 indent_msg(&err, 2);
1262 goto fail;
1263 }
1264
Willy Tarreaue93bff42021-09-03 09:47:37 +02001265 if (use_fmt && !(sess = session_new(&px, NULL, &objt))) {
1266 release_sample_expr(rule.arg.vars.expr);
1267 memprintf(&err, "memory allocation error.");
1268 goto fail;
1269 }
1270
1271 action_store(&rule, &px, sess, NULL, 0);
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001272 release_sample_expr(rule.arg.vars.expr);
Willy Tarreaue93bff42021-09-03 09:47:37 +02001273 if (sess)
1274 session_free(sess);
1275
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001276 appctx->st0 = CLI_ST_PROMPT;
1277 return 0;
1278 fail:
1279 return cli_dynerr(appctx, err);
1280}
1281
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001282static int vars_max_size(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001283 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001284 char **err, unsigned int *limit)
1285{
1286 char *error;
1287
1288 *limit = strtol(args[1], &error, 10);
1289 if (*error != 0) {
1290 memprintf(err, "%s: '%s' is an invalid size", args[0], args[1]);
1291 return -1;
1292 }
1293 return 0;
1294}
1295
1296static int vars_max_size_global(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001297 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001298 char **err)
1299{
1300 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_global_limit);
1301}
1302
Christopher Fauletff2613e2016-11-09 11:36:17 +01001303static int vars_max_size_proc(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001304 const struct proxy *defpx, const char *file, int line,
Christopher Fauletff2613e2016-11-09 11:36:17 +01001305 char **err)
1306{
1307 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_proc_limit);
1308}
1309
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001310static int vars_max_size_sess(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001311 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001312 char **err)
1313{
1314 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_sess_limit);
1315}
1316
1317static int vars_max_size_txn(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001318 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001319 char **err)
1320{
1321 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_txn_limit);
1322}
1323
1324static int vars_max_size_reqres(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001325 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001326 char **err)
1327{
1328 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_reqres_limit);
1329}
1330
Gaetan Rivet13a50432020-02-21 18:13:44 +01001331static int vars_max_size_check(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001332 const struct proxy *defpx, const char *file, int line,
Gaetan Rivet13a50432020-02-21 18:13:44 +01001333 char **err)
1334{
1335 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_check_limit);
1336}
1337
Willy Tarreau2c897d92021-08-31 08:48:55 +02001338/* early boot initialization */
1339static void vars_init()
1340{
1341 var_name_hash_seed = ha_random64();
1342}
1343
1344INITCALL0(STG_PREPARE, vars_init);
1345
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001346static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
1347
Willy Tarreau44c5ff62021-11-02 17:08:15 +01001348 { "var", smp_fetch_var, ARG2(1,STR,STR), smp_check_var, SMP_T_ANY, SMP_USE_CONST },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001349 { /* END */ },
1350}};
1351
Willy Tarreau0108d902018-11-25 19:14:37 +01001352INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);
1353
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001354static struct sample_conv_kw_list sample_conv_kws = {ILH, {
Remi Tricot-Le Breton51899d22021-12-16 17:14:37 +01001355 { "set-var", smp_conv_store, ARG5(1,STR,STR,STR,STR,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
Christopher Faulet85d79c92016-11-09 16:54:56 +01001356 { "unset-var", smp_conv_clear, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001357 { /* END */ },
1358}};
1359
Willy Tarreau0108d902018-11-25 19:14:37 +01001360INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws);
1361
Jaroslaw Rzeszótkoc8637032021-11-02 16:56:05 +01001362static struct action_kw_list tcp_req_conn_kws = { { }, {
1363 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
1364 { "set-var", parse_store, KWF_MATCH_PREFIX },
1365 { "unset-var", parse_store, KWF_MATCH_PREFIX },
1366 { /* END */ }
1367}};
1368
1369INITCALL1(STG_REGISTER, tcp_req_conn_keywords_register, &tcp_req_conn_kws);
1370
Willy Tarreau620408f2016-10-21 16:37:51 +02001371static struct action_kw_list tcp_req_sess_kws = { { }, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001372 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001373 { "set-var", parse_store, KWF_MATCH_PREFIX },
1374 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Willy Tarreau620408f2016-10-21 16:37:51 +02001375 { /* END */ }
1376}};
1377
Willy Tarreau0108d902018-11-25 19:14:37 +01001378INITCALL1(STG_REGISTER, tcp_req_sess_keywords_register, &tcp_req_sess_kws);
1379
Willy Tarreau620408f2016-10-21 16:37:51 +02001380static struct action_kw_list tcp_req_cont_kws = { { }, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001381 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001382 { "set-var", parse_store, KWF_MATCH_PREFIX },
1383 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001384 { /* END */ }
1385}};
1386
Willy Tarreau0108d902018-11-25 19:14:37 +01001387INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_cont_kws);
1388
Thierry FOURNIER36481b82015-08-19 09:01:53 +02001389static struct action_kw_list tcp_res_kws = { { }, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001390 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001391 { "set-var", parse_store, KWF_MATCH_PREFIX },
1392 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001393 { /* END */ }
1394}};
1395
Willy Tarreau0108d902018-11-25 19:14:37 +01001396INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_kws);
1397
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001398static struct action_kw_list tcp_check_kws = {ILH, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001399 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001400 { "set-var", parse_store, KWF_MATCH_PREFIX },
1401 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001402 { /* END */ }
1403}};
1404
1405INITCALL1(STG_REGISTER, tcp_check_keywords_register, &tcp_check_kws);
1406
Thierry FOURNIER36481b82015-08-19 09:01:53 +02001407static struct action_kw_list http_req_kws = { { }, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001408 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001409 { "set-var", parse_store, KWF_MATCH_PREFIX },
1410 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001411 { /* END */ }
1412}};
1413
Willy Tarreau0108d902018-11-25 19:14:37 +01001414INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_kws);
1415
Thierry FOURNIER36481b82015-08-19 09:01:53 +02001416static struct action_kw_list http_res_kws = { { }, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001417 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001418 { "set-var", parse_store, KWF_MATCH_PREFIX },
1419 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001420 { /* END */ }
1421}};
1422
Willy Tarreau0108d902018-11-25 19:14:37 +01001423INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_kws);
1424
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01001425static struct action_kw_list http_after_res_kws = { { }, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001426 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001427 { "set-var", parse_store, KWF_MATCH_PREFIX },
1428 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01001429 { /* END */ }
1430}};
1431
1432INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_kws);
1433
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001434static struct cfg_kw_list cfg_kws = {{ },{
Willy Tarreau13d2ba22021-03-26 11:38:08 +01001435 { CFG_GLOBAL, "set-var", vars_parse_global_set_var },
Willy Tarreau753d4db2021-09-03 09:02:47 +02001436 { CFG_GLOBAL, "set-var-fmt", vars_parse_global_set_var },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001437 { CFG_GLOBAL, "tune.vars.global-max-size", vars_max_size_global },
Christopher Fauletff2613e2016-11-09 11:36:17 +01001438 { CFG_GLOBAL, "tune.vars.proc-max-size", vars_max_size_proc },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001439 { CFG_GLOBAL, "tune.vars.sess-max-size", vars_max_size_sess },
1440 { CFG_GLOBAL, "tune.vars.txn-max-size", vars_max_size_txn },
1441 { CFG_GLOBAL, "tune.vars.reqres-max-size", vars_max_size_reqres },
Gaetan Rivet13a50432020-02-21 18:13:44 +01001442 { CFG_GLOBAL, "tune.vars.check-max-size", vars_max_size_check },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001443 { /* END */ }
1444}};
1445
Willy Tarreau0108d902018-11-25 19:14:37 +01001446INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Willy Tarreauc35eb382021-03-26 14:51:31 +01001447
1448
1449/* register cli keywords */
1450static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02001451 { { "get", "var", NULL }, "get var <name> : retrieve contents of a process-wide variable", vars_parse_cli_get_var, NULL },
Willy Tarreaue93bff42021-09-03 09:47:37 +02001452 { { "set", "var", NULL }, "set var <name> [fmt|expr] {<fmt>|<expr>}: set variable from an expression or a format", vars_parse_cli_set_var, NULL, NULL, NULL, ACCESS_EXPERIMENTAL },
Willy Tarreauc35eb382021-03-26 14:51:31 +01001453 { { NULL }, NULL, NULL, NULL }
1454}};
1455INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);