blob: a61fb12338d890b043dbcf07b9678113630e61dc [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
Willy Tarreauf37b1402019-06-04 16:27:36 +020039/* returns the struct vars pointer for a session, stream and scope, or NULL if
40 * it does not exist.
41 */
42static inline struct vars *get_vars(struct session *sess, struct stream *strm, enum vars_scope scope)
43{
44 switch (scope) {
45 case SCOPE_PROC:
Willy Tarreaucfc4f242021-05-08 11:41:28 +020046 return &proc_vars;
Willy Tarreauf37b1402019-06-04 16:27:36 +020047 case SCOPE_SESS:
Willy Tarreaua07d61b2021-03-26 11:27:59 +010048 return sess ? &sess->vars : NULL;
Gaetan Rivet13a50432020-02-21 18:13:44 +010049 case SCOPE_CHECK: {
Christopher Fauletc4439f72021-06-02 11:48:42 +020050 struct check *check = sess ? objt_check(sess->origin) : NULL;
Gaetan Rivet13a50432020-02-21 18:13:44 +010051
Christopher Faulet0fca7ed2020-04-21 11:53:32 +020052 return check ? &check->vars : NULL;
Gaetan Rivet13a50432020-02-21 18:13:44 +010053 }
Willy Tarreauf37b1402019-06-04 16:27:36 +020054 case SCOPE_TXN:
55 return strm ? &strm->vars_txn : NULL;
56 case SCOPE_REQ:
57 case SCOPE_RES:
58 default:
59 return strm ? &strm->vars_reqres : NULL;
60 }
61}
62
Willy Tarreau72330982015-06-19 11:21:56 +020063/* This function adds or remove memory size from the accounting. The inner
64 * pointers may be null when setting the outer ones only.
65 */
Miroslav Zagorac6deab792020-12-09 16:34:29 +010066void var_accounting_diff(struct vars *vars, struct session *sess, struct stream *strm, int size)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020067{
68 switch (vars->scope) {
69 case SCOPE_REQ:
70 case SCOPE_RES:
Willy Tarreau55f8a832021-09-08 15:51:06 +020071 if (var_reqres_limit && strm)
Willy Tarreauf37b1402019-06-04 16:27:36 +020072 _HA_ATOMIC_ADD(&strm->vars_reqres.size, size);
Willy Tarreau6204cd92016-03-10 16:33:04 +010073 /* fall through */
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020074 case SCOPE_TXN:
Willy Tarreau55f8a832021-09-08 15:51:06 +020075 if (var_txn_limit && strm)
Willy Tarreauf37b1402019-06-04 16:27:36 +020076 _HA_ATOMIC_ADD(&strm->vars_txn.size, size);
Gaetan Rivet13a50432020-02-21 18:13:44 +010077 goto scope_sess;
Willy Tarreau55f8a832021-09-08 15:51:06 +020078 case SCOPE_CHECK:
79 if (var_check_limit) {
Christopher Faulet0fca7ed2020-04-21 11:53:32 +020080 struct check *check = objt_check(sess->origin);
Gaetan Rivet13a50432020-02-21 18:13:44 +010081
Christopher Faulet0fca7ed2020-04-21 11:53:32 +020082 if (check)
83 _HA_ATOMIC_ADD(&check->vars.size, size);
Gaetan Rivet13a50432020-02-21 18:13:44 +010084 }
Willy Tarreau6204cd92016-03-10 16:33:04 +010085 /* fall through */
Gaetan Rivet13a50432020-02-21 18:13:44 +010086scope_sess:
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020087 case SCOPE_SESS:
Willy Tarreau55f8a832021-09-08 15:51:06 +020088 if (var_sess_limit)
89 _HA_ATOMIC_ADD(&sess->vars.size, size);
Christopher Fauletff2613e2016-11-09 11:36:17 +010090 /* fall through */
91 case SCOPE_PROC:
Willy Tarreau55f8a832021-09-08 15:51:06 +020092 if (var_proc_limit || var_global_limit)
93 _HA_ATOMIC_ADD(&proc_vars.size, size);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020094 }
95}
96
97/* This function returns 1 if the <size> is available in the var
Joseph Herlant07676892018-11-15 09:19:50 -080098 * pool <vars>, otherwise returns 0. If the space is available,
Willy Tarreau72330982015-06-19 11:21:56 +020099 * the size is reserved. The inner pointers may be null when setting
Willy Tarreau6204cd92016-03-10 16:33:04 +0100100 * the outer ones only. The accounting uses either <sess> or <strm>
101 * depending on the scope. <strm> may be NULL when no stream is known
102 * and only the session exists (eg: tcp-request connection).
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200103 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100104static int var_accounting_add(struct vars *vars, struct session *sess, struct stream *strm, int size)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200105{
106 switch (vars->scope) {
107 case SCOPE_REQ:
108 case SCOPE_RES:
Willy Tarreauf37b1402019-06-04 16:27:36 +0200109 if (var_reqres_limit && strm && strm->vars_reqres.size + size > var_reqres_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200110 return 0;
Willy Tarreau6204cd92016-03-10 16:33:04 +0100111 /* fall through */
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200112 case SCOPE_TXN:
Willy Tarreauf37b1402019-06-04 16:27:36 +0200113 if (var_txn_limit && strm && strm->vars_txn.size + size > var_txn_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200114 return 0;
Gaetan Rivet13a50432020-02-21 18:13:44 +0100115 goto scope_sess;
116 case SCOPE_CHECK: {
Christopher Faulet0fca7ed2020-04-21 11:53:32 +0200117 struct check *check = objt_check(sess->origin);
Gaetan Rivet13a50432020-02-21 18:13:44 +0100118
Christopher Faulet0fca7ed2020-04-21 11:53:32 +0200119 if (var_check_limit && check && check->vars.size + size > var_check_limit)
Gaetan Rivet13a50432020-02-21 18:13:44 +0100120 return 0;
121 }
Willy Tarreau6204cd92016-03-10 16:33:04 +0100122 /* fall through */
Gaetan Rivet13a50432020-02-21 18:13:44 +0100123scope_sess:
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200124 case SCOPE_SESS:
Willy Tarreau6204cd92016-03-10 16:33:04 +0100125 if (var_sess_limit && sess->vars.size + size > var_sess_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200126 return 0;
Christopher Fauletff2613e2016-11-09 11:36:17 +0100127 /* fall through */
128 case SCOPE_PROC:
Willy Tarreau3b78f2a2021-09-08 15:40:58 +0200129 /* note: scope proc collects all others and is currently identical to the
130 * global limit.
131 */
Willy Tarreaucfc4f242021-05-08 11:41:28 +0200132 if (var_proc_limit && proc_vars.size + size > var_proc_limit)
Christopher Fauletff2613e2016-11-09 11:36:17 +0100133 return 0;
Willy Tarreau3b78f2a2021-09-08 15:40:58 +0200134 if (var_global_limit && proc_vars.size + size > var_global_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200135 return 0;
136 }
Willy Tarreau6204cd92016-03-10 16:33:04 +0100137 var_accounting_diff(vars, sess, strm, size);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200138 return 1;
139}
140
Willy Tarreauc1c88f42021-09-08 15:03:58 +0200141/* This function removes a variable from the list and frees the memory it was
142 * using. If the variable is marked "VF_PERMANENT", the sample_data is only
143 * reset to SMP_T_ANY unless <force> is non nul. Returns the freed size.
144 */
145unsigned int var_clear(struct var *var, int force)
Christopher Faulet85d79c92016-11-09 16:54:56 +0100146{
147 unsigned int size = 0;
148
149 if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
Willy Tarreau5b52b002021-02-26 21:19:53 +0100150 ha_free(&var->data.u.str.area);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200151 size += var->data.u.str.data;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100152 }
Christopher Fauletd02210c2017-07-24 16:24:39 +0200153 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
Willy Tarreau5b52b002021-02-26 21:19:53 +0100154 ha_free(&var->data.u.meth.str.area);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200155 size += var->data.u.meth.str.data;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100156 }
Willy Tarreauc1c88f42021-09-08 15:03:58 +0200157 /* wipe the sample */
158 var->data.type = SMP_T_ANY;
159
160 if (!(var->flags & VF_PERMANENT) || force) {
161 LIST_DELETE(&var->l);
162 pool_free(var_pool, var);
163 size += sizeof(struct var);
164 }
Christopher Faulet85d79c92016-11-09 16:54:56 +0100165 return size;
166}
167
Joseph Herlant07676892018-11-15 09:19:50 -0800168/* This function free all the memory used by all the variables
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200169 * in the list.
170 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100171void vars_prune(struct vars *vars, struct session *sess, struct stream *strm)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200172{
173 struct var *var, *tmp;
Willy Tarreau72330982015-06-19 11:21:56 +0200174 unsigned int size = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200175
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200176 vars_wrlock(vars);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200177 list_for_each_entry_safe(var, tmp, &vars->head, l) {
Willy Tarreauc1c88f42021-09-08 15:03:58 +0200178 size += var_clear(var, 1);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200179 }
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200180 vars_wrunlock(vars);
Willy Tarreau6204cd92016-03-10 16:33:04 +0100181 var_accounting_diff(vars, sess, strm, -size);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200182}
183
Willy Tarreauebcd4842015-06-19 11:59:02 +0200184/* This function frees all the memory used by all the session variables in the
185 * list starting at <vars>.
186 */
187void vars_prune_per_sess(struct vars *vars)
188{
189 struct var *var, *tmp;
190 unsigned int size = 0;
191
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200192 vars_wrlock(vars);
Willy Tarreauebcd4842015-06-19 11:59:02 +0200193 list_for_each_entry_safe(var, tmp, &vars->head, l) {
Willy Tarreauc1c88f42021-09-08 15:03:58 +0200194 size += var_clear(var, 1);
Willy Tarreauebcd4842015-06-19 11:59:02 +0200195 }
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200196 vars_wrunlock(vars);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200197
Willy Tarreau55f8a832021-09-08 15:51:06 +0200198 if (var_sess_limit)
199 _HA_ATOMIC_SUB(&vars->size, size);
200 if (var_proc_limit || var_global_limit)
201 _HA_ATOMIC_SUB(&proc_vars.size, size);
Willy Tarreauebcd4842015-06-19 11:59:02 +0200202}
203
Willy Tarreaub7bfcb32021-08-31 08:13:25 +0200204/* This function initializes a variables list head */
205void vars_init_head(struct vars *vars, enum vars_scope scope)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200206{
207 LIST_INIT(&vars->head);
208 vars->scope = scope;
209 vars->size = 0;
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100210 HA_RWLOCK_INIT(&vars->rwlock);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200211}
212
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200213/* This function returns a hash value and a scope for a variable name of a
214 * specified length. It makes sure that the scope is valid. It returns non-zero
215 * on success, 0 on failure. Neither hash nor scope may be NULL.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200216 */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200217static int vars_hash_name(const char *name, int len, enum vars_scope *scope,
218 uint64_t *hash, char **err)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200219{
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200220 const char *tmp;
221
222 /* Check length. */
223 if (len == 0) {
224 memprintf(err, "Empty variable name cannot be accepted");
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200225 return 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200226 }
227
228 /* Check scope. */
Christopher Fauletff2613e2016-11-09 11:36:17 +0100229 if (len > 5 && strncmp(name, "proc.", 5) == 0) {
230 name += 5;
231 len -= 5;
232 *scope = SCOPE_PROC;
233 }
234 else if (len > 5 && strncmp(name, "sess.", 5) == 0) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200235 name += 5;
236 len -= 5;
237 *scope = SCOPE_SESS;
238 }
239 else if (len > 4 && strncmp(name, "txn.", 4) == 0) {
240 name += 4;
241 len -= 4;
242 *scope = SCOPE_TXN;
243 }
244 else if (len > 4 && strncmp(name, "req.", 4) == 0) {
245 name += 4;
246 len -= 4;
247 *scope = SCOPE_REQ;
248 }
249 else if (len > 4 && strncmp(name, "res.", 4) == 0) {
250 name += 4;
251 len -= 4;
252 *scope = SCOPE_RES;
253 }
Gaetan Rivet13a50432020-02-21 18:13:44 +0100254 else if (len > 6 && strncmp(name, "check.", 6) == 0) {
255 name += 6;
256 len -= 6;
257 *scope = SCOPE_CHECK;
258 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200259 else {
Willy Tarreau1402fef2021-09-03 10:12:55 +0200260 memprintf(err, "invalid variable name '%.*s'. A variable name must be start by its scope. "
261 "The scope can be 'proc', 'sess', 'txn', 'req', 'res' or 'check'", len, name);
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200262 return 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200263 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200264
265 /* Check variable name syntax. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200266 for (tmp = name; tmp < name + len; tmp++) {
Willy Tarreau90807112020-02-25 08:16:33 +0100267 if (!isalnum((unsigned char)*tmp) && *tmp != '_' && *tmp != '.') {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200268 memprintf(err, "invalid syntax at char '%s'", tmp);
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200269 return 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200270 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200271 }
Christopher Faulete95f2c32017-07-24 16:30:34 +0200272
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200273 *hash = XXH3(name, len, var_name_hash_seed);
274 return 1;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200275}
276
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200277/* This function returns the variable from the given list that matches
278 * <name_hash> or returns NULL if not found. It's only a linked list since it
279 * is not expected to have many variables per scope (a few tens at best).
280 * The caller is responsible for ensuring that <vars> is properly locked.
281 */
282static struct var *var_get(struct vars *vars, uint64_t name_hash)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200283{
284 struct var *var;
285
286 list_for_each_entry(var, &vars->head, l)
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200287 if (var->name_hash == name_hash)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200288 return var;
289 return NULL;
290}
291
292/* Returns 0 if fails, else returns 1. */
293static int smp_fetch_var(const struct arg *args, struct sample *smp, const char *kw, void *private)
294{
295 const struct var_desc *var_desc = &args[0].data.var;
Willy Tarreau54496a62021-09-03 12:00:13 +0200296 const struct buffer *def = NULL;
Christopher Faulete95f2c32017-07-24 16:30:34 +0200297
Willy Tarreau54496a62021-09-03 12:00:13 +0200298 if (args[1].type == ARGT_STR)
299 def = &args[1].data.str;
300
301 return vars_get_by_desc(var_desc, smp, def);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200302}
303
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200304/* This function tries to create a variable whose name hash is <name_hash> in
305 * scope <scope> and store sample <smp> as its value.
306 *
307 * The stream and session are extracted from <smp>, whose stream may be NULL
308 * when scope is SCOPE_SESS. In case there wouldn't be enough memory to store
309 * the sample while the variable was already created, it would be changed to
310 * a bool (which is memory-less).
Willy Tarreau7978c5c2021-09-07 14:24:07 +0200311 *
312 * Flags is a bitfield that may contain one of the following flags:
313 * - VF_UPDATEONLY: if the scope is SCOPE_PROC, the variable may only be
314 * updated but not created.
Willy Tarreau4994b572021-09-08 11:38:25 +0200315 * - VF_CREATEONLY: do nothing if the variable already exists (success).
Willy Tarreau3dc6dc32021-09-08 11:07:32 +0200316 * - VF_PERMANENT: this flag will be passed to the variable upon creation
Willy Tarreau7978c5c2021-09-07 14:24:07 +0200317 *
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200318 * It returns 0 on failure, non-zero on success.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200319 */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200320static int var_set(uint64_t name_hash, enum vars_scope scope, struct sample *smp, uint flags)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200321{
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200322 struct vars *vars;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200323 struct var *var;
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200324 int ret = 0;
325
326 vars = get_vars(smp->sess, smp->strm, scope);
327 if (!vars || vars->scope != scope)
328 return 0;
329
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200330 vars_wrlock(vars);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200331
332 /* Look for existing variable name. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200333 var = var_get(vars, name_hash);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200334
335 if (var) {
Willy Tarreau4994b572021-09-08 11:38:25 +0200336 if (flags & VF_CREATEONLY) {
337 ret = 1;
338 goto unlock;
339 }
340
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200341 /* free its used memory. */
342 if (var->data.type == SMP_T_STR ||
343 var->data.type == SMP_T_BIN) {
Willy Tarreau5b52b002021-02-26 21:19:53 +0100344 ha_free(&var->data.u.str.area);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200345 var_accounting_diff(vars, smp->sess, smp->strm,
346 -var->data.u.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200347 }
Christopher Fauletd02210c2017-07-24 16:24:39 +0200348 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
Willy Tarreau5b52b002021-02-26 21:19:53 +0100349 ha_free(&var->data.u.meth.str.area);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200350 var_accounting_diff(vars, smp->sess, smp->strm,
351 -var->data.u.meth.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200352 }
353 } else {
Willy Tarreau7978c5c2021-09-07 14:24:07 +0200354 /* creation permitted for proc ? */
355 if (flags & VF_UPDATEONLY && scope == SCOPE_PROC)
356 goto unlock;
357
Joseph Herlant07676892018-11-15 09:19:50 -0800358 /* Check memory available. */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100359 if (!var_accounting_add(vars, smp->sess, smp->strm, sizeof(struct var)))
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200360 goto unlock;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200361
362 /* Create new entry. */
Willy Tarreaubafbe012017-11-24 17:34:44 +0100363 var = pool_alloc(var_pool);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200364 if (!var)
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200365 goto unlock;
Willy Tarreau2b718102021-04-21 07:32:39 +0200366 LIST_APPEND(&vars->head, &var->l);
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200367 var->name_hash = name_hash;
Willy Tarreau3dc6dc32021-09-08 11:07:32 +0200368 var->flags = flags & VF_PERMANENT;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200369 }
370
371 /* Set type. */
Thierry FOURNIER8c542ca2015-08-19 09:00:18 +0200372 var->data.type = smp->data.type;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200373
374 /* Copy data. If the data needs memory, the function can fail. */
375 switch (var->data.type) {
376 case SMP_T_BOOL:
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200377 case SMP_T_SINT:
Thierry FOURNIER136f9d32015-08-19 09:07:19 +0200378 var->data.u.sint = smp->data.u.sint;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200379 break;
380 case SMP_T_IPV4:
Thierry FOURNIER136f9d32015-08-19 09:07:19 +0200381 var->data.u.ipv4 = smp->data.u.ipv4;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200382 break;
383 case SMP_T_IPV6:
Thierry FOURNIER136f9d32015-08-19 09:07:19 +0200384 var->data.u.ipv6 = smp->data.u.ipv6;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200385 break;
386 case SMP_T_STR:
387 case SMP_T_BIN:
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200388 if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.str.data)) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200389 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200390 goto unlock;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200391 }
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200392
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200393 var->data.u.str.area = malloc(smp->data.u.str.data);
394 if (!var->data.u.str.area) {
395 var_accounting_diff(vars, smp->sess, smp->strm,
396 -smp->data.u.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200397 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200398 goto unlock;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200399 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200400 var->data.u.str.data = smp->data.u.str.data;
401 memcpy(var->data.u.str.area, smp->data.u.str.area,
402 var->data.u.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200403 break;
404 case SMP_T_METH:
Christopher Fauletd02210c2017-07-24 16:24:39 +0200405 var->data.u.meth.meth = smp->data.u.meth.meth;
406 if (smp->data.u.meth.meth != HTTP_METH_OTHER)
407 break;
408
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200409 if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.meth.str.data)) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200410 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200411 goto unlock;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200412 }
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200413
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200414 var->data.u.meth.str.area = malloc(smp->data.u.meth.str.data);
415 if (!var->data.u.meth.str.area) {
416 var_accounting_diff(vars, smp->sess, smp->strm,
417 -smp->data.u.meth.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200418 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200419 goto unlock;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200420 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200421 var->data.u.meth.str.data = smp->data.u.meth.str.data;
422 var->data.u.meth.str.size = smp->data.u.meth.str.data;
423 memcpy(var->data.u.meth.str.area, smp->data.u.meth.str.area,
424 var->data.u.meth.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200425 break;
426 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200427
Willy Tarreauf1cb0eb2021-09-07 11:37:37 +0200428 /* OK, now done */
429 ret = 1;
430 unlock:
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200431 vars_wrunlock(vars);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200432 return ret;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200433}
434
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200435/* Deletes a variable matching name hash <name_hash> and scope <scope> for the
436 * session and stream found in <smp>. Note that stream may be null for
437 * SCOPE_SESS. Returns 0 if the scope was not found otherwise 1.
Willy Tarreaud378eb82021-09-07 11:44:41 +0200438 */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200439static int var_unset(uint64_t name_hash, enum vars_scope scope, struct sample *smp)
Christopher Faulet85d79c92016-11-09 16:54:56 +0100440{
441 struct vars *vars;
442 struct var *var;
443 unsigned int size = 0;
444
Willy Tarreauf37b1402019-06-04 16:27:36 +0200445 vars = get_vars(smp->sess, smp->strm, scope);
446 if (!vars || vars->scope != scope)
Christopher Faulet85d79c92016-11-09 16:54:56 +0100447 return 0;
448
449 /* Look for existing variable name. */
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200450 vars_wrlock(vars);
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200451 var = var_get(vars, name_hash);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100452 if (var) {
Willy Tarreauc1c88f42021-09-08 15:03:58 +0200453 size = var_clear(var, 0);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100454 var_accounting_diff(vars, smp->sess, smp->strm, -size);
455 }
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200456 vars_wrunlock(vars);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100457 return 1;
458}
459
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200460/* Returns 0 if fails, else returns 1. */
461static int smp_conv_store(const struct arg *args, struct sample *smp, void *private)
462{
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200463 uint64_t seed = var_name_hash_seed;
464 uint64_t name_hash = XXH3(smp->data.u.str.area, smp->data.u.str.data, seed);
465
466 return var_set(name_hash, args[0].data.var.scope, smp, 0);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200467}
468
Christopher Faulet85d79c92016-11-09 16:54:56 +0100469/* Returns 0 if fails, else returns 1. */
470static int smp_conv_clear(const struct arg *args, struct sample *smp, void *private)
471{
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200472 uint64_t seed = var_name_hash_seed;
473 uint64_t name_hash = XXH3(smp->data.u.str.area, smp->data.u.str.data, seed);
474
475 return var_unset(name_hash, args[0].data.var.scope, smp);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100476}
477
Joseph Herlant07676892018-11-15 09:19:50 -0800478/* This functions check an argument entry and fill it with a variable
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200479 * type. The argumen must be a string. If the variable lookup fails,
Joseph Herlant07676892018-11-15 09:19:50 -0800480 * the function returns 0 and fill <err>, otherwise it returns 1.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200481 */
482int vars_check_arg(struct arg *arg, char **err)
483{
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200484 enum vars_scope scope;
Willy Tarreaudf8eeb12021-09-08 11:07:32 +0200485 struct sample empty_smp = { };
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200486 uint64_t hash;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200487
488 /* Check arg type. */
489 if (arg->type != ARGT_STR) {
490 memprintf(err, "unexpected argument type");
491 return 0;
492 }
493
494 /* Register new variable name. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200495 if (!vars_hash_name(arg->data.str.area, arg->data.str.data, &scope, &hash, err))
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200496 return 0;
497
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200498 if (scope == SCOPE_PROC && !var_set(hash, scope, &empty_smp, VF_CREATEONLY|VF_PERMANENT))
Willy Tarreaudf8eeb12021-09-08 11:07:32 +0200499 return 0;
500
Tim Duesterhusa6cc7e82019-05-13 10:53:29 +0200501 /* properly destroy the chunk */
502 chunk_destroy(&arg->data.str);
503
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200504 /* Use the global variable name pointer. */
505 arg->type = ARGT_VAR;
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200506 arg->data.var.name_hash = hash;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200507 arg->data.var.scope = scope;
508 return 1;
509}
510
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200511/* This function stores a sample in a variable if it was already defined.
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200512 * Returns zero on failure and non-zero otherwise. The variable not being
513 * defined is treated as a failure.
Christopher Faulet09c9df22016-10-31 11:05:37 +0100514 */
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200515int vars_set_by_name_ifexist(const char *name, size_t len, struct sample *smp)
Christopher Faulet09c9df22016-10-31 11:05:37 +0100516{
517 enum vars_scope scope;
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200518 uint64_t hash;
Christopher Faulet09c9df22016-10-31 11:05:37 +0100519
520 /* Resolve name and scope. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200521 if (!vars_hash_name(name, len, &scope, &hash, NULL))
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200522 return 0;
Christopher Faulet09c9df22016-10-31 11:05:37 +0100523
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200524 return var_set(hash, scope, smp, VF_UPDATEONLY);
Christopher Faulet09c9df22016-10-31 11:05:37 +0100525}
526
527
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200528/* This function stores a sample in a variable.
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200529 * Returns zero on failure and non-zero otherwise.
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200530 */
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200531int vars_set_by_name(const char *name, size_t len, struct sample *smp)
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200532{
533 enum vars_scope scope;
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200534 uint64_t hash;
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200535
536 /* Resolve name and scope. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200537 if (!vars_hash_name(name, len, &scope, &hash, NULL))
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200538 return 0;
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200539
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200540 return var_set(hash, scope, smp, 0);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200541}
542
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200543/* This function unsets a variable if it was already defined.
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200544 * Returns zero on failure and non-zero otherwise.
Christopher Faulet85d79c92016-11-09 16:54:56 +0100545 */
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200546int vars_unset_by_name_ifexist(const char *name, size_t len, struct sample *smp)
Christopher Faulet85d79c92016-11-09 16:54:56 +0100547{
548 enum vars_scope scope;
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200549 uint64_t hash;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100550
551 /* Resolve name and scope. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200552 if (!vars_hash_name(name, len, &scope, &hash, NULL))
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200553 return 0;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100554
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200555 return var_unset(hash, scope, smp);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100556}
557
558
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200559/* This retrieves variable whose hash matches <name_hash> from variables <vars>,
560 * and if found and not empty, duplicates the result into sample <smp>.
561 * smp_dup() is used in order to release the variables lock ASAP (so a pre-
562 * allocated chunk is obtained via get_trash_shunk()). The variables' lock is
563 * used for reads.
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200564 *
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200565 * The function returns 0 if the variable was not found and no default
566 * value was provided in <def>, otherwise 1 with the sample filled.
567 * Default values are always returned as strings.
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200568 */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200569static 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 +0200570{
571 struct var *var;
572
573 /* Get the variable entry. */
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200574 vars_rdlock(vars);
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200575 var = var_get(vars, name_hash);
Willy Tarreau63c30662021-09-08 13:58:19 +0200576 if (!var || !var->data.type) {
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200577 if (!def) {
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200578 vars_rdunlock(vars);
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200579 return 0;
580 }
581
582 /* not found but we have a default value */
583 smp->data.type = SMP_T_STR;
584 smp->data.u.str = *def;
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200585 }
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200586 else
587 smp->data = var->data;
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200588
589 /* Copy sample. */
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200590 smp_dup(smp);
591
Willy Tarreaudc72fbb2021-09-08 15:19:57 +0200592 vars_rdunlock(vars);
Willy Tarreaube7e00d2021-09-03 11:40:58 +0200593 return 1;
594}
595
Dragan Dosen14518f22021-02-22 17:20:01 +0100596/* This function fills a sample with the variable content.
597 *
598 * Keep in mind that a sample content is duplicated by using smp_dup()
599 * and it therefore uses a pre-allocated trash chunk as returned by
600 * get_trash_chunk().
601 *
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200602 * If the variable is not valid in this scope, 0 is always returned.
603 * If the variable is valid but not found, either the default value
604 * <def> is returned if not NULL, or zero is returned.
605 *
Dragan Dosen14518f22021-02-22 17:20:01 +0100606 * Returns 1 if the sample is filled, otherwise it returns 0.
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200607 */
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200608int vars_get_by_name(const char *name, size_t len, struct sample *smp, const struct buffer *def)
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200609{
610 struct vars *vars;
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200611 enum vars_scope scope;
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200612 uint64_t hash;
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200613
614 /* Resolve name and scope. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200615 if (!vars_hash_name(name, len, &scope, &hash, NULL))
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200616 return 0;
617
618 /* Select "vars" pool according with the scope. */
Willy Tarreauf37b1402019-06-04 16:27:36 +0200619 vars = get_vars(smp->sess, smp->strm, scope);
620 if (!vars || vars->scope != scope)
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200621 return 0;
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200622
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200623 return var_to_smp(vars, hash, smp, def);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200624}
625
Dragan Dosen14518f22021-02-22 17:20:01 +0100626/* This function fills a sample with the content of the variable described
627 * by <var_desc>.
628 *
629 * Keep in mind that a sample content is duplicated by using smp_dup()
630 * and it therefore uses a pre-allocated trash chunk as returned by
631 * get_trash_chunk().
632 *
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200633 * If the variable is not valid in this scope, 0 is always returned.
634 * If the variable is valid but not found, either the default value
635 * <def> is returned if not NULL, or zero is returned.
636 *
Dragan Dosen14518f22021-02-22 17:20:01 +0100637 * Returns 1 if the sample is filled, otherwise it returns 0.
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200638 */
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200639int vars_get_by_desc(const struct var_desc *var_desc, struct sample *smp, const struct buffer *def)
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200640{
641 struct vars *vars;
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200642
643 /* Select "vars" pool according with the scope. */
Willy Tarreauf37b1402019-06-04 16:27:36 +0200644 vars = get_vars(smp->sess, smp->strm, var_desc->scope);
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200645
Joseph Herlant07676892018-11-15 09:19:50 -0800646 /* Check if the scope is available a this point of processing. */
Willy Tarreauf37b1402019-06-04 16:27:36 +0200647 if (!vars || vars->scope != var_desc->scope)
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200648 return 0;
649
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200650 return var_to_smp(vars, var_desc->name_hash, smp, def);
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200651}
652
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200653/* Always returns ACT_RET_CONT even if an error occurs. */
654static enum act_return action_store(struct act_rule *rule, struct proxy *px,
Willy Tarreau658b85b2015-09-27 10:00:49 +0200655 struct session *sess, struct stream *s, int flags)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200656{
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200657 struct buffer *fmtstr = NULL;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200658 struct sample smp;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200659 int dir;
660
661 switch (rule->from) {
Jaroslaw Rzeszótkoc8637032021-11-02 16:56:05 +0100662 case ACT_F_TCP_REQ_CON: dir = SMP_OPT_DIR_REQ; break;
Willy Tarreau620408f2016-10-21 16:37:51 +0200663 case ACT_F_TCP_REQ_SES: dir = SMP_OPT_DIR_REQ; break;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200664 case ACT_F_TCP_REQ_CNT: dir = SMP_OPT_DIR_REQ; break;
665 case ACT_F_TCP_RES_CNT: dir = SMP_OPT_DIR_RES; break;
666 case ACT_F_HTTP_REQ: dir = SMP_OPT_DIR_REQ; break;
667 case ACT_F_HTTP_RES: dir = SMP_OPT_DIR_RES; break;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +0100668 case ACT_F_TCP_CHK: dir = SMP_OPT_DIR_REQ; break;
Willy Tarreau01d580a2021-03-26 11:11:34 +0100669 case ACT_F_CFG_PARSER: dir = SMP_OPT_DIR_REQ; break; /* not used anyway */
Willy Tarreau2f836de2021-03-26 15:36:44 +0100670 case ACT_F_CLI_PARSER: dir = SMP_OPT_DIR_REQ; break; /* not used anyway */
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200671 default:
672 send_log(px, LOG_ERR, "Vars: internal error while execute action store.");
673 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Christopher Faulet767a84b2017-11-24 16:50:31 +0100674 ha_alert("Vars: internal error while execute action store.\n");
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200675 return ACT_RET_CONT;
676 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200677
678 /* Process the expression. */
679 memset(&smp, 0, sizeof(smp));
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200680
681 if (!LIST_ISEMPTY(&rule->arg.vars.fmt)) {
682 /* a format-string is used */
683
684 fmtstr = alloc_trash_chunk();
685 if (!fmtstr) {
686 send_log(px, LOG_ERR, "Vars: memory allocation failure while processing store rule.");
687 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
688 ha_alert("Vars: memory allocation failure while processing store rule.\n");
689 return ACT_RET_CONT;
690 }
691
692 /* execute the log-format expression */
693 fmtstr->data = sess_build_logline(sess, s, fmtstr->area, fmtstr->size, &rule->arg.vars.fmt);
694
695 /* convert it to a sample of type string as it's what the vars
696 * API consumes, and store it.
697 */
698 smp_set_owner(&smp, px, sess, s, 0);
699 smp.data.type = SMP_T_STR;
700 smp.data.u.str = *fmtstr;
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200701 var_set(rule->arg.vars.name_hash, rule->arg.vars.scope, &smp, 0);
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200702 }
703 else {
704 /* an expression is used */
705 if (!sample_process(px, sess, s, dir|SMP_OPT_FINAL,
706 rule->arg.vars.expr, &smp))
707 return ACT_RET_CONT;
708 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200709
710 /* Store the sample, and ignore errors. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200711 var_set(rule->arg.vars.name_hash, rule->arg.vars.scope, &smp, 0);
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200712 free_trash_chunk(fmtstr);
Thierry FOURNIER24ff6c62015-08-06 08:52:53 +0200713 return ACT_RET_CONT;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200714}
715
Christopher Faulet85d79c92016-11-09 16:54:56 +0100716/* Always returns ACT_RET_CONT even if an error occurs. */
717static enum act_return action_clear(struct act_rule *rule, struct proxy *px,
718 struct session *sess, struct stream *s, int flags)
719{
720 struct sample smp;
721
722 memset(&smp, 0, sizeof(smp));
723 smp_set_owner(&smp, px, sess, s, SMP_OPT_FINAL);
724
725 /* Clear the variable using the sample context, and ignore errors. */
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200726 var_unset(rule->arg.vars.name_hash, rule->arg.vars.scope, &smp);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100727 return ACT_RET_CONT;
728}
729
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200730static void release_store_rule(struct act_rule *rule)
731{
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200732 struct logformat_node *lf, *lfb;
Willy Tarreauc77bad22021-09-03 10:58:07 +0200733
734 list_for_each_entry_safe(lf, lfb, &rule->arg.vars.fmt, list) {
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200735 LIST_DELETE(&lf->list);
736 release_sample_expr(lf->expr);
737 free(lf->arg);
738 free(lf);
739 }
740
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200741 release_sample_expr(rule->arg.vars.expr);
742}
743
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200744/* This two function checks the variable name and replace the
745 * configuration string name by the global string name. its
746 * the same string, but the global pointer can be easy to
Willy Tarreaue352b9d2021-09-03 11:52:38 +0200747 * compare. They return non-zero on success, zero on failure.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200748 *
749 * The first function checks a sample-fetch and the second
750 * checks a converter.
751 */
752static int smp_check_var(struct arg *args, char **err)
753{
754 return vars_check_arg(&args[0], err);
755}
756
757static int conv_check_var(struct arg *args, struct sample_conv *conv,
758 const char *file, int line, char **err_msg)
759{
760 return vars_check_arg(&args[0], err_msg);
761}
762
763/* This function is a common parser for using variables. It understands
764 * the format:
765 *
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200766 * set-var-fmt(<variable-name>) <format-string>
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200767 * set-var(<variable-name>) <expression>
Willy Tarreau4b7531f2019-06-04 16:43:29 +0200768 * unset-var(<variable-name>)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200769 *
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200770 * It returns ACT_RET_PRS_ERR if fails and <err> is filled with an error
771 * message. Otherwise, it returns ACT_RET_PRS_OK and the variable <expr>
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100772 * is filled with the pointer to the expression to execute. The proxy is
773 * only used to retrieve the ->conf entries.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200774 */
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200775static enum act_parse_ret parse_store(const char **args, int *arg, struct proxy *px,
776 struct act_rule *rule, char **err)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200777{
778 const char *var_name = args[*arg-1];
779 int var_len;
Thierry FOURNIER48a9cd12015-07-28 19:00:28 +0200780 const char *kw_name;
Christopher Faulet7a06ffb2021-10-13 17:22:17 +0200781 int flags = 0, set_var = 0; /* 0=unset-var, 1=set-var, 2=set-var-fmt */
Willy Tarreaudf8eeb12021-09-08 11:07:32 +0200782 struct sample empty_smp = { };
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200783
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200784 if (strncmp(var_name, "set-var-fmt", 11) == 0) {
785 var_name += 11;
786 set_var = 2;
787 }
788 else if (strncmp(var_name, "set-var", 7) == 0) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100789 var_name += 7;
790 set_var = 1;
791 }
Willy Tarreau28192102021-09-02 18:46:22 +0200792 else if (strncmp(var_name, "unset-var", 9) == 0) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100793 var_name += 9;
794 set_var = 0;
795 }
796
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200797 if (*var_name != '(') {
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200798 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 +0100799 args[*arg-1]);
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200800 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200801 }
802 var_name++; /* jump the '(' */
803 var_len = strlen(var_name);
804 var_len--; /* remove the ')' */
805 if (var_name[var_len] != ')') {
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200806 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 +0100807 args[*arg-1]);
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200808 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200809 }
810
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200811 LIST_INIT(&rule->arg.vars.fmt);
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200812 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 +0200813 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200814
Willy Tarreaudf8eeb12021-09-08 11:07:32 +0200815 if (rule->arg.vars.scope == SCOPE_PROC &&
Willy Tarreau3a4bedc2021-08-31 08:51:02 +0200816 !var_set(rule->arg.vars.name_hash, rule->arg.vars.scope, &empty_smp, VF_CREATEONLY|VF_PERMANENT))
Willy Tarreaudf8eeb12021-09-08 11:07:32 +0200817 return 0;
818
Christopher Faulet85d79c92016-11-09 16:54:56 +0100819 /* There is no fetch method when variable is unset. Just set the right
820 * action and return. */
821 if (!set_var) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100822 rule->action = ACT_CUSTOM;
823 rule->action_ptr = action_clear;
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200824 rule->release_ptr = release_store_rule;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100825 return ACT_RET_PRS_OK;
826 }
827
Thierry FOURNIER48a9cd12015-07-28 19:00:28 +0200828 kw_name = args[*arg-1];
829
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200830 switch (rule->from) {
Jaroslaw Rzeszótkoc8637032021-11-02 16:56:05 +0100831 case ACT_F_TCP_REQ_CON:
832 flags = SMP_VAL_FE_CON_ACC;
833 px->conf.args.ctx = ARGC_TCO;
834 break;
Willy Tarreau843096d2021-09-02 19:03:07 +0200835 case ACT_F_TCP_REQ_SES:
836 flags = SMP_VAL_FE_SES_ACC;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200837 px->conf.args.ctx = ARGC_TSE;
Willy Tarreau843096d2021-09-02 19:03:07 +0200838 break;
839 case ACT_F_TCP_REQ_CNT:
Christopher Faulet7a06ffb2021-10-13 17:22:17 +0200840 if (px->cap & PR_CAP_FE)
841 flags |= SMP_VAL_FE_REQ_CNT;
842 if (px->cap & PR_CAP_BE)
843 flags |= SMP_VAL_BE_REQ_CNT;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200844 px->conf.args.ctx = ARGC_TRQ;
Willy Tarreau843096d2021-09-02 19:03:07 +0200845 break;
846 case ACT_F_TCP_RES_CNT:
Christopher Faulet7a06ffb2021-10-13 17:22:17 +0200847 if (px->cap & PR_CAP_FE)
848 flags |= SMP_VAL_FE_RES_CNT;
849 if (px->cap & PR_CAP_BE)
850 flags |= SMP_VAL_BE_RES_CNT;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200851 px->conf.args.ctx = ARGC_TRS;
Willy Tarreau843096d2021-09-02 19:03:07 +0200852 break;
853 case ACT_F_HTTP_REQ:
Christopher Faulet7a06ffb2021-10-13 17:22:17 +0200854 if (px->cap & PR_CAP_FE)
855 flags |= SMP_VAL_FE_HRQ_HDR;
856 if (px->cap & PR_CAP_BE)
857 flags |= SMP_VAL_BE_HRQ_HDR;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200858 px->conf.args.ctx = ARGC_HRQ;
Willy Tarreau843096d2021-09-02 19:03:07 +0200859 break;
860 case ACT_F_HTTP_RES:
Christopher Faulet7a06ffb2021-10-13 17:22:17 +0200861 if (px->cap & PR_CAP_FE)
862 flags |= SMP_VAL_FE_HRS_HDR;
863 if (px->cap & PR_CAP_BE)
864 flags |= SMP_VAL_BE_HRS_HDR;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200865 px->conf.args.ctx = ARGC_HRS;
Willy Tarreau843096d2021-09-02 19:03:07 +0200866 break;
867 case ACT_F_TCP_CHK:
868 flags = SMP_VAL_BE_CHK_RUL;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200869 px->conf.args.ctx = ARGC_TCK;
Willy Tarreau843096d2021-09-02 19:03:07 +0200870 break;
871 case ACT_F_CFG_PARSER:
872 flags = SMP_VAL_CFG_PARSER;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200873 px->conf.args.ctx = ARGC_CFG;
Willy Tarreau843096d2021-09-02 19:03:07 +0200874 break;
875 case ACT_F_CLI_PARSER:
876 flags = SMP_VAL_CLI_PARSER;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200877 px->conf.args.ctx = ARGC_CLI;
Willy Tarreau843096d2021-09-02 19:03:07 +0200878 break;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200879 default:
880 memprintf(err,
881 "internal error, unexpected rule->from=%d, please report this bug!",
882 rule->from);
Thierry FOURNIERafa80492015-08-19 09:04:15 +0200883 return ACT_RET_PRS_ERR;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200884 }
Willy Tarreau54b96d92021-09-02 19:46:08 +0200885
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200886 if (set_var == 2) { /* set-var-fmt */
887 if (!parse_logformat_string(args[*arg], px, &rule->arg.vars.fmt, 0, flags, err))
888 return ACT_RET_PRS_ERR;
Willy Tarreau54b96d92021-09-02 19:46:08 +0200889
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200890 (*arg)++;
891
892 /* for late error reporting */
893 free(px->conf.lfs_file);
894 px->conf.lfs_file = strdup(px->conf.args.file);
895 px->conf.lfs_line = px->conf.args.line;
896 } else {
897 /* set-var */
898 rule->arg.vars.expr = sample_parse_expr((char **)args, arg, px->conf.args.file,
Christopher Faulet6ff7de52021-10-13 15:18:36 +0200899 px->conf.args.line, err, &px->conf.args, NULL);
Willy Tarreau9a621ae2021-09-02 21:00:38 +0200900 if (!rule->arg.vars.expr)
901 return ACT_RET_PRS_ERR;
902
903 if (!(rule->arg.vars.expr->fetch->val & flags)) {
904 memprintf(err,
905 "fetch method '%s' extracts information from '%s', none of which is available here",
906 kw_name, sample_src_names(rule->arg.vars.expr->fetch->use));
907 free(rule->arg.vars.expr);
908 return ACT_RET_PRS_ERR;
909 }
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200910 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200911
Thierry FOURNIER42148732015-09-02 17:17:33 +0200912 rule->action = ACT_CUSTOM;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200913 rule->action_ptr = action_store;
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200914 rule->release_ptr = release_store_rule;
Thierry FOURNIERafa80492015-08-19 09:04:15 +0200915 return ACT_RET_PRS_OK;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200916}
917
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100918
919/* parses a global "set-var" directive. It will create a temporary rule and
920 * expression that are parsed, processed, and released on the fly so that we
921 * respect the real set-var syntax. These directives take the following format:
922 * set-var <name> <expression>
Willy Tarreau753d4db2021-09-03 09:02:47 +0200923 * set-var-fmt <name> <fmt>
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100924 * Note that parse_store() expects "set-var(name) <expression>" so we have to
925 * temporarily replace the keyword here.
926 */
927static int vars_parse_global_set_var(char **args, int section_type, struct proxy *curpx,
928 const struct proxy *defpx, const char *file, int line,
929 char **err)
930{
931 struct proxy px = {
Willy Tarreau9c204332021-09-03 08:19:43 +0200932 .id = "CFG",
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100933 .conf.args.file = file,
934 .conf.args.line = line,
935 };
936 struct act_rule rule = {
937 .arg.vars.scope = SCOPE_PROC,
938 .from = ACT_F_CFG_PARSER,
Willy Tarreauc9e48682021-10-11 09:13:07 +0200939 .conf.file = (char *)file,
940 .conf.line = line,
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100941 };
Willy Tarreau753d4db2021-09-03 09:02:47 +0200942 enum obj_type objt = OBJ_TYPE_NONE;
943 struct session *sess = NULL;
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100944 enum act_parse_ret p_ret;
945 char *old_arg1;
946 char *tmp_arg1;
947 int arg = 2; // variable name
948 int ret = -1;
Willy Tarreau753d4db2021-09-03 09:02:47 +0200949 int use_fmt = 0;
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100950
951 LIST_INIT(&px.conf.args.list);
952
Willy Tarreau753d4db2021-09-03 09:02:47 +0200953 use_fmt = strcmp(args[0], "set-var-fmt") == 0;
954
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100955 if (!*args[1] || !*args[2]) {
Willy Tarreau753d4db2021-09-03 09:02:47 +0200956 if (use_fmt)
957 memprintf(err, "'%s' requires a process-wide variable name ('proc.<name>') and a format string.", args[0]);
958 else
959 memprintf(err, "'%s' requires a process-wide variable name ('proc.<name>') and a sample expression.", args[0]);
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100960 goto end;
961 }
962
963 tmp_arg1 = NULL;
Willy Tarreau753d4db2021-09-03 09:02:47 +0200964 if (!memprintf(&tmp_arg1, "set-var%s(%s)", use_fmt ? "-fmt" : "", args[1]))
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100965 goto end;
966
967 /* parse_store() will always return a message in <err> on error */
968 old_arg1 = args[1]; args[1] = tmp_arg1;
969 p_ret = parse_store((const char **)args, &arg, &px, &rule, err);
970 free(args[1]); args[1] = old_arg1;
971
972 if (p_ret != ACT_RET_PRS_OK)
973 goto end;
974
975 if (rule.arg.vars.scope != SCOPE_PROC) {
976 memprintf(err, "'%s': cannot set variable '%s', only scope 'proc' is permitted in the global section.", args[0], args[1]);
977 goto end;
978 }
979
980 if (smp_resolve_args(&px, err) != 0) {
981 release_sample_expr(rule.arg.vars.expr);
982 indent_msg(err, 2);
983 goto end;
984 }
985
Willy Tarreau753d4db2021-09-03 09:02:47 +0200986 if (use_fmt && !(sess = session_new(&px, NULL, &objt))) {
987 release_sample_expr(rule.arg.vars.expr);
988 memprintf(err, "'%s': out of memory when trying to set variable '%s' in the global section.", args[0], args[1]);
989 goto end;
990 }
991
992 action_store(&rule, &px, sess, NULL, 0);
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100993 release_sample_expr(rule.arg.vars.expr);
Willy Tarreau753d4db2021-09-03 09:02:47 +0200994 if (sess)
995 session_free(sess);
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100996
997 ret = 0;
998 end:
999 return ret;
1000}
1001
Willy Tarreauc35eb382021-03-26 14:51:31 +01001002/* parse CLI's "get var <name>" */
1003static int vars_parse_cli_get_var(char **args, char *payload, struct appctx *appctx, void *private)
1004{
1005 struct vars *vars;
Willy Tarreau374edc72021-04-01 17:01:43 +02001006 struct sample smp = { };
Willy Tarreauc35eb382021-03-26 14:51:31 +01001007 int i;
1008
1009 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
1010 return 1;
1011
1012 if (!*args[2])
1013 return cli_err(appctx, "Missing process-wide variable identifier.\n");
1014
1015 vars = get_vars(NULL, NULL, SCOPE_PROC);
1016 if (!vars || vars->scope != SCOPE_PROC)
1017 return 0;
1018
Willy Tarreaue352b9d2021-09-03 11:52:38 +02001019 if (!vars_get_by_name(args[2], strlen(args[2]), &smp, NULL))
Willy Tarreauc35eb382021-03-26 14:51:31 +01001020 return cli_err(appctx, "Variable not found.\n");
1021
1022 /* the sample returned by vars_get_by_name() is allocated into a trash
1023 * chunk so we have no constraint to manipulate it.
1024 */
1025 chunk_printf(&trash, "%s: type=%s value=", args[2], smp_to_type[smp.data.type]);
1026
1027 if (!sample_casts[smp.data.type][SMP_T_STR] ||
1028 !sample_casts[smp.data.type][SMP_T_STR](&smp)) {
1029 chunk_appendf(&trash, "(undisplayable)");
1030 } else {
1031 /* Display the displayable chars*. */
1032 b_putchr(&trash, '<');
1033 for (i = 0; i < smp.data.u.str.data; i++) {
1034 if (isprint((unsigned char)smp.data.u.str.area[i]))
1035 b_putchr(&trash, smp.data.u.str.area[i]);
1036 else
1037 b_putchr(&trash, '.');
1038 }
1039 b_putchr(&trash, '>');
1040 b_putchr(&trash, 0);
1041 }
1042 return cli_msg(appctx, LOG_INFO, trash.area);
1043}
1044
Willy Tarreaue93bff42021-09-03 09:47:37 +02001045/* parse CLI's "set var <name>". It accepts:
1046 * - set var <name> <expression>
1047 * - set var <name> expr <expression>
1048 * - set var <name> fmt <format>
1049 */
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001050static int vars_parse_cli_set_var(char **args, char *payload, struct appctx *appctx, void *private)
1051{
1052 struct proxy px = {
1053 .id = "CLI",
1054 .conf.args.file = "CLI",
1055 .conf.args.line = 0,
1056 };
1057 struct act_rule rule = {
1058 .arg.vars.scope = SCOPE_PROC,
1059 .from = ACT_F_CLI_PARSER,
Willy Tarreauc9e48682021-10-11 09:13:07 +02001060 .conf.file = "CLI",
1061 .conf.line = 0,
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001062 };
Willy Tarreaue93bff42021-09-03 09:47:37 +02001063 enum obj_type objt = OBJ_TYPE_NONE;
1064 struct session *sess = NULL;
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001065 enum act_parse_ret p_ret;
Willy Tarreaue93bff42021-09-03 09:47:37 +02001066 const char *tmp_args[3];
1067 int tmp_arg;
1068 char *tmp_act;
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001069 char *err = NULL;
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001070 int nberr;
Willy Tarreaue93bff42021-09-03 09:47:37 +02001071 int use_fmt = 0;
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001072
1073 LIST_INIT(&px.conf.args.list);
1074
1075 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
1076 return 1;
1077
Willy Tarreaue93bff42021-09-03 09:47:37 +02001078 if (!*args[2])
1079 return cli_err(appctx, "Missing process-wide variable identifier.\n");
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001080
Willy Tarreaue93bff42021-09-03 09:47:37 +02001081 if (!*args[3])
1082 return cli_err(appctx, "Missing either 'expr', 'fmt' or expression.\n");
1083
1084 if (*args[4]) {
1085 /* this is the long format */
1086 if (strcmp(args[3], "fmt") == 0)
1087 use_fmt = 1;
1088 else if (strcmp(args[3], "expr") != 0) {
1089 memprintf(&err, "'%s %s': arg type must be either 'expr' or 'fmt' but got '%s'.", args[0], args[1], args[3]);
1090 goto fail;
1091 }
1092 }
1093
1094 tmp_act = NULL;
1095 if (!memprintf(&tmp_act, "set-var%s(%s)", use_fmt ? "-fmt" : "", args[2])) {
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001096 memprintf(&err, "memory allocation error.");
1097 goto fail;
1098 }
1099
1100 /* parse_store() will always return a message in <err> on error */
Willy Tarreaue93bff42021-09-03 09:47:37 +02001101 tmp_args[0] = tmp_act;
1102 tmp_args[1] = (*args[4]) ? args[4] : args[3];
1103 tmp_args[2] = "";
1104 tmp_arg = 1; // must point to the first arg after the action
1105 p_ret = parse_store(tmp_args, &tmp_arg, &px, &rule, &err);
1106 free(tmp_act);
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001107
1108 if (p_ret != ACT_RET_PRS_OK)
1109 goto fail;
1110
1111 if (rule.arg.vars.scope != SCOPE_PROC) {
Willy Tarreauc767eeb2021-09-03 10:23:26 +02001112 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 +01001113 goto fail;
1114 }
1115
1116 err = NULL;
1117 nberr = smp_resolve_args(&px, &err);
1118 if (nberr) {
1119 release_sample_expr(rule.arg.vars.expr);
1120 indent_msg(&err, 2);
1121 goto fail;
1122 }
1123
Willy Tarreaue93bff42021-09-03 09:47:37 +02001124 if (use_fmt && !(sess = session_new(&px, NULL, &objt))) {
1125 release_sample_expr(rule.arg.vars.expr);
1126 memprintf(&err, "memory allocation error.");
1127 goto fail;
1128 }
1129
1130 action_store(&rule, &px, sess, NULL, 0);
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001131 release_sample_expr(rule.arg.vars.expr);
Willy Tarreaue93bff42021-09-03 09:47:37 +02001132 if (sess)
1133 session_free(sess);
1134
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +01001135 appctx->st0 = CLI_ST_PROMPT;
1136 return 0;
1137 fail:
1138 return cli_dynerr(appctx, err);
1139}
1140
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001141static int vars_max_size(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001142 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001143 char **err, unsigned int *limit)
1144{
1145 char *error;
1146
1147 *limit = strtol(args[1], &error, 10);
1148 if (*error != 0) {
1149 memprintf(err, "%s: '%s' is an invalid size", args[0], args[1]);
1150 return -1;
1151 }
1152 return 0;
1153}
1154
1155static int vars_max_size_global(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001156 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001157 char **err)
1158{
1159 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_global_limit);
1160}
1161
Christopher Fauletff2613e2016-11-09 11:36:17 +01001162static int vars_max_size_proc(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001163 const struct proxy *defpx, const char *file, int line,
Christopher Fauletff2613e2016-11-09 11:36:17 +01001164 char **err)
1165{
1166 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_proc_limit);
1167}
1168
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001169static int vars_max_size_sess(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001170 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001171 char **err)
1172{
1173 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_sess_limit);
1174}
1175
1176static int vars_max_size_txn(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001177 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001178 char **err)
1179{
1180 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_txn_limit);
1181}
1182
1183static int vars_max_size_reqres(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001184 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001185 char **err)
1186{
1187 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_reqres_limit);
1188}
1189
Gaetan Rivet13a50432020-02-21 18:13:44 +01001190static int vars_max_size_check(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001191 const struct proxy *defpx, const char *file, int line,
Gaetan Rivet13a50432020-02-21 18:13:44 +01001192 char **err)
1193{
1194 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_check_limit);
1195}
1196
Willy Tarreau2c897d92021-08-31 08:48:55 +02001197/* early boot initialization */
1198static void vars_init()
1199{
1200 var_name_hash_seed = ha_random64();
1201}
1202
1203INITCALL0(STG_PREPARE, vars_init);
1204
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001205static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
1206
Willy Tarreau44c5ff62021-11-02 17:08:15 +01001207 { "var", smp_fetch_var, ARG2(1,STR,STR), smp_check_var, SMP_T_ANY, SMP_USE_CONST },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001208 { /* END */ },
1209}};
1210
Willy Tarreau0108d902018-11-25 19:14:37 +01001211INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);
1212
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001213static struct sample_conv_kw_list sample_conv_kws = {ILH, {
Christopher Faulet85d79c92016-11-09 16:54:56 +01001214 { "set-var", smp_conv_store, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
1215 { "unset-var", smp_conv_clear, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001216 { /* END */ },
1217}};
1218
Willy Tarreau0108d902018-11-25 19:14:37 +01001219INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws);
1220
Jaroslaw Rzeszótkoc8637032021-11-02 16:56:05 +01001221static struct action_kw_list tcp_req_conn_kws = { { }, {
1222 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
1223 { "set-var", parse_store, KWF_MATCH_PREFIX },
1224 { "unset-var", parse_store, KWF_MATCH_PREFIX },
1225 { /* END */ }
1226}};
1227
1228INITCALL1(STG_REGISTER, tcp_req_conn_keywords_register, &tcp_req_conn_kws);
1229
Willy Tarreau620408f2016-10-21 16:37:51 +02001230static struct action_kw_list tcp_req_sess_kws = { { }, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001231 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001232 { "set-var", parse_store, KWF_MATCH_PREFIX },
1233 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Willy Tarreau620408f2016-10-21 16:37:51 +02001234 { /* END */ }
1235}};
1236
Willy Tarreau0108d902018-11-25 19:14:37 +01001237INITCALL1(STG_REGISTER, tcp_req_sess_keywords_register, &tcp_req_sess_kws);
1238
Willy Tarreau620408f2016-10-21 16:37:51 +02001239static struct action_kw_list tcp_req_cont_kws = { { }, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001240 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001241 { "set-var", parse_store, KWF_MATCH_PREFIX },
1242 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001243 { /* END */ }
1244}};
1245
Willy Tarreau0108d902018-11-25 19:14:37 +01001246INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_cont_kws);
1247
Thierry FOURNIER36481b82015-08-19 09:01:53 +02001248static struct action_kw_list tcp_res_kws = { { }, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001249 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001250 { "set-var", parse_store, KWF_MATCH_PREFIX },
1251 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001252 { /* END */ }
1253}};
1254
Willy Tarreau0108d902018-11-25 19:14:37 +01001255INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_kws);
1256
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001257static struct action_kw_list tcp_check_kws = {ILH, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001258 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001259 { "set-var", parse_store, KWF_MATCH_PREFIX },
1260 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001261 { /* END */ }
1262}};
1263
1264INITCALL1(STG_REGISTER, tcp_check_keywords_register, &tcp_check_kws);
1265
Thierry FOURNIER36481b82015-08-19 09:01:53 +02001266static struct action_kw_list http_req_kws = { { }, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001267 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001268 { "set-var", parse_store, KWF_MATCH_PREFIX },
1269 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001270 { /* END */ }
1271}};
1272
Willy Tarreau0108d902018-11-25 19:14:37 +01001273INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_kws);
1274
Thierry FOURNIER36481b82015-08-19 09:01:53 +02001275static struct action_kw_list http_res_kws = { { }, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001276 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001277 { "set-var", parse_store, KWF_MATCH_PREFIX },
1278 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001279 { /* END */ }
1280}};
1281
Willy Tarreau0108d902018-11-25 19:14:37 +01001282INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_kws);
1283
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01001284static struct action_kw_list http_after_res_kws = { { }, {
Willy Tarreau9a621ae2021-09-02 21:00:38 +02001285 { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001286 { "set-var", parse_store, KWF_MATCH_PREFIX },
1287 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01001288 { /* END */ }
1289}};
1290
1291INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_kws);
1292
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001293static struct cfg_kw_list cfg_kws = {{ },{
Willy Tarreau13d2ba22021-03-26 11:38:08 +01001294 { CFG_GLOBAL, "set-var", vars_parse_global_set_var },
Willy Tarreau753d4db2021-09-03 09:02:47 +02001295 { CFG_GLOBAL, "set-var-fmt", vars_parse_global_set_var },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001296 { CFG_GLOBAL, "tune.vars.global-max-size", vars_max_size_global },
Christopher Fauletff2613e2016-11-09 11:36:17 +01001297 { CFG_GLOBAL, "tune.vars.proc-max-size", vars_max_size_proc },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001298 { CFG_GLOBAL, "tune.vars.sess-max-size", vars_max_size_sess },
1299 { CFG_GLOBAL, "tune.vars.txn-max-size", vars_max_size_txn },
1300 { CFG_GLOBAL, "tune.vars.reqres-max-size", vars_max_size_reqres },
Gaetan Rivet13a50432020-02-21 18:13:44 +01001301 { CFG_GLOBAL, "tune.vars.check-max-size", vars_max_size_check },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001302 { /* END */ }
1303}};
1304
Willy Tarreau0108d902018-11-25 19:14:37 +01001305INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Willy Tarreauc35eb382021-03-26 14:51:31 +01001306
1307
1308/* register cli keywords */
1309static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02001310 { { "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 +02001311 { { "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 +01001312 { { NULL }, NULL, NULL, NULL }
1313}};
1314INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);