blob: 572b5f56fe3c35ecdb075da07cdf5807cdbe3b81 [file] [log] [blame]
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001#include <ctype.h>
2
Willy Tarreau4c7e4b72020-05-27 12:58:42 +02003#include <haproxy/api.h>
Willy Tarreaub2551052020-06-09 09:07:15 +02004#include <haproxy/arg.h>
Willy Tarreauc35eb382021-03-26 14:51:31 +01005#include <haproxy/buf.h>
Willy Tarreau6be78492020-06-05 00:00:29 +02006#include <haproxy/cfgparse.h>
Willy Tarreau4aa573d2020-06-04 18:21:56 +02007#include <haproxy/check.h>
Willy Tarreauc35eb382021-03-26 14:51:31 +01008#include <haproxy/cli.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +02009#include <haproxy/global.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020010#include <haproxy/http.h>
Willy Tarreauc761f842020-06-04 11:40:28 +020011#include <haproxy/http_rules.h>
Willy Tarreau853b2972020-05-27 18:01:47 +020012#include <haproxy/list.h>
Willy Tarreau36979d92020-06-05 17:27:29 +020013#include <haproxy/log.h>
Willy Tarreaue6ce10b2020-06-04 15:33:47 +020014#include <haproxy/sample.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020015#include <haproxy/stream-t.h>
Willy Tarreau8b550af2020-06-04 17:42:48 +020016#include <haproxy/tcp_rules.h>
Willy Tarreaub2551052020-06-09 09:07:15 +020017#include <haproxy/tcpcheck.h>
Willy Tarreaua1718922020-06-04 16:25:31 +020018#include <haproxy/vars.h>
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020019
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020020
21/* This contains a pool of struct vars */
Willy Tarreau8ceae722018-11-26 11:58:30 +010022DECLARE_STATIC_POOL(var_pool, "vars", sizeof(struct var));
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020023
Willy Tarreaucfc4f242021-05-08 11:41:28 +020024/* list of variables for the process scope. */
25struct vars proc_vars THREAD_ALIGNED(64);
26
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020027/* This array contain all the names of all the HAProxy vars.
28 * This permits to identify two variables name with
29 * only one pointer. It permits to not using strdup() for
30 * each variable name used during the runtime.
31 */
32static char **var_names = NULL;
33static int var_names_nb = 0;
34
35/* This array of int contains the system limits per context. */
36static unsigned int var_global_limit = 0;
37static unsigned int var_global_size = 0;
Christopher Fauletff2613e2016-11-09 11:36:17 +010038static unsigned int var_proc_limit = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020039static unsigned int var_sess_limit = 0;
40static unsigned int var_txn_limit = 0;
41static unsigned int var_reqres_limit = 0;
Gaetan Rivet13a50432020-02-21 18:13:44 +010042static unsigned int var_check_limit = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020043
Willy Tarreau86abe442018-11-25 20:12:18 +010044__decl_rwlock(var_names_rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +020045
Willy Tarreauf37b1402019-06-04 16:27:36 +020046/* returns the struct vars pointer for a session, stream and scope, or NULL if
47 * it does not exist.
48 */
49static inline struct vars *get_vars(struct session *sess, struct stream *strm, enum vars_scope scope)
50{
51 switch (scope) {
52 case SCOPE_PROC:
Willy Tarreaucfc4f242021-05-08 11:41:28 +020053 return &proc_vars;
Willy Tarreauf37b1402019-06-04 16:27:36 +020054 case SCOPE_SESS:
Willy Tarreaua07d61b2021-03-26 11:27:59 +010055 return sess ? &sess->vars : NULL;
Gaetan Rivet13a50432020-02-21 18:13:44 +010056 case SCOPE_CHECK: {
Christopher Faulet0fca7ed2020-04-21 11:53:32 +020057 struct check *check = objt_check(sess->origin);
Gaetan Rivet13a50432020-02-21 18:13:44 +010058
Christopher Faulet0fca7ed2020-04-21 11:53:32 +020059 return check ? &check->vars : NULL;
Gaetan Rivet13a50432020-02-21 18:13:44 +010060 }
Willy Tarreauf37b1402019-06-04 16:27:36 +020061 case SCOPE_TXN:
62 return strm ? &strm->vars_txn : NULL;
63 case SCOPE_REQ:
64 case SCOPE_RES:
65 default:
66 return strm ? &strm->vars_reqres : NULL;
67 }
68}
69
Willy Tarreau72330982015-06-19 11:21:56 +020070/* This function adds or remove memory size from the accounting. The inner
71 * pointers may be null when setting the outer ones only.
72 */
Miroslav Zagorac6deab792020-12-09 16:34:29 +010073void var_accounting_diff(struct vars *vars, struct session *sess, struct stream *strm, int size)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020074{
75 switch (vars->scope) {
76 case SCOPE_REQ:
77 case SCOPE_RES:
Willy Tarreauf37b1402019-06-04 16:27:36 +020078 if (strm)
79 _HA_ATOMIC_ADD(&strm->vars_reqres.size, size);
Willy Tarreau6204cd92016-03-10 16:33:04 +010080 /* fall through */
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020081 case SCOPE_TXN:
Willy Tarreauf37b1402019-06-04 16:27:36 +020082 if (strm)
83 _HA_ATOMIC_ADD(&strm->vars_txn.size, size);
Gaetan Rivet13a50432020-02-21 18:13:44 +010084 goto scope_sess;
85 case SCOPE_CHECK: {
Christopher Faulet0fca7ed2020-04-21 11:53:32 +020086 struct check *check = objt_check(sess->origin);
Gaetan Rivet13a50432020-02-21 18:13:44 +010087
Christopher Faulet0fca7ed2020-04-21 11:53:32 +020088 if (check)
89 _HA_ATOMIC_ADD(&check->vars.size, size);
Gaetan Rivet13a50432020-02-21 18:13:44 +010090 }
Willy Tarreau6204cd92016-03-10 16:33:04 +010091 /* fall through */
Gaetan Rivet13a50432020-02-21 18:13:44 +010092scope_sess:
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020093 case SCOPE_SESS:
Olivier Houchard25ad13f2019-03-08 18:55:38 +010094 _HA_ATOMIC_ADD(&sess->vars.size, size);
Christopher Fauletff2613e2016-11-09 11:36:17 +010095 /* fall through */
96 case SCOPE_PROC:
Willy Tarreaucfc4f242021-05-08 11:41:28 +020097 _HA_ATOMIC_ADD(&proc_vars.size, size);
Olivier Houchard25ad13f2019-03-08 18:55:38 +010098 _HA_ATOMIC_ADD(&var_global_size, size);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020099 }
100}
101
102/* This function returns 1 if the <size> is available in the var
Joseph Herlant07676892018-11-15 09:19:50 -0800103 * pool <vars>, otherwise returns 0. If the space is available,
Willy Tarreau72330982015-06-19 11:21:56 +0200104 * the size is reserved. The inner pointers may be null when setting
Willy Tarreau6204cd92016-03-10 16:33:04 +0100105 * the outer ones only. The accounting uses either <sess> or <strm>
106 * depending on the scope. <strm> may be NULL when no stream is known
107 * and only the session exists (eg: tcp-request connection).
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200108 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100109static int var_accounting_add(struct vars *vars, struct session *sess, struct stream *strm, int size)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200110{
111 switch (vars->scope) {
112 case SCOPE_REQ:
113 case SCOPE_RES:
Willy Tarreauf37b1402019-06-04 16:27:36 +0200114 if (var_reqres_limit && strm && strm->vars_reqres.size + size > var_reqres_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200115 return 0;
Willy Tarreau6204cd92016-03-10 16:33:04 +0100116 /* fall through */
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200117 case SCOPE_TXN:
Willy Tarreauf37b1402019-06-04 16:27:36 +0200118 if (var_txn_limit && strm && strm->vars_txn.size + size > var_txn_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200119 return 0;
Gaetan Rivet13a50432020-02-21 18:13:44 +0100120 goto scope_sess;
121 case SCOPE_CHECK: {
Christopher Faulet0fca7ed2020-04-21 11:53:32 +0200122 struct check *check = objt_check(sess->origin);
Gaetan Rivet13a50432020-02-21 18:13:44 +0100123
Christopher Faulet0fca7ed2020-04-21 11:53:32 +0200124 if (var_check_limit && check && check->vars.size + size > var_check_limit)
Gaetan Rivet13a50432020-02-21 18:13:44 +0100125 return 0;
126 }
Willy Tarreau6204cd92016-03-10 16:33:04 +0100127 /* fall through */
Gaetan Rivet13a50432020-02-21 18:13:44 +0100128scope_sess:
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200129 case SCOPE_SESS:
Willy Tarreau6204cd92016-03-10 16:33:04 +0100130 if (var_sess_limit && sess->vars.size + size > var_sess_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200131 return 0;
Christopher Fauletff2613e2016-11-09 11:36:17 +0100132 /* fall through */
133 case SCOPE_PROC:
Willy Tarreaucfc4f242021-05-08 11:41:28 +0200134 if (var_proc_limit && proc_vars.size + size > var_proc_limit)
Christopher Fauletff2613e2016-11-09 11:36:17 +0100135 return 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200136 if (var_global_limit && var_global_size + size > var_global_limit)
137 return 0;
138 }
Willy Tarreau6204cd92016-03-10 16:33:04 +0100139 var_accounting_diff(vars, sess, strm, size);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200140 return 1;
141}
142
Christopher Faulet85d79c92016-11-09 16:54:56 +0100143/* This fnuction remove a variable from the list and free memory it used */
144unsigned int var_clear(struct var *var)
145{
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 Tarreau2b718102021-04-21 07:32:39 +0200156 LIST_DELETE(&var->l);
Willy Tarreaubafbe012017-11-24 17:34:44 +0100157 pool_free(var_pool, var);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100158 size += sizeof(struct var);
159 return size;
160}
161
Joseph Herlant07676892018-11-15 09:19:50 -0800162/* This function free all the memory used by all the variables
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200163 * in the list.
164 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100165void vars_prune(struct vars *vars, struct session *sess, struct stream *strm)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200166{
167 struct var *var, *tmp;
Willy Tarreau72330982015-06-19 11:21:56 +0200168 unsigned int size = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200169
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100170 HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200171 list_for_each_entry_safe(var, tmp, &vars->head, l) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100172 size += var_clear(var);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200173 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100174 HA_RWLOCK_WRUNLOCK(VARS_LOCK, &vars->rwlock);
Willy Tarreau6204cd92016-03-10 16:33:04 +0100175 var_accounting_diff(vars, sess, strm, -size);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200176}
177
Willy Tarreauebcd4842015-06-19 11:59:02 +0200178/* This function frees all the memory used by all the session variables in the
179 * list starting at <vars>.
180 */
181void vars_prune_per_sess(struct vars *vars)
182{
183 struct var *var, *tmp;
184 unsigned int size = 0;
185
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100186 HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock);
Willy Tarreauebcd4842015-06-19 11:59:02 +0200187 list_for_each_entry_safe(var, tmp, &vars->head, l) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100188 size += var_clear(var);
Willy Tarreauebcd4842015-06-19 11:59:02 +0200189 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100190 HA_RWLOCK_WRUNLOCK(VARS_LOCK, &vars->rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200191
Olivier Houchard25ad13f2019-03-08 18:55:38 +0100192 _HA_ATOMIC_SUB(&vars->size, size);
Willy Tarreaucfc4f242021-05-08 11:41:28 +0200193 _HA_ATOMIC_SUB(&proc_vars.size, size);
Olivier Houchard25ad13f2019-03-08 18:55:38 +0100194 _HA_ATOMIC_SUB(&var_global_size, size);
Willy Tarreauebcd4842015-06-19 11:59:02 +0200195}
196
Ilya Shipitsin46a030c2020-07-05 16:36:08 +0500197/* This function init a list of variables. */
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200198void vars_init(struct vars *vars, enum vars_scope scope)
199{
200 LIST_INIT(&vars->head);
201 vars->scope = scope;
202 vars->size = 0;
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100203 HA_RWLOCK_INIT(&vars->rwlock);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200204}
205
206/* This function declares a new variable name. It returns a pointer
207 * on the string identifying the name. This function assures that
208 * the same name exists only once.
209 *
210 * This function check if the variable name is acceptable.
211 *
212 * The function returns NULL if an error occurs, and <err> is filled.
213 * In this case, the HAProxy must be stopped because the structs are
214 * left inconsistent. Otherwise, it returns the pointer on the global
215 * name.
216 */
Christopher Faulet09c9df22016-10-31 11:05:37 +0100217static char *register_name(const char *name, int len, enum vars_scope *scope,
218 int alloc, char **err)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200219{
220 int i;
Hubert Verstraete831962e2016-06-28 22:44:26 +0200221 char **var_names2;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200222 const char *tmp;
Christopher Faulete95f2c32017-07-24 16:30:34 +0200223 char *res = NULL;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200224
225 /* Check length. */
226 if (len == 0) {
227 memprintf(err, "Empty variable name cannot be accepted");
Christopher Fauleteb3e2762017-12-08 09:17:39 +0100228 return res;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200229 }
230
231 /* Check scope. */
Christopher Fauletff2613e2016-11-09 11:36:17 +0100232 if (len > 5 && strncmp(name, "proc.", 5) == 0) {
233 name += 5;
234 len -= 5;
235 *scope = SCOPE_PROC;
236 }
237 else if (len > 5 && strncmp(name, "sess.", 5) == 0) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200238 name += 5;
239 len -= 5;
240 *scope = SCOPE_SESS;
241 }
242 else if (len > 4 && strncmp(name, "txn.", 4) == 0) {
243 name += 4;
244 len -= 4;
245 *scope = SCOPE_TXN;
246 }
247 else if (len > 4 && strncmp(name, "req.", 4) == 0) {
248 name += 4;
249 len -= 4;
250 *scope = SCOPE_REQ;
251 }
252 else if (len > 4 && strncmp(name, "res.", 4) == 0) {
253 name += 4;
254 len -= 4;
255 *scope = SCOPE_RES;
256 }
Gaetan Rivet13a50432020-02-21 18:13:44 +0100257 else if (len > 6 && strncmp(name, "check.", 6) == 0) {
258 name += 6;
259 len -= 6;
260 *scope = SCOPE_CHECK;
261 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200262 else {
263 memprintf(err, "invalid variable name '%s'. A variable name must be start by its scope. "
Gaetan Rivet13a50432020-02-21 18:13:44 +0100264 "The scope can be 'proc', 'sess', 'txn', 'req', 'res' or 'check'", name);
Christopher Fauleteb3e2762017-12-08 09:17:39 +0100265 return res;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200266 }
267
Christopher Faulete95f2c32017-07-24 16:30:34 +0200268 if (alloc)
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100269 HA_RWLOCK_WRLOCK(VARS_LOCK, &var_names_rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200270 else
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100271 HA_RWLOCK_RDLOCK(VARS_LOCK, &var_names_rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200272
273
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200274 /* Look for existing variable name. */
275 for (i = 0; i < var_names_nb; i++)
Christopher Faulete95f2c32017-07-24 16:30:34 +0200276 if (strncmp(var_names[i], name, len) == 0 && var_names[i][len] == '\0') {
277 res = var_names[i];
278 goto end;
279 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200280
Christopher Faulete95f2c32017-07-24 16:30:34 +0200281 if (!alloc) {
282 res = NULL;
283 goto end;
284 }
Christopher Faulet09c9df22016-10-31 11:05:37 +0100285
Hubert Verstraete831962e2016-06-28 22:44:26 +0200286 /* Store variable name. If realloc fails, var_names remains valid */
287 var_names2 = realloc(var_names, (var_names_nb + 1) * sizeof(*var_names));
288 if (!var_names2) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200289 memprintf(err, "out of memory error");
Christopher Faulete95f2c32017-07-24 16:30:34 +0200290 res = NULL;
291 goto end;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200292 }
Hubert Verstraete831962e2016-06-28 22:44:26 +0200293 var_names_nb++;
294 var_names = var_names2;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200295 var_names[var_names_nb - 1] = malloc(len + 1);
296 if (!var_names[var_names_nb - 1]) {
297 memprintf(err, "out of memory error");
Christopher Faulete95f2c32017-07-24 16:30:34 +0200298 res = NULL;
299 goto end;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200300 }
301 memcpy(var_names[var_names_nb - 1], name, len);
302 var_names[var_names_nb - 1][len] = '\0';
303
304 /* Check variable name syntax. */
305 tmp = var_names[var_names_nb - 1];
306 while (*tmp) {
Willy Tarreau90807112020-02-25 08:16:33 +0100307 if (!isalnum((unsigned char)*tmp) && *tmp != '_' && *tmp != '.') {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200308 memprintf(err, "invalid syntax at char '%s'", tmp);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200309 res = NULL;
310 goto end;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200311 }
312 tmp++;
313 }
Christopher Faulete95f2c32017-07-24 16:30:34 +0200314 res = var_names[var_names_nb - 1];
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200315
Christopher Faulete95f2c32017-07-24 16:30:34 +0200316 end:
317 if (alloc)
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100318 HA_RWLOCK_WRUNLOCK(VARS_LOCK, &var_names_rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200319 else
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100320 HA_RWLOCK_RDUNLOCK(VARS_LOCK, &var_names_rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200321
322 return res;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200323}
324
325/* This function returns an existing variable or returns NULL. */
326static inline struct var *var_get(struct vars *vars, const char *name)
327{
328 struct var *var;
329
330 list_for_each_entry(var, &vars->head, l)
331 if (var->name == name)
332 return var;
333 return NULL;
334}
335
336/* Returns 0 if fails, else returns 1. */
337static int smp_fetch_var(const struct arg *args, struct sample *smp, const char *kw, void *private)
338{
339 const struct var_desc *var_desc = &args[0].data.var;
Christopher Faulete95f2c32017-07-24 16:30:34 +0200340
Dragan Dosenec0a6042021-02-22 17:39:02 +0100341 return vars_get_by_desc(var_desc, smp);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200342}
343
344/* This function search in the <head> a variable with the same
345 * pointer value that the <name>. If the variable doesn't exists,
346 * create it. The function stores a copy of smp> if the variable.
347 * It returns 0 if fails, else returns 1.
348 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100349static int sample_store(struct vars *vars, const char *name, struct sample *smp)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200350{
351 struct var *var;
352
353 /* Look for existing variable name. */
354 var = var_get(vars, name);
355
356 if (var) {
357 /* free its used memory. */
358 if (var->data.type == SMP_T_STR ||
359 var->data.type == SMP_T_BIN) {
Willy Tarreau5b52b002021-02-26 21:19:53 +0100360 ha_free(&var->data.u.str.area);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200361 var_accounting_diff(vars, smp->sess, smp->strm,
362 -var->data.u.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200363 }
Christopher Fauletd02210c2017-07-24 16:24:39 +0200364 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
Willy Tarreau5b52b002021-02-26 21:19:53 +0100365 ha_free(&var->data.u.meth.str.area);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200366 var_accounting_diff(vars, smp->sess, smp->strm,
367 -var->data.u.meth.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200368 }
369 } else {
370
Joseph Herlant07676892018-11-15 09:19:50 -0800371 /* Check memory available. */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100372 if (!var_accounting_add(vars, smp->sess, smp->strm, sizeof(struct var)))
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200373 return 0;
374
375 /* Create new entry. */
Willy Tarreaubafbe012017-11-24 17:34:44 +0100376 var = pool_alloc(var_pool);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200377 if (!var)
378 return 0;
Willy Tarreau2b718102021-04-21 07:32:39 +0200379 LIST_APPEND(&vars->head, &var->l);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200380 var->name = name;
381 }
382
383 /* Set type. */
Thierry FOURNIER8c542ca2015-08-19 09:00:18 +0200384 var->data.type = smp->data.type;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200385
386 /* Copy data. If the data needs memory, the function can fail. */
387 switch (var->data.type) {
388 case SMP_T_BOOL:
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200389 case SMP_T_SINT:
Thierry FOURNIER136f9d32015-08-19 09:07:19 +0200390 var->data.u.sint = smp->data.u.sint;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200391 break;
392 case SMP_T_IPV4:
Thierry FOURNIER136f9d32015-08-19 09:07:19 +0200393 var->data.u.ipv4 = smp->data.u.ipv4;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200394 break;
395 case SMP_T_IPV6:
Thierry FOURNIER136f9d32015-08-19 09:07:19 +0200396 var->data.u.ipv6 = smp->data.u.ipv6;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200397 break;
398 case SMP_T_STR:
399 case SMP_T_BIN:
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200400 if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.str.data)) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200401 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
402 return 0;
403 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200404 var->data.u.str.area = malloc(smp->data.u.str.data);
405 if (!var->data.u.str.area) {
406 var_accounting_diff(vars, smp->sess, smp->strm,
407 -smp->data.u.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200408 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
409 return 0;
410 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200411 var->data.u.str.data = smp->data.u.str.data;
412 memcpy(var->data.u.str.area, smp->data.u.str.area,
413 var->data.u.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200414 break;
415 case SMP_T_METH:
Christopher Fauletd02210c2017-07-24 16:24:39 +0200416 var->data.u.meth.meth = smp->data.u.meth.meth;
417 if (smp->data.u.meth.meth != HTTP_METH_OTHER)
418 break;
419
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200420 if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.meth.str.data)) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200421 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
422 return 0;
423 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200424 var->data.u.meth.str.area = malloc(smp->data.u.meth.str.data);
425 if (!var->data.u.meth.str.area) {
426 var_accounting_diff(vars, smp->sess, smp->strm,
427 -smp->data.u.meth.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200428 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
429 return 0;
430 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200431 var->data.u.meth.str.data = smp->data.u.meth.str.data;
432 var->data.u.meth.str.size = smp->data.u.meth.str.data;
433 memcpy(var->data.u.meth.str.area, smp->data.u.meth.str.area,
434 var->data.u.meth.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200435 break;
436 }
437 return 1;
438}
439
Willy Tarreau620408f2016-10-21 16:37:51 +0200440/* Returns 0 if fails, else returns 1. Note that stream may be null for SCOPE_SESS. */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100441static inline int sample_store_stream(const char *name, enum vars_scope scope, struct sample *smp)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200442{
443 struct vars *vars;
Christopher Faulete95f2c32017-07-24 16:30:34 +0200444 int ret;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200445
Willy Tarreauf37b1402019-06-04 16:27:36 +0200446 vars = get_vars(smp->sess, smp->strm, scope);
447 if (!vars || vars->scope != scope)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200448 return 0;
Christopher Faulete95f2c32017-07-24 16:30:34 +0200449
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100450 HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200451 ret = sample_store(vars, name, smp);
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100452 HA_RWLOCK_WRUNLOCK(VARS_LOCK, &vars->rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200453 return ret;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200454}
455
Christopher Faulet85d79c92016-11-09 16:54:56 +0100456/* Returns 0 if fails, else returns 1. Note that stream may be null for SCOPE_SESS. */
457static inline int sample_clear_stream(const char *name, enum vars_scope scope, struct sample *smp)
458{
459 struct vars *vars;
460 struct var *var;
461 unsigned int size = 0;
462
Willy Tarreauf37b1402019-06-04 16:27:36 +0200463 vars = get_vars(smp->sess, smp->strm, scope);
464 if (!vars || vars->scope != scope)
Christopher Faulet85d79c92016-11-09 16:54:56 +0100465 return 0;
466
467 /* Look for existing variable name. */
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100468 HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100469 var = var_get(vars, name);
470 if (var) {
471 size = var_clear(var);
472 var_accounting_diff(vars, smp->sess, smp->strm, -size);
473 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100474 HA_RWLOCK_WRUNLOCK(VARS_LOCK, &vars->rwlock);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100475 return 1;
476}
477
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200478/* Returns 0 if fails, else returns 1. */
479static int smp_conv_store(const struct arg *args, struct sample *smp, void *private)
480{
Christopher Faulet0099a8c2016-11-09 16:15:32 +0100481 return sample_store_stream(args[0].data.var.name, args[0].data.var.scope, smp);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200482}
483
Christopher Faulet85d79c92016-11-09 16:54:56 +0100484/* Returns 0 if fails, else returns 1. */
485static int smp_conv_clear(const struct arg *args, struct sample *smp, void *private)
486{
487 return sample_clear_stream(args[0].data.var.name, args[0].data.var.scope, smp);
488}
489
Joseph Herlant07676892018-11-15 09:19:50 -0800490/* This functions check an argument entry and fill it with a variable
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200491 * type. The argumen must be a string. If the variable lookup fails,
Joseph Herlant07676892018-11-15 09:19:50 -0800492 * the function returns 0 and fill <err>, otherwise it returns 1.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200493 */
494int vars_check_arg(struct arg *arg, char **err)
495{
496 char *name;
497 enum vars_scope scope;
498
499 /* Check arg type. */
500 if (arg->type != ARGT_STR) {
501 memprintf(err, "unexpected argument type");
502 return 0;
503 }
504
505 /* Register new variable name. */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200506 name = register_name(arg->data.str.area, arg->data.str.data, &scope,
507 1,
508 err);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200509 if (!name)
510 return 0;
511
Tim Duesterhusa6cc7e82019-05-13 10:53:29 +0200512 /* properly destroy the chunk */
513 chunk_destroy(&arg->data.str);
514
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200515 /* Use the global variable name pointer. */
516 arg->type = ARGT_VAR;
517 arg->data.var.name = name;
518 arg->data.var.scope = scope;
519 return 1;
520}
521
Christopher Faulet09c9df22016-10-31 11:05:37 +0100522/* This function store a sample in a variable if it was already defined.
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200523 * Returns zero on failure and non-zero otherwise. The variable not being
524 * defined is treated as a failure.
Christopher Faulet09c9df22016-10-31 11:05:37 +0100525 */
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200526int vars_set_by_name_ifexist(const char *name, size_t len, struct sample *smp)
Christopher Faulet09c9df22016-10-31 11:05:37 +0100527{
528 enum vars_scope scope;
529
530 /* Resolve name and scope. */
531 name = register_name(name, len, &scope, 0, NULL);
532 if (!name)
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200533 return 0;
Christopher Faulet09c9df22016-10-31 11:05:37 +0100534
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200535 return sample_store_stream(name, scope, smp);
Christopher Faulet09c9df22016-10-31 11:05:37 +0100536}
537
538
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200539/* This function store a sample in a variable.
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200540 * Returns zero on failure and non-zero otherwise.
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200541 */
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200542int vars_set_by_name(const char *name, size_t len, struct sample *smp)
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200543{
544 enum vars_scope scope;
545
546 /* Resolve name and scope. */
Christopher Faulet09c9df22016-10-31 11:05:37 +0100547 name = register_name(name, len, &scope, 1, NULL);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200548 if (!name)
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200549 return 0;
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200550
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200551 return sample_store_stream(name, scope, smp);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200552}
553
Christopher Faulet85d79c92016-11-09 16:54:56 +0100554/* This function unset a variable if it was already defined.
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200555 * Returns zero on failure and non-zero otherwise.
Christopher Faulet85d79c92016-11-09 16:54:56 +0100556 */
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200557int vars_unset_by_name_ifexist(const char *name, size_t len, struct sample *smp)
Christopher Faulet85d79c92016-11-09 16:54:56 +0100558{
559 enum vars_scope scope;
560
561 /* Resolve name and scope. */
562 name = register_name(name, len, &scope, 0, NULL);
563 if (!name)
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200564 return 0;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100565
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200566 return sample_clear_stream(name, scope, smp);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100567}
568
569
Dragan Dosen14518f22021-02-22 17:20:01 +0100570/* This function fills a sample with the variable content.
571 *
572 * Keep in mind that a sample content is duplicated by using smp_dup()
573 * and it therefore uses a pre-allocated trash chunk as returned by
574 * get_trash_chunk().
575 *
576 * Returns 1 if the sample is filled, otherwise it returns 0.
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200577 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100578int vars_get_by_name(const char *name, size_t len, struct sample *smp)
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200579{
580 struct vars *vars;
581 struct var *var;
582 enum vars_scope scope;
583
584 /* Resolve name and scope. */
Christopher Faulet09c9df22016-10-31 11:05:37 +0100585 name = register_name(name, len, &scope, 1, NULL);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200586 if (!name)
587 return 0;
588
589 /* Select "vars" pool according with the scope. */
Willy Tarreauf37b1402019-06-04 16:27:36 +0200590 vars = get_vars(smp->sess, smp->strm, scope);
591 if (!vars || vars->scope != scope)
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200592 return 0;
593
594 /* Get the variable entry. */
Dragan Dosen14518f22021-02-22 17:20:01 +0100595 HA_RWLOCK_RDLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200596 var = var_get(vars, name);
Dragan Dosen14518f22021-02-22 17:20:01 +0100597 if (!var) {
598 HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200599 return 0;
Dragan Dosen14518f22021-02-22 17:20:01 +0100600 }
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200601
602 /* Copy sample. */
Thierry FOURNIER5cc18d42015-08-19 09:02:36 +0200603 smp->data = var->data;
Dragan Dosen14518f22021-02-22 17:20:01 +0100604 smp_dup(smp);
605
606 HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200607 return 1;
608}
609
Dragan Dosen14518f22021-02-22 17:20:01 +0100610/* This function fills a sample with the content of the variable described
611 * by <var_desc>.
612 *
613 * Keep in mind that a sample content is duplicated by using smp_dup()
614 * and it therefore uses a pre-allocated trash chunk as returned by
615 * get_trash_chunk().
616 *
617 * Returns 1 if the sample is filled, otherwise it returns 0.
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200618 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100619int vars_get_by_desc(const struct var_desc *var_desc, struct sample *smp)
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200620{
621 struct vars *vars;
622 struct var *var;
623
624 /* Select "vars" pool according with the scope. */
Willy Tarreauf37b1402019-06-04 16:27:36 +0200625 vars = get_vars(smp->sess, smp->strm, var_desc->scope);
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200626
Joseph Herlant07676892018-11-15 09:19:50 -0800627 /* Check if the scope is available a this point of processing. */
Willy Tarreauf37b1402019-06-04 16:27:36 +0200628 if (!vars || vars->scope != var_desc->scope)
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200629 return 0;
630
631 /* Get the variable entry. */
Dragan Dosen14518f22021-02-22 17:20:01 +0100632 HA_RWLOCK_RDLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200633 var = var_get(vars, var_desc->name);
Dragan Dosen14518f22021-02-22 17:20:01 +0100634 if (!var) {
635 HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200636 return 0;
Dragan Dosen14518f22021-02-22 17:20:01 +0100637 }
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200638
639 /* Copy sample. */
Thierry FOURNIER5cc18d42015-08-19 09:02:36 +0200640 smp->data = var->data;
Dragan Dosen14518f22021-02-22 17:20:01 +0100641 smp_dup(smp);
642
643 HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200644 return 1;
645}
646
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200647/* Always returns ACT_RET_CONT even if an error occurs. */
648static enum act_return action_store(struct act_rule *rule, struct proxy *px,
Willy Tarreau658b85b2015-09-27 10:00:49 +0200649 struct session *sess, struct stream *s, int flags)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200650{
651 struct sample smp;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200652 int dir;
653
654 switch (rule->from) {
Willy Tarreau620408f2016-10-21 16:37:51 +0200655 case ACT_F_TCP_REQ_SES: dir = SMP_OPT_DIR_REQ; break;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200656 case ACT_F_TCP_REQ_CNT: dir = SMP_OPT_DIR_REQ; break;
657 case ACT_F_TCP_RES_CNT: dir = SMP_OPT_DIR_RES; break;
658 case ACT_F_HTTP_REQ: dir = SMP_OPT_DIR_REQ; break;
659 case ACT_F_HTTP_RES: dir = SMP_OPT_DIR_RES; break;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +0100660 case ACT_F_TCP_CHK: dir = SMP_OPT_DIR_REQ; break;
Willy Tarreau01d580a2021-03-26 11:11:34 +0100661 case ACT_F_CFG_PARSER: dir = SMP_OPT_DIR_REQ; break; /* not used anyway */
Willy Tarreau2f836de2021-03-26 15:36:44 +0100662 case ACT_F_CLI_PARSER: dir = SMP_OPT_DIR_REQ; break; /* not used anyway */
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200663 default:
664 send_log(px, LOG_ERR, "Vars: internal error while execute action store.");
665 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Christopher Faulet767a84b2017-11-24 16:50:31 +0100666 ha_alert("Vars: internal error while execute action store.\n");
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200667 return ACT_RET_CONT;
668 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200669
670 /* Process the expression. */
671 memset(&smp, 0, sizeof(smp));
Willy Tarreau108a8fd2016-10-21 17:13:24 +0200672 if (!sample_process(px, sess, s, dir|SMP_OPT_FINAL,
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200673 rule->arg.vars.expr, &smp))
Thierry FOURNIER24ff6c62015-08-06 08:52:53 +0200674 return ACT_RET_CONT;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200675
676 /* Store the sample, and ignore errors. */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100677 sample_store_stream(rule->arg.vars.name, rule->arg.vars.scope, &smp);
Thierry FOURNIER24ff6c62015-08-06 08:52:53 +0200678 return ACT_RET_CONT;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200679}
680
Christopher Faulet85d79c92016-11-09 16:54:56 +0100681/* Always returns ACT_RET_CONT even if an error occurs. */
682static enum act_return action_clear(struct act_rule *rule, struct proxy *px,
683 struct session *sess, struct stream *s, int flags)
684{
685 struct sample smp;
686
687 memset(&smp, 0, sizeof(smp));
688 smp_set_owner(&smp, px, sess, s, SMP_OPT_FINAL);
689
690 /* Clear the variable using the sample context, and ignore errors. */
691 sample_clear_stream(rule->arg.vars.name, rule->arg.vars.scope, &smp);
692 return ACT_RET_CONT;
693}
694
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200695static void release_store_rule(struct act_rule *rule)
696{
697 release_sample_expr(rule->arg.vars.expr);
698}
699
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200700/* This two function checks the variable name and replace the
701 * configuration string name by the global string name. its
702 * the same string, but the global pointer can be easy to
703 * compare.
704 *
705 * The first function checks a sample-fetch and the second
706 * checks a converter.
707 */
708static int smp_check_var(struct arg *args, char **err)
709{
710 return vars_check_arg(&args[0], err);
711}
712
713static int conv_check_var(struct arg *args, struct sample_conv *conv,
714 const char *file, int line, char **err_msg)
715{
716 return vars_check_arg(&args[0], err_msg);
717}
718
719/* This function is a common parser for using variables. It understands
720 * the format:
721 *
722 * set-var(<variable-name>) <expression>
Willy Tarreau4b7531f2019-06-04 16:43:29 +0200723 * unset-var(<variable-name>)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200724 *
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200725 * It returns ACT_RET_PRS_ERR if fails and <err> is filled with an error
726 * message. Otherwise, it returns ACT_RET_PRS_OK and the variable <expr>
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100727 * is filled with the pointer to the expression to execute. The proxy is
728 * only used to retrieve the ->conf entries.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200729 */
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200730static enum act_parse_ret parse_store(const char **args, int *arg, struct proxy *px,
731 struct act_rule *rule, char **err)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200732{
733 const char *var_name = args[*arg-1];
734 int var_len;
Thierry FOURNIER48a9cd12015-07-28 19:00:28 +0200735 const char *kw_name;
Willy Tarreaue3658152016-11-24 21:23:28 +0100736 int flags, set_var = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200737
Christopher Faulet85d79c92016-11-09 16:54:56 +0100738 if (!strncmp(var_name, "set-var", 7)) {
739 var_name += 7;
740 set_var = 1;
741 }
742 if (!strncmp(var_name, "unset-var", 9)) {
743 var_name += 9;
744 set_var = 0;
745 }
746
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200747 if (*var_name != '(') {
Willy Tarreau3d1d1782021-03-12 11:53:19 +0100748 memprintf(err, "invalid or incomplete action '%s'. Expects 'set-var(<var-name>)' or 'unset-var(<var-name>)'",
Christopher Faulet85d79c92016-11-09 16:54:56 +0100749 args[*arg-1]);
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200750 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200751 }
752 var_name++; /* jump the '(' */
753 var_len = strlen(var_name);
754 var_len--; /* remove the ')' */
755 if (var_name[var_len] != ')') {
Willy Tarreau3d1d1782021-03-12 11:53:19 +0100756 memprintf(err, "incomplete expression after action '%s'. Expects 'set-var(<var-name>)' or 'unset-var(<var-name>)'",
Christopher Faulet85d79c92016-11-09 16:54:56 +0100757 args[*arg-1]);
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200758 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200759 }
760
Christopher Faulet09c9df22016-10-31 11:05:37 +0100761 rule->arg.vars.name = register_name(var_name, var_len, &rule->arg.vars.scope, 1, err);
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200762 if (!rule->arg.vars.name)
763 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200764
Christopher Faulet85d79c92016-11-09 16:54:56 +0100765 /* There is no fetch method when variable is unset. Just set the right
766 * action and return. */
767 if (!set_var) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100768 rule->action = ACT_CUSTOM;
769 rule->action_ptr = action_clear;
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200770 rule->release_ptr = release_store_rule;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100771 return ACT_RET_PRS_OK;
772 }
773
Thierry FOURNIER48a9cd12015-07-28 19:00:28 +0200774 kw_name = args[*arg-1];
775
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200776 rule->arg.vars.expr = sample_parse_expr((char **)args, arg, px->conf.args.file,
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100777 px->conf.args.line, err, &px->conf.args, NULL);
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200778 if (!rule->arg.vars.expr)
Thierry FOURNIERafa80492015-08-19 09:04:15 +0200779 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200780
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200781 switch (rule->from) {
Willy Tarreau620408f2016-10-21 16:37:51 +0200782 case ACT_F_TCP_REQ_SES: flags = SMP_VAL_FE_SES_ACC; break;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200783 case ACT_F_TCP_REQ_CNT: flags = SMP_VAL_FE_REQ_CNT; break;
784 case ACT_F_TCP_RES_CNT: flags = SMP_VAL_BE_RES_CNT; break;
785 case ACT_F_HTTP_REQ: flags = SMP_VAL_FE_HRQ_HDR; break;
786 case ACT_F_HTTP_RES: flags = SMP_VAL_BE_HRS_HDR; break;
Gaetan Rivet707b52f2020-02-21 18:14:59 +0100787 case ACT_F_TCP_CHK: flags = SMP_VAL_BE_CHK_RUL; break;
Willy Tarreau01d580a2021-03-26 11:11:34 +0100788 case ACT_F_CFG_PARSER: flags = SMP_VAL_CFG_PARSER; break;
Willy Tarreau2f836de2021-03-26 15:36:44 +0100789 case ACT_F_CLI_PARSER: flags = SMP_VAL_CLI_PARSER; break;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200790 default:
791 memprintf(err,
792 "internal error, unexpected rule->from=%d, please report this bug!",
793 rule->from);
Thierry FOURNIERafa80492015-08-19 09:04:15 +0200794 return ACT_RET_PRS_ERR;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200795 }
796 if (!(rule->arg.vars.expr->fetch->val & flags)) {
797 memprintf(err,
798 "fetch method '%s' extracts information from '%s', none of which is available here",
799 kw_name, sample_src_names(rule->arg.vars.expr->fetch->use));
800 free(rule->arg.vars.expr);
Thierry FOURNIERafa80492015-08-19 09:04:15 +0200801 return ACT_RET_PRS_ERR;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200802 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200803
Thierry FOURNIER42148732015-09-02 17:17:33 +0200804 rule->action = ACT_CUSTOM;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200805 rule->action_ptr = action_store;
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200806 rule->release_ptr = release_store_rule;
Thierry FOURNIERafa80492015-08-19 09:04:15 +0200807 return ACT_RET_PRS_OK;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200808}
809
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100810
811/* parses a global "set-var" directive. It will create a temporary rule and
812 * expression that are parsed, processed, and released on the fly so that we
813 * respect the real set-var syntax. These directives take the following format:
814 * set-var <name> <expression>
815 * Note that parse_store() expects "set-var(name) <expression>" so we have to
816 * temporarily replace the keyword here.
817 */
818static int vars_parse_global_set_var(char **args, int section_type, struct proxy *curpx,
819 const struct proxy *defpx, const char *file, int line,
820 char **err)
821{
822 struct proxy px = {
823 .id = "CLI",
824 .conf.args.file = file,
825 .conf.args.line = line,
826 };
827 struct act_rule rule = {
828 .arg.vars.scope = SCOPE_PROC,
829 .from = ACT_F_CFG_PARSER,
830 };
831 enum act_parse_ret p_ret;
832 char *old_arg1;
833 char *tmp_arg1;
834 int arg = 2; // variable name
835 int ret = -1;
836
837 LIST_INIT(&px.conf.args.list);
838
839 if (!*args[1] || !*args[2]) {
840 memprintf(err, "'%s' requires a process-wide variable name ('proc.<name>') and a sample expression.", args[0]);
841 goto end;
842 }
843
844 tmp_arg1 = NULL;
845 if (!memprintf(&tmp_arg1, "set-var(%s)", args[1]))
846 goto end;
847
848 /* parse_store() will always return a message in <err> on error */
849 old_arg1 = args[1]; args[1] = tmp_arg1;
850 p_ret = parse_store((const char **)args, &arg, &px, &rule, err);
851 free(args[1]); args[1] = old_arg1;
852
853 if (p_ret != ACT_RET_PRS_OK)
854 goto end;
855
856 if (rule.arg.vars.scope != SCOPE_PROC) {
857 memprintf(err, "'%s': cannot set variable '%s', only scope 'proc' is permitted in the global section.", args[0], args[1]);
858 goto end;
859 }
860
861 if (smp_resolve_args(&px, err) != 0) {
862 release_sample_expr(rule.arg.vars.expr);
863 indent_msg(err, 2);
864 goto end;
865 }
866
867 action_store(&rule, &px, NULL, NULL, 0);
868 release_sample_expr(rule.arg.vars.expr);
869
870 ret = 0;
871 end:
872 return ret;
873}
874
Willy Tarreauc35eb382021-03-26 14:51:31 +0100875/* parse CLI's "get var <name>" */
876static int vars_parse_cli_get_var(char **args, char *payload, struct appctx *appctx, void *private)
877{
878 struct vars *vars;
Willy Tarreau374edc72021-04-01 17:01:43 +0200879 struct sample smp = { };
Willy Tarreauc35eb382021-03-26 14:51:31 +0100880 int i;
881
882 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
883 return 1;
884
885 if (!*args[2])
886 return cli_err(appctx, "Missing process-wide variable identifier.\n");
887
888 vars = get_vars(NULL, NULL, SCOPE_PROC);
889 if (!vars || vars->scope != SCOPE_PROC)
890 return 0;
891
892 if (!vars_get_by_name(args[2], strlen(args[2]), &smp))
893 return cli_err(appctx, "Variable not found.\n");
894
895 /* the sample returned by vars_get_by_name() is allocated into a trash
896 * chunk so we have no constraint to manipulate it.
897 */
898 chunk_printf(&trash, "%s: type=%s value=", args[2], smp_to_type[smp.data.type]);
899
900 if (!sample_casts[smp.data.type][SMP_T_STR] ||
901 !sample_casts[smp.data.type][SMP_T_STR](&smp)) {
902 chunk_appendf(&trash, "(undisplayable)");
903 } else {
904 /* Display the displayable chars*. */
905 b_putchr(&trash, '<');
906 for (i = 0; i < smp.data.u.str.data; i++) {
907 if (isprint((unsigned char)smp.data.u.str.area[i]))
908 b_putchr(&trash, smp.data.u.str.area[i]);
909 else
910 b_putchr(&trash, '.');
911 }
912 b_putchr(&trash, '>');
913 b_putchr(&trash, 0);
914 }
915 return cli_msg(appctx, LOG_INFO, trash.area);
916}
917
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +0100918/* parse CLI's "set var <name> <expression>" */
919static int vars_parse_cli_set_var(char **args, char *payload, struct appctx *appctx, void *private)
920{
921 struct proxy px = {
922 .id = "CLI",
923 .conf.args.file = "CLI",
924 .conf.args.line = 0,
925 };
926 struct act_rule rule = {
927 .arg.vars.scope = SCOPE_PROC,
928 .from = ACT_F_CLI_PARSER,
929 };
930 enum act_parse_ret p_ret;
931 char *old_arg2;
932 char *tmp_arg2;
933 char *err = NULL;
934 int arg = 2; // variable name
935 int nberr;
936
937 LIST_INIT(&px.conf.args.list);
938
939 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
940 return 1;
941
942 if (!*args[2] || !*args[3])
943 return cli_err(appctx, "Missing process-wide variable identifier and expression.\n");
944
945 tmp_arg2 = NULL;
946 if (!memprintf(&tmp_arg2, "set-var(%s)", args[2])) {
947 memprintf(&err, "memory allocation error.");
948 goto fail;
949 }
950
951 /* parse_store() will always return a message in <err> on error */
952 old_arg2 = args[2]; args[2] = tmp_arg2;
953 p_ret = parse_store((const char **)(args + 1), &arg, &px, &rule, &err);
954 free(args[2]); args[2] = old_arg2;
955
956 if (p_ret != ACT_RET_PRS_OK)
957 goto fail;
958
959 if (rule.arg.vars.scope != SCOPE_PROC) {
960 memprintf(&err, "'%s %s': cannot set variable '%s', only scope 'proc' is permitted in the global section.", args[0], args[1], args[2]);
961 goto fail;
962 }
963
964 err = NULL;
965 nberr = smp_resolve_args(&px, &err);
966 if (nberr) {
967 release_sample_expr(rule.arg.vars.expr);
968 indent_msg(&err, 2);
969 goto fail;
970 }
971
972 action_store(&rule, &px, NULL, NULL, 0);
973 release_sample_expr(rule.arg.vars.expr);
974 appctx->st0 = CLI_ST_PROMPT;
975 return 0;
976 fail:
977 return cli_dynerr(appctx, err);
978}
979
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200980static int vars_max_size(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +0100981 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200982 char **err, unsigned int *limit)
983{
984 char *error;
985
986 *limit = strtol(args[1], &error, 10);
987 if (*error != 0) {
988 memprintf(err, "%s: '%s' is an invalid size", args[0], args[1]);
989 return -1;
990 }
991 return 0;
992}
993
994static int vars_max_size_global(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +0100995 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200996 char **err)
997{
998 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_global_limit);
999}
1000
Christopher Fauletff2613e2016-11-09 11:36:17 +01001001static int vars_max_size_proc(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001002 const struct proxy *defpx, const char *file, int line,
Christopher Fauletff2613e2016-11-09 11:36:17 +01001003 char **err)
1004{
1005 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_proc_limit);
1006}
1007
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001008static int vars_max_size_sess(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001009 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001010 char **err)
1011{
1012 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_sess_limit);
1013}
1014
1015static int vars_max_size_txn(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001016 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001017 char **err)
1018{
1019 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_txn_limit);
1020}
1021
1022static int vars_max_size_reqres(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001023 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001024 char **err)
1025{
1026 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_reqres_limit);
1027}
1028
Gaetan Rivet13a50432020-02-21 18:13:44 +01001029static int vars_max_size_check(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001030 const struct proxy *defpx, const char *file, int line,
Gaetan Rivet13a50432020-02-21 18:13:44 +01001031 char **err)
1032{
1033 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_check_limit);
1034}
1035
Tim Duesterhusbbdd5b82020-07-04 11:53:25 +02001036static void vars_deinit()
1037{
1038 while (var_names_nb-- > 0)
1039 free(var_names[var_names_nb]);
1040 free(var_names);
1041}
1042
1043REGISTER_POST_DEINIT(vars_deinit);
1044
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001045static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
1046
Willy Tarreau0209c972021-03-26 12:03:11 +01001047 { "var", smp_fetch_var, ARG1(1,STR), smp_check_var, SMP_T_STR, SMP_USE_CONST },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001048 { /* END */ },
1049}};
1050
Willy Tarreau0108d902018-11-25 19:14:37 +01001051INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);
1052
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001053static struct sample_conv_kw_list sample_conv_kws = {ILH, {
Christopher Faulet85d79c92016-11-09 16:54:56 +01001054 { "set-var", smp_conv_store, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
1055 { "unset-var", smp_conv_clear, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001056 { /* END */ },
1057}};
1058
Willy Tarreau0108d902018-11-25 19:14:37 +01001059INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws);
1060
Willy Tarreau620408f2016-10-21 16:37:51 +02001061static struct action_kw_list tcp_req_sess_kws = { { }, {
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001062 { "set-var", parse_store, KWF_MATCH_PREFIX },
1063 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Willy Tarreau620408f2016-10-21 16:37:51 +02001064 { /* END */ }
1065}};
1066
Willy Tarreau0108d902018-11-25 19:14:37 +01001067INITCALL1(STG_REGISTER, tcp_req_sess_keywords_register, &tcp_req_sess_kws);
1068
Willy Tarreau620408f2016-10-21 16:37:51 +02001069static struct action_kw_list tcp_req_cont_kws = { { }, {
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001070 { "set-var", parse_store, KWF_MATCH_PREFIX },
1071 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001072 { /* END */ }
1073}};
1074
Willy Tarreau0108d902018-11-25 19:14:37 +01001075INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_cont_kws);
1076
Thierry FOURNIER36481b82015-08-19 09:01:53 +02001077static struct action_kw_list tcp_res_kws = { { }, {
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001078 { "set-var", parse_store, KWF_MATCH_PREFIX },
1079 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001080 { /* END */ }
1081}};
1082
Willy Tarreau0108d902018-11-25 19:14:37 +01001083INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_kws);
1084
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001085static struct action_kw_list tcp_check_kws = {ILH, {
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001086 { "set-var", parse_store, KWF_MATCH_PREFIX },
1087 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001088 { /* END */ }
1089}};
1090
1091INITCALL1(STG_REGISTER, tcp_check_keywords_register, &tcp_check_kws);
1092
Thierry FOURNIER36481b82015-08-19 09:01:53 +02001093static struct action_kw_list http_req_kws = { { }, {
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001094 { "set-var", parse_store, KWF_MATCH_PREFIX },
1095 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001096 { /* END */ }
1097}};
1098
Willy Tarreau0108d902018-11-25 19:14:37 +01001099INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_kws);
1100
Thierry FOURNIER36481b82015-08-19 09:01:53 +02001101static struct action_kw_list http_res_kws = { { }, {
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001102 { "set-var", parse_store, KWF_MATCH_PREFIX },
1103 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001104 { /* END */ }
1105}};
1106
Willy Tarreau0108d902018-11-25 19:14:37 +01001107INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_kws);
1108
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01001109static struct action_kw_list http_after_res_kws = { { }, {
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001110 { "set-var", parse_store, KWF_MATCH_PREFIX },
1111 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01001112 { /* END */ }
1113}};
1114
1115INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_kws);
1116
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001117static struct cfg_kw_list cfg_kws = {{ },{
Willy Tarreau13d2ba22021-03-26 11:38:08 +01001118 { CFG_GLOBAL, "set-var", vars_parse_global_set_var },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001119 { CFG_GLOBAL, "tune.vars.global-max-size", vars_max_size_global },
Christopher Fauletff2613e2016-11-09 11:36:17 +01001120 { CFG_GLOBAL, "tune.vars.proc-max-size", vars_max_size_proc },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001121 { CFG_GLOBAL, "tune.vars.sess-max-size", vars_max_size_sess },
1122 { CFG_GLOBAL, "tune.vars.txn-max-size", vars_max_size_txn },
1123 { CFG_GLOBAL, "tune.vars.reqres-max-size", vars_max_size_reqres },
Gaetan Rivet13a50432020-02-21 18:13:44 +01001124 { CFG_GLOBAL, "tune.vars.check-max-size", vars_max_size_check },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001125 { /* END */ }
1126}};
1127
Willy Tarreau0108d902018-11-25 19:14:37 +01001128INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Willy Tarreauc35eb382021-03-26 14:51:31 +01001129
1130
1131/* register cli keywords */
1132static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02001133 { { "get", "var", NULL }, "get var <name> : retrieve contents of a process-wide variable", vars_parse_cli_get_var, NULL },
1134 { { "set", "var", NULL }, "set var <name> <expr> : set variable from an expression", vars_parse_cli_set_var, NULL, NULL, NULL, ACCESS_EXPERIMENTAL },
Willy Tarreauc35eb382021-03-26 14:51:31 +01001135 { { NULL }, NULL, NULL, NULL }
1136}};
1137INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);