blob: d533b180addc86bfe6f07346b36be8ed56f76843 [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 Tarreau67046bf2021-05-08 13:56:31 +020018#include <haproxy/tools.h>
Willy Tarreaua1718922020-06-04 16:25:31 +020019#include <haproxy/vars.h>
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020020
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020021
22/* This contains a pool of struct vars */
Willy Tarreau8ceae722018-11-26 11:58:30 +010023DECLARE_STATIC_POOL(var_pool, "vars", sizeof(struct var));
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020024
Willy Tarreaucfc4f242021-05-08 11:41:28 +020025/* list of variables for the process scope. */
26struct vars proc_vars THREAD_ALIGNED(64);
27
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020028/* This array contain all the names of all the HAProxy vars.
29 * This permits to identify two variables name with
30 * only one pointer. It permits to not using strdup() for
31 * each variable name used during the runtime.
32 */
33static char **var_names = NULL;
34static int var_names_nb = 0;
35
36/* This array of int contains the system limits per context. */
37static unsigned int var_global_limit = 0;
38static unsigned int var_global_size = 0;
Christopher Fauletff2613e2016-11-09 11:36:17 +010039static unsigned int var_proc_limit = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020040static unsigned int var_sess_limit = 0;
41static unsigned int var_txn_limit = 0;
42static unsigned int var_reqres_limit = 0;
Gaetan Rivet13a50432020-02-21 18:13:44 +010043static unsigned int var_check_limit = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020044
Willy Tarreau86abe442018-11-25 20:12:18 +010045__decl_rwlock(var_names_rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +020046
Willy Tarreauf37b1402019-06-04 16:27:36 +020047/* returns the struct vars pointer for a session, stream and scope, or NULL if
48 * it does not exist.
49 */
50static inline struct vars *get_vars(struct session *sess, struct stream *strm, enum vars_scope scope)
51{
52 switch (scope) {
53 case SCOPE_PROC:
Willy Tarreaucfc4f242021-05-08 11:41:28 +020054 return &proc_vars;
Willy Tarreauf37b1402019-06-04 16:27:36 +020055 case SCOPE_SESS:
Willy Tarreaua07d61b2021-03-26 11:27:59 +010056 return sess ? &sess->vars : NULL;
Gaetan Rivet13a50432020-02-21 18:13:44 +010057 case SCOPE_CHECK: {
Christopher Fauletf0367302021-06-02 11:48:42 +020058 struct check *check = sess ? objt_check(sess->origin) : NULL;
Gaetan Rivet13a50432020-02-21 18:13:44 +010059
Christopher Faulet0fca7ed2020-04-21 11:53:32 +020060 return check ? &check->vars : NULL;
Gaetan Rivet13a50432020-02-21 18:13:44 +010061 }
Willy Tarreauf37b1402019-06-04 16:27:36 +020062 case SCOPE_TXN:
63 return strm ? &strm->vars_txn : NULL;
64 case SCOPE_REQ:
65 case SCOPE_RES:
66 default:
67 return strm ? &strm->vars_reqres : NULL;
68 }
69}
70
Willy Tarreau72330982015-06-19 11:21:56 +020071/* This function adds or remove memory size from the accounting. The inner
72 * pointers may be null when setting the outer ones only.
73 */
Miroslav Zagorac6deab792020-12-09 16:34:29 +010074void var_accounting_diff(struct vars *vars, struct session *sess, struct stream *strm, int size)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020075{
76 switch (vars->scope) {
77 case SCOPE_REQ:
78 case SCOPE_RES:
Willy Tarreauf37b1402019-06-04 16:27:36 +020079 if (strm)
80 _HA_ATOMIC_ADD(&strm->vars_reqres.size, size);
Willy Tarreau6204cd92016-03-10 16:33:04 +010081 /* fall through */
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020082 case SCOPE_TXN:
Willy Tarreauf37b1402019-06-04 16:27:36 +020083 if (strm)
84 _HA_ATOMIC_ADD(&strm->vars_txn.size, size);
Gaetan Rivet13a50432020-02-21 18:13:44 +010085 goto scope_sess;
86 case SCOPE_CHECK: {
Christopher Faulet0fca7ed2020-04-21 11:53:32 +020087 struct check *check = objt_check(sess->origin);
Gaetan Rivet13a50432020-02-21 18:13:44 +010088
Christopher Faulet0fca7ed2020-04-21 11:53:32 +020089 if (check)
90 _HA_ATOMIC_ADD(&check->vars.size, size);
Gaetan Rivet13a50432020-02-21 18:13:44 +010091 }
Willy Tarreau6204cd92016-03-10 16:33:04 +010092 /* fall through */
Gaetan Rivet13a50432020-02-21 18:13:44 +010093scope_sess:
Thierry FOURNIER4834bc72015-06-06 19:29:07 +020094 case SCOPE_SESS:
Olivier Houchard25ad13f2019-03-08 18:55:38 +010095 _HA_ATOMIC_ADD(&sess->vars.size, size);
Christopher Fauletff2613e2016-11-09 11:36:17 +010096 /* fall through */
97 case SCOPE_PROC:
Willy Tarreaucfc4f242021-05-08 11:41:28 +020098 _HA_ATOMIC_ADD(&proc_vars.size, size);
Olivier Houchard25ad13f2019-03-08 18:55:38 +010099 _HA_ATOMIC_ADD(&var_global_size, size);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200100 }
101}
102
103/* This function returns 1 if the <size> is available in the var
Joseph Herlant07676892018-11-15 09:19:50 -0800104 * pool <vars>, otherwise returns 0. If the space is available,
Willy Tarreau72330982015-06-19 11:21:56 +0200105 * the size is reserved. The inner pointers may be null when setting
Willy Tarreau6204cd92016-03-10 16:33:04 +0100106 * the outer ones only. The accounting uses either <sess> or <strm>
107 * depending on the scope. <strm> may be NULL when no stream is known
108 * and only the session exists (eg: tcp-request connection).
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200109 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100110static int var_accounting_add(struct vars *vars, struct session *sess, struct stream *strm, int size)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200111{
112 switch (vars->scope) {
113 case SCOPE_REQ:
114 case SCOPE_RES:
Willy Tarreauf37b1402019-06-04 16:27:36 +0200115 if (var_reqres_limit && strm && strm->vars_reqres.size + size > var_reqres_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200116 return 0;
Willy Tarreau6204cd92016-03-10 16:33:04 +0100117 /* fall through */
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200118 case SCOPE_TXN:
Willy Tarreauf37b1402019-06-04 16:27:36 +0200119 if (var_txn_limit && strm && strm->vars_txn.size + size > var_txn_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200120 return 0;
Gaetan Rivet13a50432020-02-21 18:13:44 +0100121 goto scope_sess;
122 case SCOPE_CHECK: {
Christopher Faulet0fca7ed2020-04-21 11:53:32 +0200123 struct check *check = objt_check(sess->origin);
Gaetan Rivet13a50432020-02-21 18:13:44 +0100124
Christopher Faulet0fca7ed2020-04-21 11:53:32 +0200125 if (var_check_limit && check && check->vars.size + size > var_check_limit)
Gaetan Rivet13a50432020-02-21 18:13:44 +0100126 return 0;
127 }
Willy Tarreau6204cd92016-03-10 16:33:04 +0100128 /* fall through */
Gaetan Rivet13a50432020-02-21 18:13:44 +0100129scope_sess:
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200130 case SCOPE_SESS:
Willy Tarreau6204cd92016-03-10 16:33:04 +0100131 if (var_sess_limit && sess->vars.size + size > var_sess_limit)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200132 return 0;
Christopher Fauletff2613e2016-11-09 11:36:17 +0100133 /* fall through */
134 case SCOPE_PROC:
Willy Tarreaucfc4f242021-05-08 11:41:28 +0200135 if (var_proc_limit && proc_vars.size + size > var_proc_limit)
Christopher Fauletff2613e2016-11-09 11:36:17 +0100136 return 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200137 if (var_global_limit && var_global_size + size > var_global_limit)
138 return 0;
139 }
Willy Tarreau6204cd92016-03-10 16:33:04 +0100140 var_accounting_diff(vars, sess, strm, size);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200141 return 1;
142}
143
Christopher Faulet85d79c92016-11-09 16:54:56 +0100144/* This fnuction remove a variable from the list and free memory it used */
145unsigned int var_clear(struct var *var)
146{
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 Tarreau2b718102021-04-21 07:32:39 +0200157 LIST_DELETE(&var->l);
Willy Tarreaubafbe012017-11-24 17:34:44 +0100158 pool_free(var_pool, var);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100159 size += sizeof(struct var);
160 return size;
161}
162
Joseph Herlant07676892018-11-15 09:19:50 -0800163/* This function free all the memory used by all the variables
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200164 * in the list.
165 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100166void vars_prune(struct vars *vars, struct session *sess, struct stream *strm)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200167{
168 struct var *var, *tmp;
Willy Tarreau72330982015-06-19 11:21:56 +0200169 unsigned int size = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200170
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100171 HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200172 list_for_each_entry_safe(var, tmp, &vars->head, l) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100173 size += var_clear(var);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200174 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100175 HA_RWLOCK_WRUNLOCK(VARS_LOCK, &vars->rwlock);
Willy Tarreau6204cd92016-03-10 16:33:04 +0100176 var_accounting_diff(vars, sess, strm, -size);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200177}
178
Willy Tarreauebcd4842015-06-19 11:59:02 +0200179/* This function frees all the memory used by all the session variables in the
180 * list starting at <vars>.
181 */
182void vars_prune_per_sess(struct vars *vars)
183{
184 struct var *var, *tmp;
185 unsigned int size = 0;
186
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100187 HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock);
Willy Tarreauebcd4842015-06-19 11:59:02 +0200188 list_for_each_entry_safe(var, tmp, &vars->head, l) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100189 size += var_clear(var);
Willy Tarreauebcd4842015-06-19 11:59:02 +0200190 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100191 HA_RWLOCK_WRUNLOCK(VARS_LOCK, &vars->rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200192
Olivier Houchard25ad13f2019-03-08 18:55:38 +0100193 _HA_ATOMIC_SUB(&vars->size, size);
Willy Tarreaucfc4f242021-05-08 11:41:28 +0200194 _HA_ATOMIC_SUB(&proc_vars.size, size);
Olivier Houchard25ad13f2019-03-08 18:55:38 +0100195 _HA_ATOMIC_SUB(&var_global_size, size);
Willy Tarreauebcd4842015-06-19 11:59:02 +0200196}
197
Ilya Shipitsin46a030c2020-07-05 16:36:08 +0500198/* This function init a list of variables. */
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200199void vars_init(struct vars *vars, enum vars_scope scope)
200{
201 LIST_INIT(&vars->head);
202 vars->scope = scope;
203 vars->size = 0;
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100204 HA_RWLOCK_INIT(&vars->rwlock);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200205}
206
207/* This function declares a new variable name. It returns a pointer
208 * on the string identifying the name. This function assures that
209 * the same name exists only once.
210 *
211 * This function check if the variable name is acceptable.
212 *
213 * The function returns NULL if an error occurs, and <err> is filled.
214 * In this case, the HAProxy must be stopped because the structs are
215 * left inconsistent. Otherwise, it returns the pointer on the global
216 * name.
217 */
Christopher Faulet09c9df22016-10-31 11:05:37 +0100218static char *register_name(const char *name, int len, enum vars_scope *scope,
219 int alloc, char **err)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200220{
221 int i;
Hubert Verstraete831962e2016-06-28 22:44:26 +0200222 char **var_names2;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200223 const char *tmp;
Christopher Faulete95f2c32017-07-24 16:30:34 +0200224 char *res = NULL;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200225
226 /* Check length. */
227 if (len == 0) {
228 memprintf(err, "Empty variable name cannot be accepted");
Christopher Fauleteb3e2762017-12-08 09:17:39 +0100229 return res;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200230 }
231
232 /* Check scope. */
Christopher Fauletff2613e2016-11-09 11:36:17 +0100233 if (len > 5 && strncmp(name, "proc.", 5) == 0) {
234 name += 5;
235 len -= 5;
236 *scope = SCOPE_PROC;
237 }
238 else if (len > 5 && strncmp(name, "sess.", 5) == 0) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200239 name += 5;
240 len -= 5;
241 *scope = SCOPE_SESS;
242 }
243 else if (len > 4 && strncmp(name, "txn.", 4) == 0) {
244 name += 4;
245 len -= 4;
246 *scope = SCOPE_TXN;
247 }
248 else if (len > 4 && strncmp(name, "req.", 4) == 0) {
249 name += 4;
250 len -= 4;
251 *scope = SCOPE_REQ;
252 }
253 else if (len > 4 && strncmp(name, "res.", 4) == 0) {
254 name += 4;
255 len -= 4;
256 *scope = SCOPE_RES;
257 }
Gaetan Rivet13a50432020-02-21 18:13:44 +0100258 else if (len > 6 && strncmp(name, "check.", 6) == 0) {
259 name += 6;
260 len -= 6;
261 *scope = SCOPE_CHECK;
262 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200263 else {
264 memprintf(err, "invalid variable name '%s'. A variable name must be start by its scope. "
Gaetan Rivet13a50432020-02-21 18:13:44 +0100265 "The scope can be 'proc', 'sess', 'txn', 'req', 'res' or 'check'", name);
Christopher Fauleteb3e2762017-12-08 09:17:39 +0100266 return res;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200267 }
268
Christopher Faulete95f2c32017-07-24 16:30:34 +0200269 if (alloc)
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100270 HA_RWLOCK_WRLOCK(VARS_LOCK, &var_names_rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200271 else
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100272 HA_RWLOCK_RDLOCK(VARS_LOCK, &var_names_rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200273
274
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200275 /* Look for existing variable name. */
276 for (i = 0; i < var_names_nb; i++)
Christopher Faulete95f2c32017-07-24 16:30:34 +0200277 if (strncmp(var_names[i], name, len) == 0 && var_names[i][len] == '\0') {
278 res = var_names[i];
279 goto end;
280 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200281
Christopher Faulete95f2c32017-07-24 16:30:34 +0200282 if (!alloc) {
283 res = NULL;
284 goto end;
285 }
Christopher Faulet09c9df22016-10-31 11:05:37 +0100286
Hubert Verstraete831962e2016-06-28 22:44:26 +0200287 /* Store variable name. If realloc fails, var_names remains valid */
288 var_names2 = realloc(var_names, (var_names_nb + 1) * sizeof(*var_names));
289 if (!var_names2) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200290 memprintf(err, "out of memory error");
Christopher Faulete95f2c32017-07-24 16:30:34 +0200291 res = NULL;
292 goto end;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200293 }
Hubert Verstraete831962e2016-06-28 22:44:26 +0200294 var_names_nb++;
295 var_names = var_names2;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200296 var_names[var_names_nb - 1] = malloc(len + 1);
297 if (!var_names[var_names_nb - 1]) {
298 memprintf(err, "out of memory error");
Christopher Faulete95f2c32017-07-24 16:30:34 +0200299 res = NULL;
300 goto end;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200301 }
302 memcpy(var_names[var_names_nb - 1], name, len);
303 var_names[var_names_nb - 1][len] = '\0';
304
305 /* Check variable name syntax. */
306 tmp = var_names[var_names_nb - 1];
307 while (*tmp) {
Willy Tarreau90807112020-02-25 08:16:33 +0100308 if (!isalnum((unsigned char)*tmp) && *tmp != '_' && *tmp != '.') {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200309 memprintf(err, "invalid syntax at char '%s'", tmp);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200310 res = NULL;
311 goto end;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200312 }
313 tmp++;
314 }
Christopher Faulete95f2c32017-07-24 16:30:34 +0200315 res = var_names[var_names_nb - 1];
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200316
Christopher Faulete95f2c32017-07-24 16:30:34 +0200317 end:
318 if (alloc)
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100319 HA_RWLOCK_WRUNLOCK(VARS_LOCK, &var_names_rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200320 else
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100321 HA_RWLOCK_RDUNLOCK(VARS_LOCK, &var_names_rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200322
323 return res;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200324}
325
326/* This function returns an existing variable or returns NULL. */
327static inline struct var *var_get(struct vars *vars, const char *name)
328{
329 struct var *var;
330
331 list_for_each_entry(var, &vars->head, l)
332 if (var->name == name)
333 return var;
334 return NULL;
335}
336
337/* Returns 0 if fails, else returns 1. */
338static int smp_fetch_var(const struct arg *args, struct sample *smp, const char *kw, void *private)
339{
340 const struct var_desc *var_desc = &args[0].data.var;
Christopher Faulete95f2c32017-07-24 16:30:34 +0200341
Dragan Dosenec0a6042021-02-22 17:39:02 +0100342 return vars_get_by_desc(var_desc, smp);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200343}
344
345/* This function search in the <head> a variable with the same
346 * pointer value that the <name>. If the variable doesn't exists,
347 * create it. The function stores a copy of smp> if the variable.
348 * It returns 0 if fails, else returns 1.
349 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100350static int sample_store(struct vars *vars, const char *name, struct sample *smp)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200351{
352 struct var *var;
353
354 /* Look for existing variable name. */
355 var = var_get(vars, name);
356
357 if (var) {
358 /* free its used memory. */
359 if (var->data.type == SMP_T_STR ||
360 var->data.type == SMP_T_BIN) {
Willy Tarreau5b52b002021-02-26 21:19:53 +0100361 ha_free(&var->data.u.str.area);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200362 var_accounting_diff(vars, smp->sess, smp->strm,
363 -var->data.u.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200364 }
Christopher Fauletd02210c2017-07-24 16:24:39 +0200365 else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
Willy Tarreau5b52b002021-02-26 21:19:53 +0100366 ha_free(&var->data.u.meth.str.area);
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200367 var_accounting_diff(vars, smp->sess, smp->strm,
368 -var->data.u.meth.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200369 }
370 } else {
371
Joseph Herlant07676892018-11-15 09:19:50 -0800372 /* Check memory available. */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100373 if (!var_accounting_add(vars, smp->sess, smp->strm, sizeof(struct var)))
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200374 return 0;
375
376 /* Create new entry. */
Willy Tarreaubafbe012017-11-24 17:34:44 +0100377 var = pool_alloc(var_pool);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200378 if (!var)
379 return 0;
Willy Tarreau2b718102021-04-21 07:32:39 +0200380 LIST_APPEND(&vars->head, &var->l);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200381 var->name = name;
382 }
383
384 /* Set type. */
Thierry FOURNIER8c542ca2015-08-19 09:00:18 +0200385 var->data.type = smp->data.type;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200386
387 /* Copy data. If the data needs memory, the function can fail. */
388 switch (var->data.type) {
389 case SMP_T_BOOL:
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200390 case SMP_T_SINT:
Thierry FOURNIER136f9d32015-08-19 09:07:19 +0200391 var->data.u.sint = smp->data.u.sint;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200392 break;
393 case SMP_T_IPV4:
Thierry FOURNIER136f9d32015-08-19 09:07:19 +0200394 var->data.u.ipv4 = smp->data.u.ipv4;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200395 break;
396 case SMP_T_IPV6:
Thierry FOURNIER136f9d32015-08-19 09:07:19 +0200397 var->data.u.ipv6 = smp->data.u.ipv6;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200398 break;
399 case SMP_T_STR:
400 case SMP_T_BIN:
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200401 if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.str.data)) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200402 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
403 return 0;
404 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200405 var->data.u.str.area = malloc(smp->data.u.str.data);
406 if (!var->data.u.str.area) {
407 var_accounting_diff(vars, smp->sess, smp->strm,
408 -smp->data.u.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200409 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
410 return 0;
411 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200412 var->data.u.str.data = smp->data.u.str.data;
413 memcpy(var->data.u.str.area, smp->data.u.str.area,
414 var->data.u.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200415 break;
416 case SMP_T_METH:
Christopher Fauletd02210c2017-07-24 16:24:39 +0200417 var->data.u.meth.meth = smp->data.u.meth.meth;
418 if (smp->data.u.meth.meth != HTTP_METH_OTHER)
419 break;
420
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200421 if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.meth.str.data)) {
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200422 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
423 return 0;
424 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200425 var->data.u.meth.str.area = malloc(smp->data.u.meth.str.data);
426 if (!var->data.u.meth.str.area) {
427 var_accounting_diff(vars, smp->sess, smp->strm,
428 -smp->data.u.meth.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200429 var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
430 return 0;
431 }
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200432 var->data.u.meth.str.data = smp->data.u.meth.str.data;
433 var->data.u.meth.str.size = smp->data.u.meth.str.data;
434 memcpy(var->data.u.meth.str.area, smp->data.u.meth.str.area,
435 var->data.u.meth.str.data);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200436 break;
437 }
438 return 1;
439}
440
Willy Tarreau620408f2016-10-21 16:37:51 +0200441/* Returns 0 if fails, else returns 1. Note that stream may be null for SCOPE_SESS. */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100442static inline int sample_store_stream(const char *name, enum vars_scope scope, struct sample *smp)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200443{
444 struct vars *vars;
Christopher Faulete95f2c32017-07-24 16:30:34 +0200445 int ret;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200446
Willy Tarreauf37b1402019-06-04 16:27:36 +0200447 vars = get_vars(smp->sess, smp->strm, scope);
448 if (!vars || vars->scope != scope)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200449 return 0;
Christopher Faulete95f2c32017-07-24 16:30:34 +0200450
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100451 HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200452 ret = sample_store(vars, name, smp);
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100453 HA_RWLOCK_WRUNLOCK(VARS_LOCK, &vars->rwlock);
Christopher Faulete95f2c32017-07-24 16:30:34 +0200454 return ret;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200455}
456
Christopher Faulet85d79c92016-11-09 16:54:56 +0100457/* Returns 0 if fails, else returns 1. Note that stream may be null for SCOPE_SESS. */
458static inline int sample_clear_stream(const char *name, enum vars_scope scope, struct sample *smp)
459{
460 struct vars *vars;
461 struct var *var;
462 unsigned int size = 0;
463
Willy Tarreauf37b1402019-06-04 16:27:36 +0200464 vars = get_vars(smp->sess, smp->strm, scope);
465 if (!vars || vars->scope != scope)
Christopher Faulet85d79c92016-11-09 16:54:56 +0100466 return 0;
467
468 /* Look for existing variable name. */
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100469 HA_RWLOCK_WRLOCK(VARS_LOCK, &vars->rwlock);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100470 var = var_get(vars, name);
471 if (var) {
472 size = var_clear(var);
473 var_accounting_diff(vars, smp->sess, smp->strm, -size);
474 }
Christopher Faulet2a944ee2017-11-07 10:42:54 +0100475 HA_RWLOCK_WRUNLOCK(VARS_LOCK, &vars->rwlock);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100476 return 1;
477}
478
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200479/* Returns 0 if fails, else returns 1. */
480static int smp_conv_store(const struct arg *args, struct sample *smp, void *private)
481{
Christopher Faulet0099a8c2016-11-09 16:15:32 +0100482 return sample_store_stream(args[0].data.var.name, args[0].data.var.scope, smp);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200483}
484
Christopher Faulet85d79c92016-11-09 16:54:56 +0100485/* Returns 0 if fails, else returns 1. */
486static int smp_conv_clear(const struct arg *args, struct sample *smp, void *private)
487{
488 return sample_clear_stream(args[0].data.var.name, args[0].data.var.scope, smp);
489}
490
Joseph Herlant07676892018-11-15 09:19:50 -0800491/* This functions check an argument entry and fill it with a variable
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200492 * type. The argumen must be a string. If the variable lookup fails,
Joseph Herlant07676892018-11-15 09:19:50 -0800493 * the function returns 0 and fill <err>, otherwise it returns 1.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200494 */
495int vars_check_arg(struct arg *arg, char **err)
496{
497 char *name;
498 enum vars_scope scope;
499
500 /* Check arg type. */
501 if (arg->type != ARGT_STR) {
502 memprintf(err, "unexpected argument type");
503 return 0;
504 }
505
506 /* Register new variable name. */
Willy Tarreau843b7cb2018-07-13 10:54:26 +0200507 name = register_name(arg->data.str.area, arg->data.str.data, &scope,
508 1,
509 err);
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200510 if (!name)
511 return 0;
512
Tim Duesterhusa6cc7e82019-05-13 10:53:29 +0200513 /* properly destroy the chunk */
514 chunk_destroy(&arg->data.str);
515
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200516 /* Use the global variable name pointer. */
517 arg->type = ARGT_VAR;
518 arg->data.var.name = name;
519 arg->data.var.scope = scope;
520 return 1;
521}
522
Christopher Faulet09c9df22016-10-31 11:05:37 +0100523/* This function store a sample in a variable if it was already defined.
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200524 * Returns zero on failure and non-zero otherwise. The variable not being
525 * defined is treated as a failure.
Christopher Faulet09c9df22016-10-31 11:05:37 +0100526 */
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200527int vars_set_by_name_ifexist(const char *name, size_t len, struct sample *smp)
Christopher Faulet09c9df22016-10-31 11:05:37 +0100528{
529 enum vars_scope scope;
530
531 /* Resolve name and scope. */
532 name = register_name(name, len, &scope, 0, NULL);
533 if (!name)
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200534 return 0;
Christopher Faulet09c9df22016-10-31 11:05:37 +0100535
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200536 return sample_store_stream(name, scope, smp);
Christopher Faulet09c9df22016-10-31 11:05:37 +0100537}
538
539
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200540/* This function store a sample in a variable.
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200541 * Returns zero on failure and non-zero otherwise.
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200542 */
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200543int vars_set_by_name(const char *name, size_t len, struct sample *smp)
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200544{
545 enum vars_scope scope;
546
547 /* Resolve name and scope. */
Christopher Faulet09c9df22016-10-31 11:05:37 +0100548 name = register_name(name, len, &scope, 1, NULL);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200549 if (!name)
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200550 return 0;
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200551
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200552 return sample_store_stream(name, scope, smp);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200553}
554
Christopher Faulet85d79c92016-11-09 16:54:56 +0100555/* This function unset a variable if it was already defined.
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200556 * Returns zero on failure and non-zero otherwise.
Christopher Faulet85d79c92016-11-09 16:54:56 +0100557 */
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200558int vars_unset_by_name_ifexist(const char *name, size_t len, struct sample *smp)
Christopher Faulet85d79c92016-11-09 16:54:56 +0100559{
560 enum vars_scope scope;
561
562 /* Resolve name and scope. */
563 name = register_name(name, len, &scope, 0, NULL);
564 if (!name)
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200565 return 0;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100566
Tim Duesterhusb4fac1e2020-05-19 13:49:40 +0200567 return sample_clear_stream(name, scope, smp);
Christopher Faulet85d79c92016-11-09 16:54:56 +0100568}
569
570
Dragan Dosen14518f22021-02-22 17:20:01 +0100571/* This function fills a sample with the variable content.
572 *
573 * Keep in mind that a sample content is duplicated by using smp_dup()
574 * and it therefore uses a pre-allocated trash chunk as returned by
575 * get_trash_chunk().
576 *
577 * Returns 1 if the sample is filled, otherwise it returns 0.
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200578 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100579int vars_get_by_name(const char *name, size_t len, struct sample *smp)
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200580{
581 struct vars *vars;
582 struct var *var;
583 enum vars_scope scope;
584
585 /* Resolve name and scope. */
Willy Tarreau89f6ded2021-05-13 13:30:12 +0200586 name = register_name(name, len, &scope, 0, NULL);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200587 if (!name)
588 return 0;
589
590 /* Select "vars" pool according with the scope. */
Willy Tarreauf37b1402019-06-04 16:27:36 +0200591 vars = get_vars(smp->sess, smp->strm, scope);
592 if (!vars || vars->scope != scope)
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200593 return 0;
594
595 /* Get the variable entry. */
Dragan Dosen14518f22021-02-22 17:20:01 +0100596 HA_RWLOCK_RDLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200597 var = var_get(vars, name);
Dragan Dosen14518f22021-02-22 17:20:01 +0100598 if (!var) {
599 HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200600 return 0;
Dragan Dosen14518f22021-02-22 17:20:01 +0100601 }
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200602
603 /* Copy sample. */
Thierry FOURNIER5cc18d42015-08-19 09:02:36 +0200604 smp->data = var->data;
Dragan Dosen14518f22021-02-22 17:20:01 +0100605 smp_dup(smp);
606
607 HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIERc365d992015-06-09 12:27:17 +0200608 return 1;
609}
610
Dragan Dosen14518f22021-02-22 17:20:01 +0100611/* This function fills a sample with the content of the variable described
612 * by <var_desc>.
613 *
614 * Keep in mind that a sample content is duplicated by using smp_dup()
615 * and it therefore uses a pre-allocated trash chunk as returned by
616 * get_trash_chunk().
617 *
618 * Returns 1 if the sample is filled, otherwise it returns 0.
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200619 */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100620int vars_get_by_desc(const struct var_desc *var_desc, struct sample *smp)
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200621{
622 struct vars *vars;
623 struct var *var;
624
625 /* Select "vars" pool according with the scope. */
Willy Tarreauf37b1402019-06-04 16:27:36 +0200626 vars = get_vars(smp->sess, smp->strm, var_desc->scope);
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200627
Joseph Herlant07676892018-11-15 09:19:50 -0800628 /* Check if the scope is available a this point of processing. */
Willy Tarreauf37b1402019-06-04 16:27:36 +0200629 if (!vars || vars->scope != var_desc->scope)
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200630 return 0;
631
632 /* Get the variable entry. */
Dragan Dosen14518f22021-02-22 17:20:01 +0100633 HA_RWLOCK_RDLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200634 var = var_get(vars, var_desc->name);
Dragan Dosen14518f22021-02-22 17:20:01 +0100635 if (!var) {
636 HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200637 return 0;
Dragan Dosen14518f22021-02-22 17:20:01 +0100638 }
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200639
640 /* Copy sample. */
Thierry FOURNIER5cc18d42015-08-19 09:02:36 +0200641 smp->data = var->data;
Dragan Dosen14518f22021-02-22 17:20:01 +0100642 smp_dup(smp);
643
644 HA_RWLOCK_RDUNLOCK(VARS_LOCK, &vars->rwlock);
Thierry FOURNIERfd77e052015-07-07 21:20:42 +0200645 return 1;
646}
647
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200648/* Always returns ACT_RET_CONT even if an error occurs. */
649static enum act_return action_store(struct act_rule *rule, struct proxy *px,
Willy Tarreau658b85b2015-09-27 10:00:49 +0200650 struct session *sess, struct stream *s, int flags)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200651{
652 struct sample smp;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200653 int dir;
654
655 switch (rule->from) {
Willy Tarreau620408f2016-10-21 16:37:51 +0200656 case ACT_F_TCP_REQ_SES: dir = SMP_OPT_DIR_REQ; break;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200657 case ACT_F_TCP_REQ_CNT: dir = SMP_OPT_DIR_REQ; break;
658 case ACT_F_TCP_RES_CNT: dir = SMP_OPT_DIR_RES; break;
659 case ACT_F_HTTP_REQ: dir = SMP_OPT_DIR_REQ; break;
660 case ACT_F_HTTP_RES: dir = SMP_OPT_DIR_RES; break;
Gaetan Rivet0c39ecc2020-02-24 17:34:11 +0100661 case ACT_F_TCP_CHK: dir = SMP_OPT_DIR_REQ; break;
Willy Tarreau01d580a2021-03-26 11:11:34 +0100662 case ACT_F_CFG_PARSER: dir = SMP_OPT_DIR_REQ; break; /* not used anyway */
Willy Tarreau2f836de2021-03-26 15:36:44 +0100663 case ACT_F_CLI_PARSER: dir = SMP_OPT_DIR_REQ; break; /* not used anyway */
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200664 default:
665 send_log(px, LOG_ERR, "Vars: internal error while execute action store.");
666 if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
Christopher Faulet767a84b2017-11-24 16:50:31 +0100667 ha_alert("Vars: internal error while execute action store.\n");
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200668 return ACT_RET_CONT;
669 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200670
671 /* Process the expression. */
672 memset(&smp, 0, sizeof(smp));
Willy Tarreau108a8fd2016-10-21 17:13:24 +0200673 if (!sample_process(px, sess, s, dir|SMP_OPT_FINAL,
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200674 rule->arg.vars.expr, &smp))
Thierry FOURNIER24ff6c62015-08-06 08:52:53 +0200675 return ACT_RET_CONT;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200676
677 /* Store the sample, and ignore errors. */
Willy Tarreau6204cd92016-03-10 16:33:04 +0100678 sample_store_stream(rule->arg.vars.name, rule->arg.vars.scope, &smp);
Thierry FOURNIER24ff6c62015-08-06 08:52:53 +0200679 return ACT_RET_CONT;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200680}
681
Christopher Faulet85d79c92016-11-09 16:54:56 +0100682/* Always returns ACT_RET_CONT even if an error occurs. */
683static enum act_return action_clear(struct act_rule *rule, struct proxy *px,
684 struct session *sess, struct stream *s, int flags)
685{
686 struct sample smp;
687
688 memset(&smp, 0, sizeof(smp));
689 smp_set_owner(&smp, px, sess, s, SMP_OPT_FINAL);
690
691 /* Clear the variable using the sample context, and ignore errors. */
692 sample_clear_stream(rule->arg.vars.name, rule->arg.vars.scope, &smp);
693 return ACT_RET_CONT;
694}
695
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200696static void release_store_rule(struct act_rule *rule)
697{
698 release_sample_expr(rule->arg.vars.expr);
699}
700
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200701/* This two function checks the variable name and replace the
702 * configuration string name by the global string name. its
703 * the same string, but the global pointer can be easy to
704 * compare.
705 *
706 * The first function checks a sample-fetch and the second
707 * checks a converter.
708 */
709static int smp_check_var(struct arg *args, char **err)
710{
711 return vars_check_arg(&args[0], err);
712}
713
714static int conv_check_var(struct arg *args, struct sample_conv *conv,
715 const char *file, int line, char **err_msg)
716{
717 return vars_check_arg(&args[0], err_msg);
718}
719
720/* This function is a common parser for using variables. It understands
721 * the format:
722 *
723 * set-var(<variable-name>) <expression>
Willy Tarreau4b7531f2019-06-04 16:43:29 +0200724 * unset-var(<variable-name>)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200725 *
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200726 * It returns ACT_RET_PRS_ERR if fails and <err> is filled with an error
727 * message. Otherwise, it returns ACT_RET_PRS_OK and the variable <expr>
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100728 * is filled with the pointer to the expression to execute. The proxy is
729 * only used to retrieve the ->conf entries.
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200730 */
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200731static enum act_parse_ret parse_store(const char **args, int *arg, struct proxy *px,
732 struct act_rule *rule, char **err)
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200733{
734 const char *var_name = args[*arg-1];
735 int var_len;
Thierry FOURNIER48a9cd12015-07-28 19:00:28 +0200736 const char *kw_name;
Willy Tarreaue3658152016-11-24 21:23:28 +0100737 int flags, set_var = 0;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200738
Willy Tarreauf6bda612021-09-02 18:46:22 +0200739 if (strncmp(var_name, "set-var", 7) == 0) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100740 var_name += 7;
741 set_var = 1;
742 }
Willy Tarreauf6bda612021-09-02 18:46:22 +0200743 else if (strncmp(var_name, "unset-var", 9) == 0) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100744 var_name += 9;
745 set_var = 0;
746 }
747
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200748 if (*var_name != '(') {
Willy Tarreau3d1d1782021-03-12 11:53:19 +0100749 memprintf(err, "invalid or incomplete action '%s'. Expects 'set-var(<var-name>)' or 'unset-var(<var-name>)'",
Christopher Faulet85d79c92016-11-09 16:54:56 +0100750 args[*arg-1]);
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200751 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200752 }
753 var_name++; /* jump the '(' */
754 var_len = strlen(var_name);
755 var_len--; /* remove the ')' */
756 if (var_name[var_len] != ')') {
Willy Tarreau3d1d1782021-03-12 11:53:19 +0100757 memprintf(err, "incomplete expression after action '%s'. Expects 'set-var(<var-name>)' or 'unset-var(<var-name>)'",
Christopher Faulet85d79c92016-11-09 16:54:56 +0100758 args[*arg-1]);
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200759 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200760 }
761
Christopher Faulet09c9df22016-10-31 11:05:37 +0100762 rule->arg.vars.name = register_name(var_name, var_len, &rule->arg.vars.scope, 1, err);
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200763 if (!rule->arg.vars.name)
764 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200765
Christopher Faulet85d79c92016-11-09 16:54:56 +0100766 /* There is no fetch method when variable is unset. Just set the right
767 * action and return. */
768 if (!set_var) {
Christopher Faulet85d79c92016-11-09 16:54:56 +0100769 rule->action = ACT_CUSTOM;
770 rule->action_ptr = action_clear;
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200771 rule->release_ptr = release_store_rule;
Christopher Faulet85d79c92016-11-09 16:54:56 +0100772 return ACT_RET_PRS_OK;
773 }
774
Thierry FOURNIER48a9cd12015-07-28 19:00:28 +0200775 kw_name = args[*arg-1];
776
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200777 rule->arg.vars.expr = sample_parse_expr((char **)args, arg, px->conf.args.file,
Willy Tarreaue3b57bf2020-02-14 16:50:14 +0100778 px->conf.args.line, err, &px->conf.args, NULL);
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200779 if (!rule->arg.vars.expr)
Thierry FOURNIERafa80492015-08-19 09:04:15 +0200780 return ACT_RET_PRS_ERR;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200781
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200782 switch (rule->from) {
Willy Tarreau620408f2016-10-21 16:37:51 +0200783 case ACT_F_TCP_REQ_SES: flags = SMP_VAL_FE_SES_ACC; break;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200784 case ACT_F_TCP_REQ_CNT: flags = SMP_VAL_FE_REQ_CNT; break;
785 case ACT_F_TCP_RES_CNT: flags = SMP_VAL_BE_RES_CNT; break;
786 case ACT_F_HTTP_REQ: flags = SMP_VAL_FE_HRQ_HDR; break;
787 case ACT_F_HTTP_RES: flags = SMP_VAL_BE_HRS_HDR; break;
Gaetan Rivet707b52f2020-02-21 18:14:59 +0100788 case ACT_F_TCP_CHK: flags = SMP_VAL_BE_CHK_RUL; break;
Willy Tarreau01d580a2021-03-26 11:11:34 +0100789 case ACT_F_CFG_PARSER: flags = SMP_VAL_CFG_PARSER; break;
Willy Tarreau2f836de2021-03-26 15:36:44 +0100790 case ACT_F_CLI_PARSER: flags = SMP_VAL_CLI_PARSER; break;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200791 default:
792 memprintf(err,
793 "internal error, unexpected rule->from=%d, please report this bug!",
794 rule->from);
Thierry FOURNIERafa80492015-08-19 09:04:15 +0200795 return ACT_RET_PRS_ERR;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200796 }
797 if (!(rule->arg.vars.expr->fetch->val & flags)) {
798 memprintf(err,
799 "fetch method '%s' extracts information from '%s', none of which is available here",
800 kw_name, sample_src_names(rule->arg.vars.expr->fetch->use));
801 free(rule->arg.vars.expr);
Thierry FOURNIERafa80492015-08-19 09:04:15 +0200802 return ACT_RET_PRS_ERR;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200803 }
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200804
Thierry FOURNIER42148732015-09-02 17:17:33 +0200805 rule->action = ACT_CUSTOM;
Thierry FOURNIER4dc15d12015-08-06 18:25:56 +0200806 rule->action_ptr = action_store;
Tim Duesterhus01a0ce32020-06-14 17:27:36 +0200807 rule->release_ptr = release_store_rule;
Thierry FOURNIERafa80492015-08-19 09:04:15 +0200808 return ACT_RET_PRS_OK;
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200809}
810
Willy Tarreau13d2ba22021-03-26 11:38:08 +0100811
812/* parses a global "set-var" directive. It will create a temporary rule and
813 * expression that are parsed, processed, and released on the fly so that we
814 * respect the real set-var syntax. These directives take the following format:
815 * set-var <name> <expression>
816 * Note that parse_store() expects "set-var(name) <expression>" so we have to
817 * temporarily replace the keyword here.
818 */
819static int vars_parse_global_set_var(char **args, int section_type, struct proxy *curpx,
820 const struct proxy *defpx, const char *file, int line,
821 char **err)
822{
823 struct proxy px = {
824 .id = "CLI",
825 .conf.args.file = file,
826 .conf.args.line = line,
827 };
828 struct act_rule rule = {
829 .arg.vars.scope = SCOPE_PROC,
830 .from = ACT_F_CFG_PARSER,
831 };
832 enum act_parse_ret p_ret;
833 char *old_arg1;
834 char *tmp_arg1;
835 int arg = 2; // variable name
836 int ret = -1;
837
838 LIST_INIT(&px.conf.args.list);
839
840 if (!*args[1] || !*args[2]) {
841 memprintf(err, "'%s' requires a process-wide variable name ('proc.<name>') and a sample expression.", args[0]);
842 goto end;
843 }
844
845 tmp_arg1 = NULL;
846 if (!memprintf(&tmp_arg1, "set-var(%s)", args[1]))
847 goto end;
848
849 /* parse_store() will always return a message in <err> on error */
850 old_arg1 = args[1]; args[1] = tmp_arg1;
851 p_ret = parse_store((const char **)args, &arg, &px, &rule, err);
852 free(args[1]); args[1] = old_arg1;
853
854 if (p_ret != ACT_RET_PRS_OK)
855 goto end;
856
857 if (rule.arg.vars.scope != SCOPE_PROC) {
858 memprintf(err, "'%s': cannot set variable '%s', only scope 'proc' is permitted in the global section.", args[0], args[1]);
859 goto end;
860 }
861
862 if (smp_resolve_args(&px, err) != 0) {
863 release_sample_expr(rule.arg.vars.expr);
864 indent_msg(err, 2);
865 goto end;
866 }
867
868 action_store(&rule, &px, NULL, NULL, 0);
869 release_sample_expr(rule.arg.vars.expr);
870
871 ret = 0;
872 end:
873 return ret;
874}
875
Willy Tarreauc35eb382021-03-26 14:51:31 +0100876/* parse CLI's "get var <name>" */
877static int vars_parse_cli_get_var(char **args, char *payload, struct appctx *appctx, void *private)
878{
879 struct vars *vars;
Willy Tarreau374edc72021-04-01 17:01:43 +0200880 struct sample smp = { };
Willy Tarreauc35eb382021-03-26 14:51:31 +0100881 int i;
882
883 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
884 return 1;
885
886 if (!*args[2])
887 return cli_err(appctx, "Missing process-wide variable identifier.\n");
888
889 vars = get_vars(NULL, NULL, SCOPE_PROC);
890 if (!vars || vars->scope != SCOPE_PROC)
891 return 0;
892
893 if (!vars_get_by_name(args[2], strlen(args[2]), &smp))
894 return cli_err(appctx, "Variable not found.\n");
895
896 /* the sample returned by vars_get_by_name() is allocated into a trash
897 * chunk so we have no constraint to manipulate it.
898 */
899 chunk_printf(&trash, "%s: type=%s value=", args[2], smp_to_type[smp.data.type]);
900
901 if (!sample_casts[smp.data.type][SMP_T_STR] ||
902 !sample_casts[smp.data.type][SMP_T_STR](&smp)) {
903 chunk_appendf(&trash, "(undisplayable)");
904 } else {
905 /* Display the displayable chars*. */
906 b_putchr(&trash, '<');
907 for (i = 0; i < smp.data.u.str.data; i++) {
908 if (isprint((unsigned char)smp.data.u.str.area[i]))
909 b_putchr(&trash, smp.data.u.str.area[i]);
910 else
911 b_putchr(&trash, '.');
912 }
913 b_putchr(&trash, '>');
914 b_putchr(&trash, 0);
915 }
916 return cli_msg(appctx, LOG_INFO, trash.area);
917}
918
Willy Tarreaub8bd1ee2021-03-26 15:19:50 +0100919/* parse CLI's "set var <name> <expression>" */
920static int vars_parse_cli_set_var(char **args, char *payload, struct appctx *appctx, void *private)
921{
922 struct proxy px = {
923 .id = "CLI",
924 .conf.args.file = "CLI",
925 .conf.args.line = 0,
926 };
927 struct act_rule rule = {
928 .arg.vars.scope = SCOPE_PROC,
929 .from = ACT_F_CLI_PARSER,
930 };
931 enum act_parse_ret p_ret;
932 char *old_arg2;
933 char *tmp_arg2;
934 char *err = NULL;
935 int arg = 2; // variable name
936 int nberr;
937
938 LIST_INIT(&px.conf.args.list);
939
940 if (!cli_has_level(appctx, ACCESS_LVL_OPER))
941 return 1;
942
943 if (!*args[2] || !*args[3])
944 return cli_err(appctx, "Missing process-wide variable identifier and expression.\n");
945
946 tmp_arg2 = NULL;
947 if (!memprintf(&tmp_arg2, "set-var(%s)", args[2])) {
948 memprintf(&err, "memory allocation error.");
949 goto fail;
950 }
951
952 /* parse_store() will always return a message in <err> on error */
953 old_arg2 = args[2]; args[2] = tmp_arg2;
954 p_ret = parse_store((const char **)(args + 1), &arg, &px, &rule, &err);
955 free(args[2]); args[2] = old_arg2;
956
957 if (p_ret != ACT_RET_PRS_OK)
958 goto fail;
959
960 if (rule.arg.vars.scope != SCOPE_PROC) {
961 memprintf(&err, "'%s %s': cannot set variable '%s', only scope 'proc' is permitted in the global section.", args[0], args[1], args[2]);
962 goto fail;
963 }
964
965 err = NULL;
966 nberr = smp_resolve_args(&px, &err);
967 if (nberr) {
968 release_sample_expr(rule.arg.vars.expr);
969 indent_msg(&err, 2);
970 goto fail;
971 }
972
973 action_store(&rule, &px, NULL, NULL, 0);
974 release_sample_expr(rule.arg.vars.expr);
975 appctx->st0 = CLI_ST_PROMPT;
976 return 0;
977 fail:
978 return cli_dynerr(appctx, err);
979}
980
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200981static int vars_max_size(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +0100982 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200983 char **err, unsigned int *limit)
984{
985 char *error;
986
987 *limit = strtol(args[1], &error, 10);
988 if (*error != 0) {
989 memprintf(err, "%s: '%s' is an invalid size", args[0], args[1]);
990 return -1;
991 }
992 return 0;
993}
994
995static int vars_max_size_global(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +0100996 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +0200997 char **err)
998{
999 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_global_limit);
1000}
1001
Christopher Fauletff2613e2016-11-09 11:36:17 +01001002static int vars_max_size_proc(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001003 const struct proxy *defpx, const char *file, int line,
Christopher Fauletff2613e2016-11-09 11:36:17 +01001004 char **err)
1005{
1006 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_proc_limit);
1007}
1008
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001009static int vars_max_size_sess(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001010 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001011 char **err)
1012{
1013 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_sess_limit);
1014}
1015
1016static int vars_max_size_txn(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001017 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001018 char **err)
1019{
1020 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_txn_limit);
1021}
1022
1023static int vars_max_size_reqres(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001024 const struct proxy *defpx, const char *file, int line,
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001025 char **err)
1026{
1027 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_reqres_limit);
1028}
1029
Gaetan Rivet13a50432020-02-21 18:13:44 +01001030static int vars_max_size_check(char **args, int section_type, struct proxy *curpx,
Willy Tarreau01825162021-03-09 09:53:46 +01001031 const struct proxy *defpx, const char *file, int line,
Gaetan Rivet13a50432020-02-21 18:13:44 +01001032 char **err)
1033{
1034 return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_check_limit);
1035}
1036
Tim Duesterhusbbdd5b82020-07-04 11:53:25 +02001037static void vars_deinit()
1038{
1039 while (var_names_nb-- > 0)
1040 free(var_names[var_names_nb]);
1041 free(var_names);
1042}
1043
1044REGISTER_POST_DEINIT(vars_deinit);
1045
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001046static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
1047
Willy Tarreau0209c972021-03-26 12:03:11 +01001048 { "var", smp_fetch_var, ARG1(1,STR), smp_check_var, SMP_T_STR, SMP_USE_CONST },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001049 { /* END */ },
1050}};
1051
Willy Tarreau0108d902018-11-25 19:14:37 +01001052INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);
1053
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001054static struct sample_conv_kw_list sample_conv_kws = {ILH, {
Christopher Faulet85d79c92016-11-09 16:54:56 +01001055 { "set-var", smp_conv_store, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
1056 { "unset-var", smp_conv_clear, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001057 { /* END */ },
1058}};
1059
Willy Tarreau0108d902018-11-25 19:14:37 +01001060INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws);
1061
Willy Tarreau620408f2016-10-21 16:37:51 +02001062static struct action_kw_list tcp_req_sess_kws = { { }, {
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001063 { "set-var", parse_store, KWF_MATCH_PREFIX },
1064 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Willy Tarreau620408f2016-10-21 16:37:51 +02001065 { /* END */ }
1066}};
1067
Willy Tarreau0108d902018-11-25 19:14:37 +01001068INITCALL1(STG_REGISTER, tcp_req_sess_keywords_register, &tcp_req_sess_kws);
1069
Willy Tarreau620408f2016-10-21 16:37:51 +02001070static struct action_kw_list tcp_req_cont_kws = { { }, {
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001071 { "set-var", parse_store, KWF_MATCH_PREFIX },
1072 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001073 { /* END */ }
1074}};
1075
Willy Tarreau0108d902018-11-25 19:14:37 +01001076INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_cont_kws);
1077
Thierry FOURNIER36481b82015-08-19 09:01:53 +02001078static struct action_kw_list tcp_res_kws = { { }, {
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001079 { "set-var", parse_store, KWF_MATCH_PREFIX },
1080 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001081 { /* END */ }
1082}};
1083
Willy Tarreau0108d902018-11-25 19:14:37 +01001084INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_kws);
1085
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001086static struct action_kw_list tcp_check_kws = {ILH, {
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001087 { "set-var", parse_store, KWF_MATCH_PREFIX },
1088 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Gaetan Rivet707b52f2020-02-21 18:14:59 +01001089 { /* END */ }
1090}};
1091
1092INITCALL1(STG_REGISTER, tcp_check_keywords_register, &tcp_check_kws);
1093
Thierry FOURNIER36481b82015-08-19 09:01:53 +02001094static struct action_kw_list http_req_kws = { { }, {
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001095 { "set-var", parse_store, KWF_MATCH_PREFIX },
1096 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001097 { /* END */ }
1098}};
1099
Willy Tarreau0108d902018-11-25 19:14:37 +01001100INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_kws);
1101
Thierry FOURNIER36481b82015-08-19 09:01:53 +02001102static struct action_kw_list http_res_kws = { { }, {
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001103 { "set-var", parse_store, KWF_MATCH_PREFIX },
1104 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001105 { /* END */ }
1106}};
1107
Willy Tarreau0108d902018-11-25 19:14:37 +01001108INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_kws);
1109
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01001110static struct action_kw_list http_after_res_kws = { { }, {
Amaury Denoyellee4a617c2021-05-06 15:33:09 +02001111 { "set-var", parse_store, KWF_MATCH_PREFIX },
1112 { "unset-var", parse_store, KWF_MATCH_PREFIX },
Christopher Faulet6d0c3df2020-01-22 09:26:35 +01001113 { /* END */ }
1114}};
1115
1116INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_kws);
1117
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001118static struct cfg_kw_list cfg_kws = {{ },{
Willy Tarreau13d2ba22021-03-26 11:38:08 +01001119 { CFG_GLOBAL, "set-var", vars_parse_global_set_var },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001120 { CFG_GLOBAL, "tune.vars.global-max-size", vars_max_size_global },
Christopher Fauletff2613e2016-11-09 11:36:17 +01001121 { CFG_GLOBAL, "tune.vars.proc-max-size", vars_max_size_proc },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001122 { CFG_GLOBAL, "tune.vars.sess-max-size", vars_max_size_sess },
1123 { CFG_GLOBAL, "tune.vars.txn-max-size", vars_max_size_txn },
1124 { CFG_GLOBAL, "tune.vars.reqres-max-size", vars_max_size_reqres },
Gaetan Rivet13a50432020-02-21 18:13:44 +01001125 { CFG_GLOBAL, "tune.vars.check-max-size", vars_max_size_check },
Thierry FOURNIER4834bc72015-06-06 19:29:07 +02001126 { /* END */ }
1127}};
1128
Willy Tarreau0108d902018-11-25 19:14:37 +01001129INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
Willy Tarreauc35eb382021-03-26 14:51:31 +01001130
1131
1132/* register cli keywords */
1133static struct cli_kw_list cli_kws = {{ },{
Willy Tarreaub205bfd2021-05-07 11:38:37 +02001134 { { "get", "var", NULL }, "get var <name> : retrieve contents of a process-wide variable", vars_parse_cli_get_var, NULL },
1135 { { "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 +01001136 { { NULL }, NULL, NULL, NULL }
1137}};
1138INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);