blob: a4c6f4528803457dadac2964a0d677f55cfc7f4e [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>
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020021
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020022
23/* This contains a pool of struct vars */
Willy Tarreau8ceae722018-11-26 11:58:30 +010024DECLARE_STATIC_POOL(var_pool, "vars", sizeof(struct var));
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020025
Willy Tarreaucfc4f242021-05-08 11:41:28 +020026/* list of variables for the process scope. */
27struct vars proc_vars THREAD_ALIGNED(64);
28
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020029/* This array of int contains the system limits per context. */
30static unsigned int var_global_limit = 0;
Christopher Fauletff2613e2016-11-09 11:36:17 +010031static unsigned int var_proc_limit = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020032static unsigned int var_sess_limit = 0;
33static unsigned int var_txn_limit = 0;
34static unsigned int var_reqres_limit = 0;
Gaetan Rivet13a50432020-02-21 18:13:44 +010035static unsigned int var_check_limit = 0;
Willy Tarreau2c897d92021-08-31 08:48:55 +020036static uint64_t var_name_hash_seed = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020037
Willy Tarreauf37b1402019-06-04 16:27:36 +020038/* returns the struct vars pointer for a session, stream and scope, or NULL if
39 * it does not exist.
40 */
41static inline struct vars *get_vars(struct session *sess, struct stream *strm, enum vars_scope scope)
42{
43 switch (scope) {
44 case SCOPE_PROC:
Willy Tarreaucfc4f242021-05-08 11:41:28 +020045 return &proc_vars;
Willy Tarreauf37b1402019-06-04 16:27:36 +020046 case SCOPE_SESS:
Willy Tarreaua07d61b2021-03-26 11:27:59 +010047 return sess ? &sess->vars : NULL;
Gaetan Rivet13a50432020-02-21 18:13:44 +010048 case SCOPE_CHECK: {
Christopher Fauletc4439f72021-06-02 11:48:42 +020049 struct check *check = sess ? objt_check(sess->origin) : NULL;
Gaetan Rivet13a50432020-02-21 18:13:44 +010050
Christopher Faulet0fca7ed2020-04-21 11:53:32 +020051 return check ? &check->vars : NULL;
Gaetan Rivet13a50432020-02-21 18:13:44 +010052 }
Willy Tarreauf37b1402019-06-04 16:27:36 +020053 case SCOPE_TXN:
54 return strm ? &strm->vars_txn : NULL;
55 case SCOPE_REQ:
56 case SCOPE_RES:
57 default:
58 return strm ? &strm->vars_reqres : NULL;
59 }
60}
61
Willy Tarreau72330982015-06-19 11:21:56 +020062/* This function adds or remove memory size from the accounting. The inner
63 * pointers may be null when setting the outer ones only.
64 */
Miroslav Zagorac6deab792020-12-09 16:34:29 +010065void var_accounting_diff(struct vars *vars, struct session *sess, struct stream *strm, int size)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020066{
67 switch (vars->scope) {
68 case SCOPE_REQ:
69 case SCOPE_RES:
Willy Tarreau55f8a832021-09-08 15:51:06 +020070 if (var_reqres_limit && strm)
Willy Tarreauf37b1402019-06-04 16:27:36 +020071 _HA_ATOMIC_ADD(&strm->vars_reqres.size, size);
Willy Tarreau6204cd92016-03-10 16:33:04 +010072 /* fall through */
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020073 case SCOPE_TXN:
Willy Tarreau55f8a832021-09-08 15:51:06 +020074 if (var_txn_limit && strm)
Willy Tarreauf37b1402019-06-04 16:27:36 +020075 _HA_ATOMIC_ADD(&strm->vars_txn.size, size);
Gaetan Rivet13a50432020-02-21 18:13:44 +010076 goto scope_sess;
Willy Tarreau55f8a832021-09-08 15:51:06 +020077 case SCOPE_CHECK:
78 if (var_check_limit) {
Christopher Faulet0fca7ed2020-04-21 11:53:32 +020079 struct check *check = objt_check(sess->origin);
Gaetan Rivet13a50432020-02-21 18:13:44 +010080
Christopher Faulet0fca7ed2020-04-21 11:53:32 +020081 if (check)
82 _HA_ATOMIC_ADD(&check->vars.size, size);
Gaetan Rivet13a50432020-02-21 18:13:44 +010083 }
Willy Tarreau6204cd92016-03-10 16:33:04 +010084 /* fall through */
Gaetan Rivet13a50432020-02-21 18:13:44 +010085scope_sess:
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020086 case SCOPE_SESS:
Willy Tarreau55f8a832021-09-08 15:51:06 +020087 if (var_sess_limit)
88 _HA_ATOMIC_ADD(&sess->vars.size, size);
Christopher Fauletff2613e2016-11-09 11:36:17 +010089 /* fall through */
90 case SCOPE_PROC:
Willy Tarreau55f8a832021-09-08 15:51:06 +020091 if (var_proc_limit || var_global_limit)
92 _HA_ATOMIC_ADD(&proc_vars.size, size);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020093 }
94}
95
96/* This function returns 1 if the <size> is available in the var
Joseph Herlant07676892018-11-15 09:19:50 -080097 * pool <vars>, otherwise returns 0. If the space is available,
Willy Tarreau72330982015-06-19 11:21:56 +020098 * the size is reserved. The inner pointers may be null when setting
Willy Tarreau6204cd92016-03-10 16:33:04 +010099 * the outer ones only. The accounting uses either <sess> or <strm>
100 * depending on the scope. <strm> may be NULL when no stream is known
101 * and only the session exists (eg: tcp-request connection).
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200102 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100103static int var_accounting_add(struct vars *vars, struct session *sess, struct stream *strm, int size)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200104{
105 switch (vars->scope) {
106 case SCOPE_REQ:
107 case SCOPE_RES:
Willy Tarreauf37b1402019-06-04 16:27:36 +0200108 if (var_reqres_limit && strm && strm->vars_reqres.size + size > var_reqres_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200109 return 0;
Willy Tarreau6204cd92016-03-10 16:33:04 +0100110 /* fall through */
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200111 case SCOPE_TXN:
Willy Tarreauf37b1402019-06-04 16:27:36 +0200112 if (var_txn_limit && strm && strm->vars_txn.size + size > var_txn_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200113 return 0;
Gaetan Rivet13a50432020-02-21 18:13:44 +0100114 goto scope_sess;
115 case SCOPE_CHECK: {
Christopher Faulet0fca7ed2020-04-21 11:53:32 +0200116 struct check *check = objt_check(sess->origin);
Gaetan Rivet13a50432020-02-21 18:13:44 +0100117
Christopher Faulet0fca7ed2020-04-21 11:53:32 +0200118 if (var_check_limit && check && check->vars.size + size > var_check_limit)
Gaetan Rivet13a50432020-02-21 18:13:44 +0100119 return 0;
120 }
Willy Tarreau6204cd92016-03-10 16:33:04 +0100121 /* fall through */
Gaetan Rivet13a50432020-02-21 18:13:44 +0100122scope_sess:
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200123 case SCOPE_SESS:
Willy Tarreau6204cd92016-03-10 16:33:04 +0100124 if (var_sess_limit && sess->vars.size + size > var_sess_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200125 return 0;
Christopher Fauletff2613e2016-11-09 11:36:17 +0100126 /* fall through */
127 case SCOPE_PROC:
Willy Tarreau3b78f2a2021-09-08 15:40:58 +0200128 /* note: scope proc collects all others and is currently identical to the
129 * global limit.
130 */
Willy Tarreaucfc4f242021-05-08 11:41:28 +0200131 if (var_proc_limit && proc_vars.size + size > var_proc_limit)
Christopher Fauletff2613e2016-11-09 11:36:17 +0100132 return 0;
Willy Tarreau3b78f2a2021-09-08 15:40:58 +0200133 if (var_global_limit && proc_vars.size + size > var_global_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200134 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
Willy Tarreauc1c88f42021-09-08 15:03:58 +0200140/* This function removes a variable from the list and frees the memory it was
141 * using. If the variable is marked "VF_PERMANENT", the sample_data is only
142 * reset to SMP_T_ANY unless <force> is non nul. Returns the freed size.
143 */
144unsigned int var_clear(struct var *var, int force)
Christopher Faulet85d79c92016-11-09 16:54:56 +0100145{
146 unsigned int size = 0;
147
148 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
Willy Tarreau5b52b002021-02-26 21:19:53 +0100149 ha_free(&var->data.u.str.area);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200150 size += var->data.u.str.data;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100151 }
Christopher Fauletd02210c2017-07-24 16:24:39 +0200152 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
Willy Tarreau5b52b002021-02-26 21:19:53 +0100153 ha_free(&var->data.u.meth.str.area);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200154 size += var->data.u.meth.str.data;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100155 }
Willy Tarreauc1c88f42021-09-08 15:03:58 +0200156 /* wipe the sample */
157 var->data.type = SMP_T_ANY;
158
159 if (!(var->flags & VF_PERMANENT) || force) {
160 LIST_DELETE(&var->l);
161 pool_free(var_pool, var);
162 size += sizeof(struct var);
163 }
Christopher Faulet85d79c92016-11-09 16:54:56 +0100164 return size;
165}
166
Joseph Herlant07676892018-11-15 09:19:50 -0800167/* This function free all the memory used by all the variables
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200168 * in the list.
169 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100170void vars_prune(struct vars *vars, struct session *sess, struct stream *strm)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200171{
172 struct var *var, *tmp;
Willy Tarreau72330982015-06-19 11:21:56 +0200173 unsigned int size = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200174
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200175 vars_wrlock(vars);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200176 list_for_each_entry_safe(var, tmp, &vars->head, l) {
Willy Tarreauc1c88f42021-09-08 15:03:58 +0200177 size += var_clear(var, 1);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200178 }
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200179 vars_wrunlock(vars);
Willy Tarreau6204cd92016-03-10 16:33:04 +0100180 var_accounting_diff(vars, sess, strm, -size);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200181}
182
Willy Tarreauebcd4842015-06-19 11:59:02 +0200183/* This function frees all the memory used by all the session variables in the
184 * list starting at <vars>.
185 */
186void vars_prune_per_sess(struct vars *vars)
187{
188 struct var *var, *tmp;
189 unsigned int size = 0;
190
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200191 vars_wrlock(vars);
Willy Tarreauebcd4842015-06-19 11:59:02 +0200192 list_for_each_entry_safe(var, tmp, &vars->head, l) {
Willy Tarreauc1c88f42021-09-08 15:03:58 +0200193 size += var_clear(var, 1);
Willy Tarreauebcd4842015-06-19 11:59:02 +0200194 }
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200195 vars_wrunlock(vars);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200196
Willy Tarreau55f8a832021-09-08 15:51:06 +0200197 if (var_sess_limit)
198 _HA_ATOMIC_SUB(&vars->size, size);
199 if (var_proc_limit || var_global_limit)
200 _HA_ATOMIC_SUB(&proc_vars.size, size);
Willy Tarreauebcd4842015-06-19 11:59:02 +0200201}
202
Willy Tarreaub7bfcb32021-08-31 08:13:25 +0200203/* This function initializes a variables list head */
204void vars_init_head(struct vars *vars, enum vars_scope scope)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200205{
206 LIST_INIT(&vars->head);
207 vars->scope = scope;
208 vars->size = 0;
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100209 HA_RWLOCK_INIT(&vars->rwlock);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200210}
211
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200212/* This function returns a hash value and a scope for a variable name of a
213 * specified length. It makes sure that the scope is valid. It returns non-zero
214 * on success, 0 on failure. Neither hash nor scope may be NULL.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200215 */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200216static int vars_hash_name(const char *name, int len, enum vars_scope *scope,
217 uint64_t *hash, char **err)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200218{
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200219 const char *tmp;
220
221 /* Check length. */
222 if (len == 0) {
223 memprintf(err, "Empty variable name cannot be accepted");
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200224 return 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200225 }
226
227 /* Check scope. */
Christopher Fauletff2613e2016-11-09 11:36:17 +0100228 if (len > 5 && strncmp(name, "proc.", 5) == 0) {
229 name += 5;
230 len -= 5;
231 *scope = SCOPE_PROC;
232 }
233 else if (len > 5 && strncmp(name, "sess.", 5) == 0) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200234 name += 5;
235 len -= 5;
236 *scope = SCOPE_SESS;
237 }
238 else if (len > 4 && strncmp(name, "txn.", 4) == 0) {
239 name += 4;
240 len -= 4;
241 *scope = SCOPE_TXN;
242 }
243 else if (len > 4 && strncmp(name, "req.", 4) == 0) {
244 name += 4;
245 len -= 4;
246 *scope = SCOPE_REQ;
247 }
248 else if (len > 4 && strncmp(name, "res.", 4) == 0) {
249 name += 4;
250 len -= 4;
251 *scope = SCOPE_RES;
252 }
Gaetan Rivet13a50432020-02-21 18:13:44 +0100253 else if (len > 6 && strncmp(name, "check.", 6) == 0) {
254 name += 6;
255 len -= 6;
256 *scope = SCOPE_CHECK;
257 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200258 else {
Willy Tarreau1402fef2021-09-03 10:12:55 +0200259 memprintf(err, "invalid variable name '%.*s'. A variable name must be start by its scope. "
260 "The scope can be 'proc', 'sess', 'txn', 'req', 'res' or 'check'", len, name);
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200261 return 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200262 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200263
264 /* Check variable name syntax. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200265 for (tmp = name; tmp < name + len; tmp++) {
Willy Tarreau90807112020-02-25 08:16:33 +0100266 if (!isalnum((unsigned char)*tmp) && *tmp != '_' && *tmp != '.') {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200267 memprintf(err, "invalid syntax at char '%s'", tmp);
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200268 return 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200269 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200270 }
Christopher Faulete95f2c32017-07-24 16:30:34 +0200271
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200272 *hash = XXH3(name, len, var_name_hash_seed);
273 return 1;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200274}
275
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200276/* This function returns the variable from the given list that matches
277 * <name_hash> or returns NULL if not found. It's only a linked list since it
278 * is not expected to have many variables per scope (a few tens at best).
279 * The caller is responsible for ensuring that <vars> is properly locked.
280 */
281static struct var *var_get(struct vars *vars, uint64_t name_hash)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200282{
283 struct var *var;
284
285 list_for_each_entry(var, &vars->head, l)
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200286 if (var->name_hash == name_hash)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200287 return var;
288 return NULL;
289}
290
291/* Returns 0 if fails, else returns 1. */
292static int smp_fetch_var(const struct arg *args, struct sample *smp, const char *kw, void *private)
293{
294 const struct var_desc *var_desc = &args[0].data.var;
Willy Tarreau54496a62021-09-03 12:00:13 +0200295 const struct buffer *def = NULL;
Christopher Faulete95f2c32017-07-24 16:30:34 +0200296
Willy Tarreau54496a62021-09-03 12:00:13 +0200297 if (args[1].type == ARGT_STR)
298 def = &args[1].data.str;
299
300 return vars_get_by_desc(var_desc, smp, def);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200301}
302
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200303/* This function tries to create a variable whose name hash is <name_hash> in
304 * scope <scope> and store sample <smp> as its value.
305 *
306 * The stream and session are extracted from <smp>, whose stream may be NULL
307 * when scope is SCOPE_SESS. In case there wouldn't be enough memory to store
308 * the sample while the variable was already created, it would be changed to
309 * a bool (which is memory-less).
Willy Tarreau7978c5c2021-09-07 14:24:07 +0200310 *
311 * Flags is a bitfield that may contain one of the following flags:
312 * - VF_UPDATEONLY: if the scope is SCOPE_PROC, the variable may only be
313 * updated but not created.
Willy Tarreau4994b572021-09-08 11:38:25 +0200314 * - VF_CREATEONLY: do nothing if the variable already exists (success).
Willy Tarreau3dc6dc32021-09-08 11:07:32 +0200315 * - VF_PERMANENT: this flag will be passed to the variable upon creation
Willy Tarreau7978c5c2021-09-07 14:24:07 +0200316 *
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200317 * It returns 0 on failure, non-zero on success.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200318 */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200319static int var_set(uint64_t name_hash, enum vars_scope scope, struct sample *smp, uint flags)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200320{
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200321 struct vars *vars;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200322 struct var *var;
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200323 int ret = 0;
324
325 vars = get_vars(smp->sess, smp->strm, scope);
326 if (!vars || vars->scope != scope)
327 return 0;
328
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200329 vars_wrlock(vars);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200330
331 /* Look for existing variable name. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200332 var = var_get(vars, name_hash);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200333
334 if (var) {
Willy Tarreau4994b572021-09-08 11:38:25 +0200335 if (flags & VF_CREATEONLY) {
336 ret = 1;
337 goto unlock;
338 }
339
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200340 /* free its used memory. */
341 if (var->data.type == SMP_T_STR ||
342 var->data.type == SMP_T_BIN) {
Willy Tarreau5b52b002021-02-26 21:19:53 +0100343 ha_free(&var->data.u.str.area);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200344 var_accounting_diff(vars, smp->sess, smp->strm,
345 -var->data.u.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200346 }
Christopher Fauletd02210c2017-07-24 16:24:39 +0200347 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
Willy Tarreau5b52b002021-02-26 21:19:53 +0100348 ha_free(&var->data.u.meth.str.area);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200349 var_accounting_diff(vars, smp->sess, smp->strm,
350 -var->data.u.meth.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200351 }
352 } else {
Willy Tarreau7978c5c2021-09-07 14:24:07 +0200353 /* creation permitted for proc ? */
354 if (flags & VF_UPDATEONLY && scope == SCOPE_PROC)
355 goto unlock;
356
Joseph Herlant07676892018-11-15 09:19:50 -0800357 /* Check memory available. */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100358 if (!var_accounting_add(vars, smp->sess, smp->strm, sizeof(struct var)))
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200359 goto unlock;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200360
361 /* Create new entry. */
Willy Tarreaubafbe012017-11-24 17:34:44 +0100362 var = pool_alloc(var_pool);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200363 if (!var)
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200364 goto unlock;
Willy Tarreau2b718102021-04-21 07:32:39 +0200365 LIST_APPEND(&vars->head, &var->l);
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200366 var->name_hash = name_hash;
Willy Tarreau3dc6dc32021-09-08 11:07:32 +0200367 var->flags = flags & VF_PERMANENT;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200368 }
369
370 /* Set type. */
Thierry FOURNIER8c542ca2015-08-19 09:00:18 +0200371 var->data.type = smp->data.type;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200372
373 /* Copy data. If the data needs memory, the function can fail. */
374 switch (var->data.type) {
375 case SMP_T_BOOL:
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200376 case SMP_T_SINT:
Thierry FOURNIER136f9d32015-08-19 09:07:19 +0200377 var->data.u.sint = smp->data.u.sint;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200378 break;
379 case SMP_T_IPV4:
Thierry FOURNIER136f9d32015-08-19 09:07:19 +0200380 var->data.u.ipv4 = smp->data.u.ipv4;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200381 break;
382 case SMP_T_IPV6:
Thierry FOURNIER136f9d32015-08-19 09:07:19 +0200383 var->data.u.ipv6 = smp->data.u.ipv6;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200384 break;
385 case SMP_T_STR:
386 case SMP_T_BIN:
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200387 if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.str.data)) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200388 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200389 goto unlock;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200390 }
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200391
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200392 var->data.u.str.area = malloc(smp->data.u.str.data);
393 if (!var->data.u.str.area) {
394 var_accounting_diff(vars, smp->sess, smp->strm,
395 -smp->data.u.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200396 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200397 goto unlock;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200398 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200399 var->data.u.str.data = smp->data.u.str.data;
400 memcpy(var->data.u.str.area, smp->data.u.str.area,
401 var->data.u.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200402 break;
403 case SMP_T_METH:
Christopher Fauletd02210c2017-07-24 16:24:39 +0200404 var->data.u.meth.meth = smp->data.u.meth.meth;
405 if (smp->data.u.meth.meth != HTTP_METH_OTHER)
406 break;
407
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200408 if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.meth.str.data)) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200409 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200410 goto unlock;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200411 }
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200412
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200413 var->data.u.meth.str.area = malloc(smp->data.u.meth.str.data);
414 if (!var->data.u.meth.str.area) {
415 var_accounting_diff(vars, smp->sess, smp->strm,
416 -smp->data.u.meth.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200417 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200418 goto unlock;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200419 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200420 var->data.u.meth.str.data = smp->data.u.meth.str.data;
421 var->data.u.meth.str.size = smp->data.u.meth.str.data;
422 memcpy(var->data.u.meth.str.area, smp->data.u.meth.str.area,
423 var->data.u.meth.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200424 break;
425 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200426
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200427 /* OK, now done */
428 ret = 1;
429 unlock:
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200430 vars_wrunlock(vars);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200431 return ret;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200432}
433
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200434/* Deletes a variable matching name hash <name_hash> and scope <scope> for the
435 * session and stream found in <smp>. Note that stream may be null for
436 * SCOPE_SESS. Returns 0 if the scope was not found otherwise 1.
Willy Tarreaud378eb82021-09-07 11:44:41 +0200437 */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200438static int var_unset(uint64_t name_hash, enum vars_scope scope, struct sample *smp)
Christopher Faulet85d79c92016-11-09 16:54:56 +0100439{
440 struct vars *vars;
441 struct var *var;
442 unsigned int size = 0;
443
Willy Tarreauf37b1402019-06-04 16:27:36 +0200444 vars = get_vars(smp->sess, smp->strm, scope);
445 if (!vars || vars->scope != scope)
Christopher Faulet85d79c92016-11-09 16:54:56 +0100446 return 0;
447
448 /* Look for existing variable name. */
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200449 vars_wrlock(vars);
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200450 var = var_get(vars, name_hash);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100451 if (var) {
Willy Tarreauc1c88f42021-09-08 15:03:58 +0200452 size = var_clear(var, 0);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100453 var_accounting_diff(vars, smp->sess, smp->strm, -size);
454 }
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200455 vars_wrunlock(vars);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100456 return 1;
457}
458
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200459/* Returns 0 if fails, else returns 1. */
460static int smp_conv_store(const struct arg *args, struct sample *smp, void *private)
461{
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200462 uint64_t seed = var_name_hash_seed;
463 uint64_t name_hash = XXH3(smp->data.u.str.area, smp->data.u.str.data, seed);
464
465 return var_set(name_hash, args[0].data.var.scope, smp, 0);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200466}
467
Christopher Faulet85d79c92016-11-09 16:54:56 +0100468/* Returns 0 if fails, else returns 1. */
469static int smp_conv_clear(const struct arg *args, struct sample *smp, void *private)
470{
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200471 uint64_t seed = var_name_hash_seed;
472 uint64_t name_hash = XXH3(smp->data.u.str.area, smp->data.u.str.data, seed);
473
474 return var_unset(name_hash, args[0].data.var.scope, smp);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100475}
476
Joseph Herlant07676892018-11-15 09:19:50 -0800477/* This functions check an argument entry and fill it with a variable
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200478 * type. The argumen must be a string. If the variable lookup fails,
Joseph Herlant07676892018-11-15 09:19:50 -0800479 * the function returns 0 and fill <err>, otherwise it returns 1.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200480 */
481int vars_check_arg(struct arg *arg, char **err)
482{
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200483 enum vars_scope scope;
Willy Tarreaudf8eeb12021-09-08 11:07:32 +0200484 struct sample empty_smp = { };
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200485 uint64_t hash;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200486
487 /* Check arg type. */
488 if (arg->type != ARGT_STR) {
489 memprintf(err, "unexpected argument type");
490 return 0;
491 }
492
493 /* Register new variable name. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200494 if (!vars_hash_name(arg->data.str.area, arg->data.str.data, &scope, &hash, err))
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200495 return 0;
496
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200497 if (scope == SCOPE_PROC && !var_set(hash, scope, &empty_smp, VF_CREATEONLY|VF_PERMANENT))
Willy Tarreaudf8eeb12021-09-08 11:07:32 +0200498 return 0;
499
Tim Duesterhusa6cc7e82019-05-13 10:53:29 +0200500 /* properly destroy the chunk */
501 chunk_destroy(&arg->data.str);
502
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200503 /* Use the global variable name pointer. */
504 arg->type = ARGT_VAR;
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200505 arg->data.var.name_hash = hash;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200506 arg->data.var.scope = scope;
507 return 1;
508}
509
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200510/* This function stores a sample in a variable if it was already defined.
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200511 * Returns zero on failure and non-zero otherwise. The variable not being
512 * defined is treated as a failure.
Christopher Faulet09c9df22016-10-31 11:05:37 +0100513 */
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200514int vars_set_by_name_ifexist(const char *name, size_t len, struct sample *smp)
Christopher Faulet09c9df22016-10-31 11:05:37 +0100515{
516 enum vars_scope scope;
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200517 uint64_t hash;
Christopher Faulet09c9df22016-10-31 11:05:37 +0100518
519 /* Resolve name and scope. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200520 if (!vars_hash_name(name, len, &scope, &hash, NULL))
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200521 return 0;
Christopher Faulet09c9df22016-10-31 11:05:37 +0100522
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200523 return var_set(hash, scope, smp, VF_UPDATEONLY);
Christopher Faulet09c9df22016-10-31 11:05:37 +0100524}
525
526
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200527/* This function stores a sample in a variable.
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200528 * Returns zero on failure and non-zero otherwise.
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200529 */
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200530int vars_set_by_name(const char *name, size_t len, struct sample *smp)
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200531{
532 enum vars_scope scope;
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200533 uint64_t hash;
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200534
535 /* Resolve name and scope. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200536 if (!vars_hash_name(name, len, &scope, &hash, NULL))
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200537 return 0;
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200538
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200539 return var_set(hash, scope, smp, 0);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200540}
541
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200542/* This function unsets a variable if it was already defined.
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200543 * Returns zero on failure and non-zero otherwise.
Christopher Faulet85d79c92016-11-09 16:54:56 +0100544 */
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200545int vars_unset_by_name_ifexist(const char *name, size_t len, struct sample *smp)
Christopher Faulet85d79c92016-11-09 16:54:56 +0100546{
547 enum vars_scope scope;
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200548 uint64_t hash;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100549
550 /* Resolve name and scope. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200551 if (!vars_hash_name(name, len, &scope, &hash, NULL))
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200552 return 0;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100553
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200554 return var_unset(hash, scope, smp);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100555}
556
557
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200558/* This retrieves variable whose hash matches <name_hash> from variables <vars>,
559 * and if found and not empty, duplicates the result into sample <smp>.
560 * smp_dup() is used in order to release the variables lock ASAP (so a pre-
561 * allocated chunk is obtained via get_trash_shunk()). The variables' lock is
562 * used for reads.
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200563 *
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200564 * The function returns 0 if the variable was not found and no default
565 * value was provided in <def>, otherwise 1 with the sample filled.
566 * Default values are always returned as strings.
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200567 */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200568static 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 +0200569{
570 struct var *var;
571
572 /* Get the variable entry. */
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200573 vars_rdlock(vars);
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200574 var = var_get(vars, name_hash);
Willy Tarreau63c30662021-09-08 13:58:19 +0200575 if (!var || !var->data.type) {
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200576 if (!def) {
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200577 vars_rdunlock(vars);
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200578 return 0;
579 }
580
581 /* not found but we have a default value */
582 smp->data.type = SMP_T_STR;
583 smp->data.u.str = *def;
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200584 }
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200585 else
586 smp->data = var->data;
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200587
588 /* Copy sample. */
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200589 smp_dup(smp);
590
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200591 vars_rdunlock(vars);
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200592 return 1;
593}
594
Dragan Dosen14518f22021-02-22 17:20:01 +0100595/* This function fills a sample with the variable content.
596 *
597 * Keep in mind that a sample content is duplicated by using smp_dup()
598 * and it therefore uses a pre-allocated trash chunk as returned by
599 * get_trash_chunk().
600 *
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200601 * If the variable is not valid in this scope, 0 is always returned.
602 * If the variable is valid but not found, either the default value
603 * <def> is returned if not NULL, or zero is returned.
604 *
Dragan Dosen14518f22021-02-22 17:20:01 +0100605 * Returns 1 if the sample is filled, otherwise it returns 0.
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200606 */
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200607int vars_get_by_name(const char *name, size_t len, struct sample *smp, const struct buffer *def)
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200608{
609 struct vars *vars;
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200610 enum vars_scope scope;
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200611 uint64_t hash;
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200612
613 /* Resolve name and scope. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200614 if (!vars_hash_name(name, len, &scope, &hash, NULL))
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200615 return 0;
616
617 /* Select "vars" pool according with the scope. */
Willy Tarreauf37b1402019-06-04 16:27:36 +0200618 vars = get_vars(smp->sess, smp->strm, scope);
619 if (!vars || vars->scope != scope)
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200620 return 0;
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200621
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200622 return var_to_smp(vars, hash, smp, def);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200623}
624
Dragan Dosen14518f22021-02-22 17:20:01 +0100625/* This function fills a sample with the content of the variable described
626 * by <var_desc>.
627 *
628 * Keep in mind that a sample content is duplicated by using smp_dup()
629 * and it therefore uses a pre-allocated trash chunk as returned by
630 * get_trash_chunk().
631 *
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200632 * If the variable is not valid in this scope, 0 is always returned.
633 * If the variable is valid but not found, either the default value
634 * <def> is returned if not NULL, or zero is returned.
635 *
Dragan Dosen14518f22021-02-22 17:20:01 +0100636 * Returns 1 if the sample is filled, otherwise it returns 0.
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200637 */
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200638int vars_get_by_desc(const struct var_desc *var_desc, struct sample *smp, const struct buffer *def)
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200639{
640 struct vars *vars;
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200641
642 /* Select "vars" pool according with the scope. */
Willy Tarreauf37b1402019-06-04 16:27:36 +0200643 vars = get_vars(smp->sess, smp->strm, var_desc->scope);
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200644
Joseph Herlant07676892018-11-15 09:19:50 -0800645 /* Check if the scope is available a this point of processing. */
Willy Tarreauf37b1402019-06-04 16:27:36 +0200646 if (!vars || vars->scope != var_desc->scope)
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200647 return 0;
648
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200649 return var_to_smp(vars, var_desc->name_hash, smp, def);
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200650}
651
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200652/* Always returns ACT_RET_CONT even if an error occurs. */
653static enum act_return action_store(struct act_rule *rule, struct proxy *px,
Willy Tarreau658b85b2015-09-27 10:00:49 +0200654 struct session *sess, struct stream *s, int flags)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200655{
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200656 struct buffer *fmtstr = NULL;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200657 struct sample smp;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200658 int dir;
659
660 switch (rule->from) {
Willy Tarreau620408f2016-10-21 16:37:51 +0200661 case ACT_F_TCP_REQ_SES: dir = SMP_OPT_DIR_REQ; break;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200662 case ACT_F_TCP_REQ_CNT: dir = SMP_OPT_DIR_REQ; break;
663 case ACT_F_TCP_RES_CNT: dir = SMP_OPT_DIR_RES; break;
664 case ACT_F_HTTP_REQ: dir = SMP_OPT_DIR_REQ; break;
665 case ACT_F_HTTP_RES: dir = SMP_OPT_DIR_RES; break;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +0100666 case ACT_F_TCP_CHK: dir = SMP_OPT_DIR_REQ; break;
Willy Tarreau01d580a2021-03-26 11:11:34 +0100667 case ACT_F_CFG_PARSER: dir = SMP_OPT_DIR_REQ; break; /* not used anyway */
Willy Tarreau2f836de2021-03-26 15:36:44 +0100668 case ACT_F_CLI_PARSER: dir = SMP_OPT_DIR_REQ; break; /* not used anyway */
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200669 default:
670 send_log(px, LOG_ERR, "Vars: internal error while execute action store.");
671 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Christopher Faulet767a84b2017-11-24 16:50:31 +0100672 ha_alert("Vars: internal error while execute action store.\n");
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200673 return ACT_RET_CONT;
674 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200675
676 /* Process the expression. */
677 memset(&smp, 0, sizeof(smp));
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200678
679 if (!LIST_ISEMPTY(&rule->arg.vars.fmt)) {
680 /* a format-string is used */
681
682 fmtstr = alloc_trash_chunk();
683 if (!fmtstr) {
684 send_log(px, LOG_ERR, "Vars: memory allocation failure while processing store rule.");
685 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
686 ha_alert("Vars: memory allocation failure while processing store rule.\n");
687 return ACT_RET_CONT;
688 }
689
690 /* execute the log-format expression */
691 fmtstr->data = sess_build_logline(sess, s, fmtstr->area, fmtstr->size, &rule->arg.vars.fmt);
692
693 /* convert it to a sample of type string as it's what the vars
694 * API consumes, and store it.
695 */
696 smp_set_owner(&smp, px, sess, s, 0);
697 smp.data.type = SMP_T_STR;
698 smp.data.u.str = *fmtstr;
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200699 var_set(rule->arg.vars.name_hash, rule->arg.vars.scope, &smp, 0);
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200700 }
701 else {
702 /* an expression is used */
703 if (!sample_process(px, sess, s, dir|SMP_OPT_FINAL,
704 rule->arg.vars.expr, &smp))
705 return ACT_RET_CONT;
706 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200707
708 /* Store the sample, and ignore errors. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200709 var_set(rule->arg.vars.name_hash, rule->arg.vars.scope, &smp, 0);
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200710 free_trash_chunk(fmtstr);
Thierry FOURNIER24ff6c62015-08-06 08:52:53 +0200711 return ACT_RET_CONT;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200712}
713
Christopher Faulet85d79c92016-11-09 16:54:56 +0100714/* Always returns ACT_RET_CONT even if an error occurs. */
715static enum act_return action_clear(struct act_rule *rule, struct proxy *px,
716 struct session *sess, struct stream *s, int flags)
717{
718 struct sample smp;
719
720 memset(&smp, 0, sizeof(smp));
721 smp_set_owner(&smp, px, sess, s, SMP_OPT_FINAL);
722
723 /* Clear the variable using the sample context, and ignore errors. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200724 var_unset(rule->arg.vars.name_hash, rule->arg.vars.scope, &smp);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100725 return ACT_RET_CONT;
726}
727
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200728static void release_store_rule(struct act_rule *rule)
729{
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200730 struct logformat_node *lf, *lfb;
Willy Tarreauc77bad22021-09-03 10:58:07 +0200731
732 list_for_each_entry_safe(lf, lfb, &rule->arg.vars.fmt, list) {
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200733 LIST_DELETE(&lf->list);
734 release_sample_expr(lf->expr);
735 free(lf->arg);
736 free(lf);
737 }
738
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200739 release_sample_expr(rule->arg.vars.expr);
740}
741
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200742/* This two function checks the variable name and replace the
743 * configuration string name by the global string name. its
744 * the same string, but the global pointer can be easy to
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200745 * compare. They return non-zero on success, zero on failure.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200746 *
747 * The first function checks a sample-fetch and the second
748 * checks a converter.
749 */
750static int smp_check_var(struct arg *args, char **err)
751{
752 return vars_check_arg(&args[0], err);
753}
754
755static int conv_check_var(struct arg *args, struct sample_conv *conv,
756 const char *file, int line, char **err_msg)
757{
758 return vars_check_arg(&args[0], err_msg);
759}
760
761/* This function is a common parser for using variables. It understands
762 * the format:
763 *
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200764 * set-var-fmt(<variable-name>) <format-string>
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200765 * set-var(<variable-name>) <expression>
Willy Tarreau4b7531f2019-06-04 16:43:29 +0200766 * unset-var(<variable-name>)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200767 *
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200768 * It returns ACT_RET_PRS_ERR if fails and <err> is filled with an error
769 * message. Otherwise, it returns ACT_RET_PRS_OK and the variable <expr>
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100770 * is filled with the pointer to the expression to execute. The proxy is
771 * only used to retrieve the ->conf entries.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200772 */
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200773static enum act_parse_ret parse_store(const char **args, int *arg, struct proxy *px,
774 struct act_rule *rule, char **err)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200775{
776 const char *var_name = args[*arg-1];
777 int var_len;
Thierry FOURNIER48a9cd12015-07-28 19:00:28 +0200778 const char *kw_name;
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200779 int flags, set_var = 0; /* 0=unset-var, 1=set-var, 2=set-var-fmt */
Willy Tarreaudf8eeb12021-09-08 11:07:32 +0200780 struct sample empty_smp = { };
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200781
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200782 if (strncmp(var_name, "set-var-fmt", 11) == 0) {
783 var_name += 11;
784 set_var = 2;
785 }
786 else if (strncmp(var_name, "set-var", 7) == 0) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100787 var_name += 7;
788 set_var = 1;
789 }
Willy Tarreau28192102021-09-02 18:46:22 +0200790 else if (strncmp(var_name, "unset-var", 9) == 0) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100791 var_name += 9;
792 set_var = 0;
793 }
794
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200795 if (*var_name != '(') {
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200796 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 +0100797 args[*arg-1]);
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200798 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200799 }
800 var_name++; /* jump the '(' */
801 var_len = strlen(var_name);
802 var_len--; /* remove the ')' */
803 if (var_name[var_len] != ')') {
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200804 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 +0100805 args[*arg-1]);
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200806 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200807 }
808
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200809 LIST_INIT(&rule->arg.vars.fmt);
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200810 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 +0200811 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200812
Willy Tarreaudf8eeb12021-09-08 11:07:32 +0200813 if (rule->arg.vars.scope == SCOPE_PROC &&
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200814 !var_set(rule->arg.vars.name_hash, rule->arg.vars.scope, &empty_smp, VF_CREATEONLY|VF_PERMANENT))
Willy Tarreaudf8eeb12021-09-08 11:07:32 +0200815 return 0;
816
Christopher Faulet85d79c92016-11-09 16:54:56 +0100817 /* There is no fetch method when variable is unset. Just set the right
818 * action and return. */
819 if (!set_var) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100820 rule->action = ACT_CUSTOM;
821 rule->action_ptr = action_clear;
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200822 rule->release_ptr = release_store_rule;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100823 return ACT_RET_PRS_OK;
824 }
825
Thierry FOURNIER48a9cd12015-07-28 19:00:28 +0200826 kw_name = args[*arg-1];
827
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200828 switch (rule->from) {
Willy Tarreau843096d2021-09-02 19:03:07 +0200829 case ACT_F_TCP_REQ_SES:
830 flags = SMP_VAL_FE_SES_ACC;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200831 px->conf.args.ctx = ARGC_TSE;
Willy Tarreau843096d2021-09-02 19:03:07 +0200832 break;
833 case ACT_F_TCP_REQ_CNT:
834 flags = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_REQ_CNT : SMP_VAL_BE_REQ_CNT;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200835 px->conf.args.ctx = ARGC_TRQ;
Willy Tarreau843096d2021-09-02 19:03:07 +0200836 break;
837 case ACT_F_TCP_RES_CNT:
838 flags = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_RES_CNT : SMP_VAL_BE_RES_CNT;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200839 px->conf.args.ctx = ARGC_TRS;
Willy Tarreau843096d2021-09-02 19:03:07 +0200840 break;
841 case ACT_F_HTTP_REQ:
842 flags = (px->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200843 px->conf.args.ctx = ARGC_HRQ;
Willy Tarreau843096d2021-09-02 19:03:07 +0200844 break;
845 case ACT_F_HTTP_RES:
846 flags = (px->cap & PR_CAP_BE) ? SMP_VAL_BE_HRS_HDR : SMP_VAL_FE_HRS_HDR;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200847 px->conf.args.ctx = ARGC_HRS;
Willy Tarreau843096d2021-09-02 19:03:07 +0200848 break;
849 case ACT_F_TCP_CHK:
850 flags = SMP_VAL_BE_CHK_RUL;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200851 px->conf.args.ctx = ARGC_TCK;
Willy Tarreau843096d2021-09-02 19:03:07 +0200852 break;
853 case ACT_F_CFG_PARSER:
854 flags = SMP_VAL_CFG_PARSER;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200855 px->conf.args.ctx = ARGC_CFG;
Willy Tarreau843096d2021-09-02 19:03:07 +0200856 break;
857 case ACT_F_CLI_PARSER:
858 flags = SMP_VAL_CLI_PARSER;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200859 px->conf.args.ctx = ARGC_CLI;
Willy Tarreau843096d2021-09-02 19:03:07 +0200860 break;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200861 default:
862 memprintf(err,
863 "internal error, unexpected rule->from=%d, please report this bug!",
864 rule->from);
Thierry FOURNIERafa80492015-08-19 09:04:15 +0200865 return ACT_RET_PRS_ERR;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200866 }
Willy Tarreau54b96d92021-09-02 19:46:08 +0200867
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200868 if (set_var == 2) { /* set-var-fmt */
869 if (!parse_logformat_string(args[*arg], px, &rule->arg.vars.fmt, 0, flags, err))
870 return ACT_RET_PRS_ERR;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200871
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200872 (*arg)++;
873
874 /* for late error reporting */
875 free(px->conf.lfs_file);
876 px->conf.lfs_file = strdup(px->conf.args.file);
877 px->conf.lfs_line = px->conf.args.line;
878 } else {
879 /* set-var */
880 rule->arg.vars.expr = sample_parse_expr((char **)args, arg, px->conf.args.file,
881 px->conf.args.line, err, &px->conf.args, NULL);
882 if (!rule->arg.vars.expr)
883 return ACT_RET_PRS_ERR;
884
885 if (!(rule->arg.vars.expr->fetch->val & flags)) {
886 memprintf(err,
887 "fetch method '%s' extracts information from '%s', none of which is available here",
888 kw_name, sample_src_names(rule->arg.vars.expr->fetch->use));
889 free(rule->arg.vars.expr);
890 return ACT_RET_PRS_ERR;
891 }
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200892 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200893
Thierry FOURNIER42148732015-09-02 17:17:33 +0200894 rule->action = ACT_CUSTOM;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200895 rule->action_ptr = action_store;
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200896 rule->release_ptr = release_store_rule;
Thierry FOURNIERafa80492015-08-19 09:04:15 +0200897 return ACT_RET_PRS_OK;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200898}
899
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100900
901/* parses a global "set-var" directive. It will create a temporary rule and
902 * expression that are parsed, processed, and released on the fly so that we
903 * respect the real set-var syntax. These directives take the following format:
904 * set-var <name> <expression>
Willy Tarreau753d4db2021-09-03 09:02:47 +0200905 * set-var-fmt <name> <fmt>
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100906 * Note that parse_store() expects "set-var(name) <expression>" so we have to
907 * temporarily replace the keyword here.
908 */
909static int vars_parse_global_set_var(char **args, int section_type, struct proxy *curpx,
910 const struct proxy *defpx, const char *file, int line,
911 char **err)
912{
913 struct proxy px = {
Willy Tarreau9c204332021-09-03 08:19:43 +0200914 .id = "CFG",
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100915 .conf.args.file = file,
916 .conf.args.line = line,
917 };
918 struct act_rule rule = {
919 .arg.vars.scope = SCOPE_PROC,
920 .from = ACT_F_CFG_PARSER,
921 };
Willy Tarreau753d4db2021-09-03 09:02:47 +0200922 enum obj_type objt = OBJ_TYPE_NONE;
923 struct session *sess = NULL;
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100924 enum act_parse_ret p_ret;
925 char *old_arg1;
926 char *tmp_arg1;
927 int arg = 2; // variable name
928 int ret = -1;
Willy Tarreau753d4db2021-09-03 09:02:47 +0200929 int use_fmt = 0;
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100930
931 LIST_INIT(&px.conf.args.list);
932
Willy Tarreau753d4db2021-09-03 09:02:47 +0200933 use_fmt = strcmp(args[0], "set-var-fmt") == 0;
934
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100935 if (!*args[1] || !*args[2]) {
Willy Tarreau753d4db2021-09-03 09:02:47 +0200936 if (use_fmt)
937 memprintf(err, "'%s' requires a process-wide variable name ('proc.<name>') and a format string.", args[0]);
938 else
939 memprintf(err, "'%s' requires a process-wide variable name ('proc.<name>') and a sample expression.", args[0]);
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100940 goto end;
941 }
942
943 tmp_arg1 = NULL;
Willy Tarreau753d4db2021-09-03 09:02:47 +0200944 if (!memprintf(&tmp_arg1, "set-var%s(%s)", use_fmt ? "-fmt" : "", args[1]))
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100945 goto end;
946
947 /* parse_store() will always return a message in <err> on error */
948 old_arg1 = args[1]; args[1] = tmp_arg1;
949 p_ret = parse_store((const char **)args, &arg, &px, &rule, err);
950 free(args[1]); args[1] = old_arg1;
951
952 if (p_ret != ACT_RET_PRS_OK)
953 goto end;
954
955 if (rule.arg.vars.scope != SCOPE_PROC) {
956 memprintf(err, "'%s': cannot set variable '%s', only scope 'proc' is permitted in the global section.", args[0], args[1]);
957 goto end;
958 }
959
960 if (smp_resolve_args(&px, err) != 0) {
961 release_sample_expr(rule.arg.vars.expr);
962 indent_msg(err, 2);
963 goto end;
964 }
965
Willy Tarreau753d4db2021-09-03 09:02:47 +0200966 if (use_fmt && !(sess = session_new(&px, NULL, &objt))) {
967 release_sample_expr(rule.arg.vars.expr);
968 memprintf(err, "'%s': out of memory when trying to set variable '%s' in the global section.", args[0], args[1]);
969 goto end;
970 }
971
972 action_store(&rule, &px, sess, NULL, 0);
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100973 release_sample_expr(rule.arg.vars.expr);
Willy Tarreau753d4db2021-09-03 09:02:47 +0200974 if (sess)
975 session_free(sess);
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100976
977 ret = 0;
978 end:
979 return ret;
980}
981
Willy Tarreauc35eb382021-03-26 14:51:31 +0100982/* parse CLI's "get var <name>" */
983static int vars_parse_cli_get_var(char **args, char *payload, struct appctx *appctx, void *private)
984{
985 struct vars *vars;
Willy Tarreau374edc72021-04-01 17:01:43 +0200986 struct sample smp = { };
Willy Tarreauc35eb382021-03-26 14:51:31 +0100987 int i;
988
989 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
990 return 1;
991
992 if (!*args[2])
993 return cli_err(appctx, "Missing process-wide variable identifier.\n");
994
995 vars = get_vars(NULL, NULL, SCOPE_PROC);
996 if (!vars || vars->scope != SCOPE_PROC)
997 return 0;
998
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200999 if (!vars_get_by_name(args[2], strlen(args[2]), &smp, NULL))
Willy Tarreauc35eb382021-03-26 14:51:31 +01001000 return cli_err(appctx, "Variable not found.\n");
1001
1002 /* the sample returned by vars_get_by_name() is allocated into a trash
1003 * chunk so we have no constraint to manipulate it.
1004 */
1005 chunk_printf(&trash, "%s: type=%s value=", args[2], smp_to_type[smp.data.type]);
1006
1007 if (!sample_casts[smp.data.type][SMP_T_STR] ||
1008 !sample_casts[smp.data.type][SMP_T_STR](&smp)) {
1009 chunk_appendf(&trash, "(undisplayable)");
1010 } else {
1011 /* Display the displayable chars*. */
1012 b_putchr(&trash, '<');
1013 for (i = 0; i < smp.data.u.str.data; i++) {
1014 if (isprint((unsigned char)smp.data.u.str.area[i]))
1015 b_putchr(&trash, smp.data.u.str.area[i]);
1016 else
1017 b_putchr(&trash, '.');
1018 }
1019 b_putchr(&trash, '>');
1020 b_putchr(&trash, 0);
1021 }
1022 return cli_msg(appctx, LOG_INFO, trash.area);
1023}
1024
Willy Tarreaue93bff42021-09-03 09:47:37 +02001025/* parse CLI's "set var <name>". It accepts:
1026 * - set var <name> <expression>
1027 * - set var <name> expr <expression>
1028 * - set var <name> fmt <format>
1029 */
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001030static int vars_parse_cli_set_var(char **args, char *payload, struct appctx *appctx, void *private)
1031{
1032 struct proxy px = {
1033 .id = "CLI",
1034 .conf.args.file = "CLI",
1035 .conf.args.line = 0,
1036 };
1037 struct act_rule rule = {
1038 .arg.vars.scope = SCOPE_PROC,
1039 .from = ACT_F_CLI_PARSER,
1040 };
Willy Tarreaue93bff42021-09-03 09:47:37 +02001041 enum obj_type objt = OBJ_TYPE_NONE;
1042 struct session *sess = NULL;
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001043 enum act_parse_ret p_ret;
Willy Tarreaue93bff42021-09-03 09:47:37 +02001044 const char *tmp_args[3];
1045 int tmp_arg;
1046 char *tmp_act;
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001047 char *err = NULL;
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001048 int nberr;
Willy Tarreaue93bff42021-09-03 09:47:37 +02001049 int use_fmt = 0;
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001050
1051 LIST_INIT(&px.conf.args.list);
1052
1053 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
1054 return 1;
1055
Willy Tarreaue93bff42021-09-03 09:47:37 +02001056 if (!*args[2])
1057 return cli_err(appctx, "Missing process-wide variable identifier.\n");
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001058
Willy Tarreaue93bff42021-09-03 09:47:37 +02001059 if (!*args[3])
1060 return cli_err(appctx, "Missing either 'expr', 'fmt' or expression.\n");
1061
1062 if (*args[4]) {
1063 /* this is the long format */
1064 if (strcmp(args[3], "fmt") == 0)
1065 use_fmt = 1;
1066 else if (strcmp(args[3], "expr") != 0) {
1067 memprintf(&err, "'%s %s': arg type must be either 'expr' or 'fmt' but got '%s'.", args[0], args[1], args[3]);
1068 goto fail;
1069 }
1070 }
1071
1072 tmp_act = NULL;
1073 if (!memprintf(&tmp_act, "set-var%s(%s)", use_fmt ? "-fmt" : "", args[2])) {
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001074 memprintf(&err, "memory allocation error.");
1075 goto fail;
1076 }
1077
1078 /* parse_store() will always return a message in <err> on error */
Willy Tarreaue93bff42021-09-03 09:47:37 +02001079 tmp_args[0] = tmp_act;
1080 tmp_args[1] = (*args[4]) ? args[4] : args[3];
1081 tmp_args[2] = "";
1082 tmp_arg = 1; // must point to the first arg after the action
1083 p_ret = parse_store(tmp_args, &tmp_arg, &px, &rule, &err);
1084 free(tmp_act);
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001085
1086 if (p_ret != ACT_RET_PRS_OK)
1087 goto fail;
1088
1089 if (rule.arg.vars.scope != SCOPE_PROC) {
Willy Tarreauc767eeb2021-09-03 10:23:26 +02001090 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 +01001091 goto fail;
1092 }
1093
1094 err = NULL;
1095 nberr = smp_resolve_args(&px, &err);
1096 if (nberr) {
1097 release_sample_expr(rule.arg.vars.expr);
1098 indent_msg(&err, 2);
1099 goto fail;
1100 }
1101
Willy Tarreaue93bff42021-09-03 09:47:37 +02001102 if (use_fmt && !(sess = session_new(&px, NULL, &objt))) {
1103 release_sample_expr(rule.arg.vars.expr);
1104 memprintf(&err, "memory allocation error.");
1105 goto fail;
1106 }
1107
1108 action_store(&rule, &px, sess, NULL, 0);
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001109 release_sample_expr(rule.arg.vars.expr);
Willy Tarreaue93bff42021-09-03 09:47:37 +02001110 if (sess)
1111 session_free(sess);
1112
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001113 appctx->st0 = CLI_ST_PROMPT;
1114 return 0;
1115 fail:
1116 return cli_dynerr(appctx, err);
1117}
1118
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001119static int vars_max_size(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001120 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001121 char **err, unsigned int *limit)
1122{
1123 char *error;
1124
1125 *limit = strtol(args[1], &error, 10);
1126 if (*error != 0) {
1127 memprintf(err, "%s: '%s' is an invalid size", args[0], args[1]);
1128 return -1;
1129 }
1130 return 0;
1131}
1132
1133static int vars_max_size_global(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001134 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001135 char **err)
1136{
1137 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_global_limit);
1138}
1139
Christopher Fauletff2613e2016-11-09 11:36:17 +01001140static int vars_max_size_proc(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001141 const struct proxy *defpx, const char *file, int line,
Christopher Fauletff2613e2016-11-09 11:36:17 +01001142 char **err)
1143{
1144 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_proc_limit);
1145}
1146
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001147static int vars_max_size_sess(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001148 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001149 char **err)
1150{
1151 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_sess_limit);
1152}
1153
1154static int vars_max_size_txn(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001155 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001156 char **err)
1157{
1158 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_txn_limit);
1159}
1160
1161static int vars_max_size_reqres(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001162 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001163 char **err)
1164{
1165 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_reqres_limit);
1166}
1167
Gaetan Rivet13a50432020-02-21 18:13:44 +01001168static int vars_max_size_check(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001169 const struct proxy *defpx, const char *file, int line,
Gaetan Rivet13a50432020-02-21 18:13:44 +01001170 char **err)
1171{
1172 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_check_limit);
1173}
1174
Willy Tarreau2c897d92021-08-31 08:48:55 +02001175/* early boot initialization */
1176static void vars_init()
1177{
1178 var_name_hash_seed = ha_random64();
1179}
1180
1181INITCALL0(STG_PREPARE, vars_init);
1182
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001183static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
1184
Willy Tarreau54496a62021-09-03 12:00:13 +02001185 { "var", smp_fetch_var, ARG2(1,STR,STR), smp_check_var, SMP_T_STR, SMP_USE_CONST },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001186 { /* END */ },
1187}};
1188
Willy Tarreau0108d902018-11-25 19:14:37 +01001189INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);
1190
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001191static struct sample_conv_kw_list sample_conv_kws = {ILH, {
Christopher Faulet85d79c92016-11-09 16:54:56 +01001192 { "set-var", smp_conv_store, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
1193 { "unset-var", smp_conv_clear, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001194 { /* END */ },
1195}};
1196
Willy Tarreau0108d902018-11-25 19:14:37 +01001197INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws);
1198
Willy Tarreau620408f2016-10-21 16:37:51 +02001199static struct action_kw_list tcp_req_sess_kws = { { }, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001200 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001201 { "set-var", parse_store, KWF_MATCH_PREFIX },
1202 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Willy Tarreau620408f2016-10-21 16:37:51 +02001203 { /* END */ }
1204}};
1205
Willy Tarreau0108d902018-11-25 19:14:37 +01001206INITCALL1(STG_REGISTER, tcp_req_sess_keywords_register, &tcp_req_sess_kws);
1207
Willy Tarreau620408f2016-10-21 16:37:51 +02001208static struct action_kw_list tcp_req_cont_kws = { { }, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001209 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001210 { "set-var", parse_store, KWF_MATCH_PREFIX },
1211 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001212 { /* END */ }
1213}};
1214
Willy Tarreau0108d902018-11-25 19:14:37 +01001215INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_cont_kws);
1216
Thierry FOURNIER36481b82015-08-19 09:01:53 +02001217static struct action_kw_list tcp_res_kws = { { }, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001218 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001219 { "set-var", parse_store, KWF_MATCH_PREFIX },
1220 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001221 { /* END */ }
1222}};
1223
Willy Tarreau0108d902018-11-25 19:14:37 +01001224INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_kws);
1225
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001226static struct action_kw_list tcp_check_kws = {ILH, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001227 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001228 { "set-var", parse_store, KWF_MATCH_PREFIX },
1229 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001230 { /* END */ }
1231}};
1232
1233INITCALL1(STG_REGISTER, tcp_check_keywords_register, &tcp_check_kws);
1234
Thierry FOURNIER36481b82015-08-19 09:01:53 +02001235static struct action_kw_list http_req_kws = { { }, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001236 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001237 { "set-var", parse_store, KWF_MATCH_PREFIX },
1238 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001239 { /* END */ }
1240}};
1241
Willy Tarreau0108d902018-11-25 19:14:37 +01001242INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_kws);
1243
Thierry FOURNIER36481b82015-08-19 09:01:53 +02001244static struct action_kw_list http_res_kws = { { }, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001245 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001246 { "set-var", parse_store, KWF_MATCH_PREFIX },
1247 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001248 { /* END */ }
1249}};
1250
Willy Tarreau0108d902018-11-25 19:14:37 +01001251INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_kws);
1252
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01001253static struct action_kw_list http_after_res_kws = { { }, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001254 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001255 { "set-var", parse_store, KWF_MATCH_PREFIX },
1256 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01001257 { /* END */ }
1258}};
1259
1260INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_kws);
1261
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001262static struct cfg_kw_list cfg_kws = {{ },{
Willy Tarreau13d2ba22021-03-26 11:38:08 +01001263 { CFG_GLOBAL, "set-var", vars_parse_global_set_var },
Willy Tarreau753d4db2021-09-03 09:02:47 +02001264 { CFG_GLOBAL, "set-var-fmt", vars_parse_global_set_var },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001265 { CFG_GLOBAL, "tune.vars.global-max-size", vars_max_size_global },
Christopher Fauletff2613e2016-11-09 11:36:17 +01001266 { CFG_GLOBAL, "tune.vars.proc-max-size", vars_max_size_proc },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001267 { CFG_GLOBAL, "tune.vars.sess-max-size", vars_max_size_sess },
1268 { CFG_GLOBAL, "tune.vars.txn-max-size", vars_max_size_txn },
1269 { CFG_GLOBAL, "tune.vars.reqres-max-size", vars_max_size_reqres },
Gaetan Rivet13a50432020-02-21 18:13:44 +01001270 { CFG_GLOBAL, "tune.vars.check-max-size", vars_max_size_check },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001271 { /* END */ }
1272}};
1273
Willy Tarreau0108d902018-11-25 19:14:37 +01001274INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Willy Tarreauc35eb382021-03-26 14:51:31 +01001275
1276
1277/* register cli keywords */
1278static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02001279 { { "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 +02001280 { { "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 +01001281 { { NULL }, NULL, NULL, NULL }
1282}};
1283INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);