Coverage Report

Created: 2026-10-13 06:32

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/haproxy/src/vars.c
Line
Count
Source
1
#include <ctype.h>
2
3
#include <haproxy/api.h>
4
#include <haproxy/arg.h>
5
#include <haproxy/buf.h>
6
#include <haproxy/cfgparse.h>
7
#include <haproxy/check.h>
8
#include <haproxy/cli.h>
9
#include <haproxy/global.h>
10
#include <haproxy/http.h>
11
#include <haproxy/http_rules.h>
12
#include <haproxy/intops.h>
13
#include <haproxy/list.h>
14
#include <haproxy/pattern.h>
15
#include <haproxy/log.h>
16
#include <haproxy/sample.h>
17
#include <haproxy/session.h>
18
#include <haproxy/stream-t.h>
19
#include <haproxy/tcp_rules.h>
20
#include <haproxy/tcpcheck.h>
21
#include <haproxy/tools.h>
22
#include <haproxy/vars.h>
23
#include <haproxy/xxhash.h>
24
25
#include <import/ceb64_tree.h>
26
27
28
/* This contains a pool of struct vars */
29
DECLARE_STATIC_TYPED_POOL(var_pool, "vars", struct var);
30
31
/* list of variables for the process scope. */
32
struct vars proc_vars THREAD_ALIGNED();
33
34
/* This array of int contains the system limits per context. */
35
static unsigned int var_global_limit = 0;
36
static unsigned int var_proc_limit = 0;
37
static unsigned int var_sess_limit = 0;
38
static unsigned int var_txn_limit = 0;
39
static unsigned int var_reqres_limit = 0;
40
static unsigned int var_check_limit = 0;
41
static uint64_t var_name_hash_seed = 0;
42
static unsigned int svfm_max = 100; /* Max number of entries processed in set-vars-from-map */
43
44
/* Structure and array matching set-var conditions to their respective flag
45
 * value.
46
 */
47
struct var_set_condition {
48
       const char *cond_str;
49
       uint flag;
50
};
51
52
static struct var_set_condition conditions_array[] = {
53
       { "ifexists", VF_COND_IFEXISTS },
54
       { "ifnotexists", VF_COND_IFNOTEXISTS },
55
       { "ifempty", VF_COND_IFEMPTY },
56
       { "ifnotempty", VF_COND_IFNOTEMPTY },
57
       { "ifset", VF_COND_IFSET },
58
       { "ifnotset", VF_COND_IFNOTSET },
59
       { "ifgt", VF_COND_IFGT },
60
       { "iflt", VF_COND_IFLT },
61
       { NULL, 0 }
62
};
63
64
/* Variable scope names with their prefixes for output */
65
static const char *var_scope_names[] = {
66
  [SCOPE_SESS] = "sess.",
67
  [SCOPE_TXN]  = "txn.",
68
  [SCOPE_REQ]  = "req.",
69
  [SCOPE_RES]  = "res.",
70
  [SCOPE_PROC] = "proc.",
71
  [SCOPE_CHECK] = "check.",
72
};
73
74
/* returns the struct vars pointer for a session, stream and scope, or NULL if
75
 * it does not exist.
76
 */
77
static inline struct vars *get_vars(struct session *sess, struct stream *strm, const struct var_desc *desc)
78
0
{
79
0
  if (!desc)
80
0
    return NULL;
81
82
0
  if (desc->flags & VDF_PARENT_CTX) {
83
0
    if (!strm || !strm->parent)
84
0
      return NULL;
85
0
    strm = strm->parent;
86
0
    sess = strm_sess(strm);
87
0
  }
88
89
0
  switch (desc->scope) {
90
0
  case SCOPE_PROC:
91
0
    return &proc_vars;
92
0
  case SCOPE_SESS:
93
0
    return sess ? &sess->vars : NULL;
94
0
  case SCOPE_CHECK: {
95
0
      struct check *check = sess ? objt_check(sess->origin) : NULL;
96
97
0
      return check ? &check->vars : NULL;
98
0
    }
99
0
  case SCOPE_TXN:
100
0
    return strm ? &strm->vars_txn : NULL;
101
0
  case SCOPE_REQ:
102
0
  case SCOPE_RES:
103
0
  default:
104
0
    return strm ? &strm->vars_reqres : NULL;
105
0
  }
106
0
}
107
108
/* This function adds or remove memory size from the accounting. The inner
109
 * pointers may be null when setting the outer ones only.
110
 */
111
void var_accounting_diff(struct vars *vars, struct session *sess, struct stream *strm, int size)
112
0
{
113
0
  switch (vars->scope) {
114
0
  case SCOPE_REQ:
115
0
  case SCOPE_RES:
116
0
    if (var_reqres_limit && strm)
117
0
      _HA_ATOMIC_ADD(&strm->vars_reqres.size, size);
118
0
    __fallthrough;
119
0
  case SCOPE_TXN:
120
0
    if (var_txn_limit && strm)
121
0
      _HA_ATOMIC_ADD(&strm->vars_txn.size, size);
122
0
    goto scope_sess;
123
0
  case SCOPE_CHECK:
124
0
    if (var_check_limit) {
125
0
      struct check *check = objt_check(sess->origin);
126
127
0
      if (check)
128
0
        _HA_ATOMIC_ADD(&check->vars.size, size);
129
0
    }
130
0
scope_sess:
131
0
    __fallthrough;
132
0
  case SCOPE_SESS:
133
0
    if (var_sess_limit)
134
0
      _HA_ATOMIC_ADD(&sess->vars.size, size);
135
0
    __fallthrough;
136
0
  case SCOPE_PROC:
137
0
    if (var_proc_limit || var_global_limit)
138
0
      _HA_ATOMIC_ADD(&proc_vars.size, size);
139
0
  }
140
0
}
141
142
/* This function returns 1 if the <size> is available in the var
143
 * pool <vars>, otherwise returns 0. If the space is available,
144
 * the size is reserved. The inner pointers may be null when setting
145
 * the outer ones only. The accounting uses either <sess> or <strm>
146
 * depending on the scope. <strm> may be NULL when no stream is known
147
 * and only the session exists (eg: tcp-request connection).
148
 */
149
static int var_accounting_add(struct vars *vars, struct session *sess, struct stream *strm, int size)
150
0
{
151
0
  switch (vars->scope) {
152
0
  case SCOPE_REQ:
153
0
  case SCOPE_RES:
154
0
    if (var_reqres_limit && strm && strm->vars_reqres.size + size > var_reqres_limit)
155
0
      return 0;
156
0
    __fallthrough;
157
0
  case SCOPE_TXN:
158
0
    if (var_txn_limit && strm && strm->vars_txn.size + size > var_txn_limit)
159
0
      return 0;
160
0
    goto scope_sess;
161
0
  case SCOPE_CHECK: {
162
0
      struct check *check = objt_check(sess->origin);
163
164
0
      if (var_check_limit && check && check->vars.size + size > var_check_limit)
165
0
        return 0;
166
0
    }
167
0
scope_sess:
168
0
    __fallthrough;
169
0
  case SCOPE_SESS:
170
0
    if (var_sess_limit && sess->vars.size + size > var_sess_limit)
171
0
      return 0;
172
0
    __fallthrough;
173
0
  case SCOPE_PROC:
174
    /* note: scope proc collects all others and is currently identical to the
175
     * global limit.
176
     */
177
0
    if (var_proc_limit && proc_vars.size + size > var_proc_limit)
178
0
      return 0;
179
0
    if (var_global_limit && proc_vars.size + size > var_global_limit)
180
0
      return 0;
181
0
  }
182
0
  var_accounting_diff(vars, sess, strm, size);
183
0
  return 1;
184
0
}
185
186
/*
187
 * Parse the scope part of a variable name starting at <scope_name> and compare
188
 * it to the "regular" scope names (see var_scope_names array). The scope part
189
 * ends at the first '.' or at the end of the string. If provided, the <end>
190
 * pointer is set past the scope, i.e. after the '.' if there is one, or on the
191
 * terminating nul byte otherwise.
192
 * Returns -1 in case of error, a vars_scope value otherwise.
193
 */
194
static int var_parse_scope_str(const char *scope_name, const char **end)
195
0
{
196
0
  const char *dot;
197
0
  size_t len;
198
199
  /* The scope ends at the first '.' or at the end of the string. */
200
0
  for (dot = scope_name; *dot && *dot != '.'; dot++)
201
0
    ;
202
203
0
  len = dot - scope_name;
204
205
0
  if (end)
206
0
    *end = *dot ? dot + 1 : dot;
207
208
0
  if (len == 3 && strncmp(scope_name, "req", 3) == 0)
209
0
    return SCOPE_REQ;
210
0
  if (len == 3 && strncmp(scope_name, "res", 3) == 0)
211
0
    return SCOPE_RES;
212
0
  if (len == 3 && strncmp(scope_name, "txn", 3) == 0)
213
0
    return SCOPE_TXN;
214
0
  if (len == 4 && strncmp(scope_name, "proc", 4) == 0)
215
0
    return SCOPE_PROC;
216
0
  if (len == 4 && strncmp(scope_name, "sess", 4) == 0)
217
0
    return SCOPE_SESS;
218
0
  if (len == 5 && strncmp(scope_name, "check", 5) == 0)
219
0
    return SCOPE_CHECK;
220
221
0
  return -1;
222
0
}
223
224
/* This function removes a variable from the list and frees the memory it was
225
 * using. If the variable is marked "VF_PERMANENT", the sample_data is only
226
 * reset to SMP_T_ANY unless <force> is non nul. Returns the freed size.
227
 */
228
unsigned int var_clear(struct vars *vars, struct var *var, int force)
229
0
{
230
0
  unsigned int size = 0;
231
232
0
  ha_free(&var->name);
233
234
0
  if (var->data.type == SMP_T_STR || var->data.type == SMP_T_BIN) {
235
0
    ha_free(&var->data.u.str.area);
236
0
    size += var->data.u.str.data;
237
0
  }
238
0
  else if (var->data.type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
239
0
    ha_free(&var->data.u.meth.str.area);
240
0
    size += var->data.u.meth.str.data;
241
0
  }
242
  /* wipe the sample */
243
0
  var->data.type = SMP_T_ANY;
244
245
0
  if (!(var->flags & VF_PERMANENT) || force) {
246
0
    cebu64_item_delete(&vars->name_root[var->name_hash % VAR_NAME_ROOTS],
247
0
           name_node, name_hash, var);
248
0
    pool_free(var_pool, var);
249
0
    size += sizeof(struct var);
250
0
  }
251
0
  return size;
252
0
}
253
254
/* This function frees all the memory used by all the session variables in the
255
 * list starting at <vars>.
256
 */
257
void vars_prune_per_sess(struct vars *vars)
258
0
{
259
0
  struct var *var;
260
0
  unsigned int size = 0;
261
0
  int i;
262
263
0
  for (i = 0; i < VAR_NAME_ROOTS; i++) {
264
0
    while ((var = cebu64_item_first(&vars->name_root[i], name_node, name_hash, struct var)))
265
0
      size += var_clear(vars, var, 1);
266
0
  }
267
268
0
  if (!size)
269
0
    return;
270
271
0
  if (var_sess_limit)
272
0
    _HA_ATOMIC_SUB(&vars->size, size);
273
0
  if (var_proc_limit || var_global_limit)
274
0
    _HA_ATOMIC_SUB(&proc_vars.size, size);
275
0
}
276
277
/* This function initializes a variables list head */
278
void vars_init_head(struct vars *vars, enum vars_scope scope)
279
0
{
280
0
  memset(vars->name_root, 0, sizeof(vars->name_root));
281
0
  vars->scope = scope;
282
0
  vars->size = 0;
283
0
  HA_RWLOCK_INIT(&vars->rwlock);
284
0
}
285
286
/* Returns non-zero if <name>, of length <len>, is a valid variable name part,
287
 * i.e. the part following the "<scope>." prefix. A valid part must not be
288
 * empty and may only contain alphanumerics, '_' and '.'. When <off> is not
289
 * NULL, it is set to the offset of the first invalid character, or to <len>
290
 * when the whole name is valid, so that callers may report it.
291
 */
292
static int var_name_is_valid(const char *name, size_t len, size_t *off)
293
0
{
294
0
  size_t i;
295
296
0
  for (i = 0; i < len; i++) {
297
0
    if (!isalnum((unsigned char)name[i]) && name[i] != '_' && name[i] != '.') {
298
0
      if (off)
299
0
        *off = i;
300
0
      return 0;
301
0
    }
302
0
  }
303
304
0
  if (off)
305
0
    *off = len;
306
0
  return len != 0;
307
0
}
308
309
/* This function returns the description (a var_desc structure) of a variable
310
 * name of a specified length. It makes sure that the scope is valid. It fills
311
 * <desc> passed as parameter and returns non-zero on success, 0 on
312
 * failure. Neither hash nor scope may be NULL.
313
 */
314
static int vars_fill_desc(const char *name, int len, struct var_desc *desc, char **err)
315
0
{
316
0
  const char *endptr = NULL;
317
0
  size_t off;
318
319
  /* Check name and length. */
320
0
  if (name == NULL || len == 0) {
321
0
    memprintf(err, "Empty variable name cannot be accepted");
322
0
    return 0;
323
0
  }
324
  /* Check desc */
325
0
  if (desc == NULL) {
326
0
    memprintf(err, "Invalid variable description");
327
0
    return 0;
328
0
  }
329
330
0
  desc->flags = 0;
331
332
  /* Check scope including those related to the parent stream (prefixed by ('p'). */
333
0
  if ((desc->scope = var_parse_scope_str(name, &endptr)) != -1) {
334
    /* The scope must be followed by a '.' and a non-empty name */
335
0
    if (!endptr || !*endptr)
336
0
      goto err;
337
0
    len -= (endptr-name);
338
0
    name = endptr;
339
0
    if (len <= 0)
340
0
      goto err;
341
0
  } else if (len > 6 && strncmp(name, "psess.", 6) == 0) {
342
0
    name += 6;
343
0
    len -= 6;
344
0
    desc->scope = SCOPE_SESS;
345
0
    desc->flags |= VDF_PARENT_CTX;
346
0
  }
347
0
  else if (len > 5 && strncmp(name, "ptxn.", 5) == 0) {
348
0
    name += 5;
349
0
    len -= 5;
350
0
    desc->scope = SCOPE_TXN;
351
0
    desc->flags |= VDF_PARENT_CTX;
352
0
  }
353
0
  else if (len > 5 && strncmp(name, "preq.", 5) == 0) {
354
0
    name += 5;
355
0
    len -= 5;
356
0
    desc->scope = SCOPE_REQ;
357
0
    desc->flags |= VDF_PARENT_CTX;
358
0
  }
359
0
  else if (len > 5 && strncmp(name, "pres.", 5) == 0) {
360
0
    name += 5;
361
0
    len -= 5;
362
0
    desc->scope = SCOPE_RES;
363
0
    desc->flags |= VDF_PARENT_CTX;
364
0
  } else
365
0
    goto err;
366
367
  /* Check variable name syntax. */
368
0
  if (!var_name_is_valid(name, len, &off)) {
369
0
    if (off < (size_t)len)
370
0
      memprintf(err, "Invalid character '%c' in variable name %.*s", name[off],
371
0
          len, name);
372
0
    goto err;
373
0
  }
374
375
0
  desc->name_hash = XXH3(name, len, var_name_hash_seed);
376
0
  desc->name = name;
377
0
  desc->name_len = len;
378
0
  return 1;
379
380
0
err:
381
0
  if (!err)
382
0
    memprintf(err, "invalid variable name '%.*s'. A variable name must start with its scope. "
383
0
        "The scope can be 'proc', '(p)sess', '(p)txn', '(p)req', '(p)res' or 'check'", len, name);
384
0
  return 0;
385
0
}
386
387
/* This function returns the variable from the given list that matches
388
 * <name_hash> or returns NULL if not found. It's only a linked list since it
389
 * is not expected to have many variables per scope (a few tens at best).
390
 * The caller is responsible for ensuring that <vars> is properly locked.
391
 */
392
static struct var *var_get(struct vars *vars, uint64_t name_hash)
393
0
{
394
0
  return cebu64_item_lookup(&vars->name_root[name_hash % VAR_NAME_ROOTS],
395
0
          name_node, name_hash, name_hash, struct var);
396
0
}
397
398
/* Returns 0 if fails, else returns 1. */
399
static int smp_fetch_var(const struct arg *args, struct sample *smp, const char *kw, void *private)
400
0
{
401
0
  const struct var_desc *var_desc = &args[0].data.var;
402
0
  const struct buffer *def = NULL;
403
404
0
  if (args[1].type == ARGT_STR)
405
0
    def = &args[1].data.str;
406
407
0
  return vars_get_by_desc(var_desc, smp, def);
408
0
}
409
410
/* Dumps all variables in the specified scope, optionally filtered by prefix.
411
 * Output format: var1=value1, var2=value2, ...
412
 * String values are quoted and escaped, binary values are hex-encoded (x...).
413
 * Returns 1 on success, 0 on failure (buffer too small).
414
 * Note: When using prefix filtering, all variables are still visited, so this
415
 * should not be used with configs involving thousands of variables.
416
 */
417
static int smp_fetch_dump_all_vars(const struct arg *args, struct sample *smp, const char *kw, void *private)
418
0
{
419
0
  struct buffer *output;
420
0
  struct vars *vars;
421
0
  struct var *var;
422
0
  struct var_desc desc;
423
0
  const char *prefix = NULL;
424
0
  size_t prefix_len = 0;
425
0
  const char *delim = ", ";
426
0
  size_t delim_len = 2;
427
0
  int first = 1;
428
0
  int i;
429
0
  int start_scope, end_scope;
430
0
  int cur_scope;
431
432
  /* Get output buffer */
433
0
  output = get_trash_chunk();
434
0
  chunk_reset(output);
435
436
  /* Parse arguments */
437
0
  if (args[0].type == ARGT_SINT) {
438
0
    if (args[0].data.sint == -1) {
439
0
      start_scope = SCOPE_SESS;
440
0
      end_scope = SCOPE_PROC;
441
0
    } else {
442
0
      start_scope = end_scope = args[0].data.sint;
443
0
    }
444
0
  } else {
445
    /* Auto-detect scope from context */
446
0
    if (smp->strm)
447
0
      start_scope = end_scope = SCOPE_TXN;
448
0
    else if (smp->sess)
449
0
      start_scope = end_scope = SCOPE_SESS;
450
0
    else
451
0
      start_scope = end_scope = SCOPE_PROC;
452
0
  }
453
454
  /* Optional prefix filter */
455
0
  if (args[1].type == ARGT_STR) {
456
0
    prefix = args[1].data.str.area;
457
0
    prefix_len = args[1].data.str.data;
458
0
  }
459
460
  /* Optional delimiter */
461
0
  if (args[2].type == ARGT_STR) {
462
0
    delim = args[2].data.str.area;
463
0
    delim_len = args[2].data.str.data;
464
0
  }
465
466
0
  desc.flags = 0;
467
0
  desc.name_hash = 0;
468
469
0
  for (cur_scope = start_scope; cur_scope <= end_scope; cur_scope++) {
470
0
    desc.scope = cur_scope;
471
472
0
    vars = get_vars(smp->sess, smp->strm, &desc);
473
0
    if (!vars || vars->scope != desc.scope)
474
0
      continue;
475
476
0
    vars_rdlock(vars);
477
478
    /* Iterate through all variable roots */
479
0
    for (i = 0; i < VAR_NAME_ROOTS; i++) {
480
0
      var = cebu64_item_first(&vars->name_root[i], name_node, name_hash, struct var);
481
482
0
      while (var) {
483
0
        const char *scope_prefix;
484
485
        /* Check prefix filter */
486
0
        if (prefix) {
487
0
          if (!var->name || strncmp(var->name, prefix, prefix_len) != 0) {
488
0
            var = cebu64_item_next(&vars->name_root[i], name_node, name_hash, var);
489
0
            continue;
490
0
          }
491
0
        }
492
493
        /* Add delimiter */
494
0
        if (!first) {
495
0
          if (output->data + delim_len >= output->size)
496
0
            goto fail_unlock;
497
0
          chunk_memcat(output, delim, delim_len);
498
0
        }
499
0
        first = 0;
500
501
        /* Add variable name with scope prefix */
502
0
        scope_prefix = var_scope_names[desc.scope];
503
0
        if (var->name) {
504
0
          if (chunk_appendf(output, "%s%s=", scope_prefix, var->name) < 0)
505
0
            goto fail_unlock;
506
0
        } else {
507
0
          if (chunk_appendf(output, "var_%016llx=", (unsigned long long)var->name_hash) < 0)
508
0
            goto fail_unlock;
509
0
        }
510
511
        /* Convert value based on type */
512
0
        if (var->data.type == SMP_T_STR) {
513
          /* String: quote and escape */
514
0
          if (chunk_escape_string(output, var->data.u.str.area, var->data.u.str.data) < 0)
515
0
            goto fail_unlock;
516
517
0
        } else if (var->data.type == SMP_T_BIN) {
518
          /* Binary: hex encode */
519
0
          if (dump_binary(output, var->data.u.str.area, var->data.u.str.data) != var->data.u.str.data)
520
0
            goto fail_unlock;
521
0
        } else if (var->data.type == SMP_T_SINT) {
522
          /* Integer */
523
0
          if (chunk_appendf(output, "%lld", (long long)var->data.u.sint) < 0)
524
0
            goto fail_unlock;
525
526
0
        } else if (var->data.type == SMP_T_BOOL) {
527
          /* Boolean */
528
0
          const char *bool_str = var->data.u.sint ? "true" : "false";
529
0
          if (chunk_appendf(output, "%s", bool_str) < 0)
530
0
            goto fail_unlock;
531
532
0
        } else if (var->data.type == SMP_T_IPV4 || var->data.type == SMP_T_IPV6) {
533
          /* Address */
534
0
          char addr_str[INET6_ADDRSTRLEN];
535
0
          const char *res;
536
537
0
          if (var->data.type == SMP_T_IPV4)
538
0
            res = inet_ntop(AF_INET, &var->data.u.ipv4, addr_str, sizeof(addr_str));
539
0
          else
540
0
            res = inet_ntop(AF_INET6, &var->data.u.ipv6, addr_str, sizeof(addr_str));
541
542
0
          if (!res) {
543
0
            if (chunk_appendf(output, "(addr)") < 0)
544
0
              goto fail_unlock;
545
0
          } else {
546
0
            if (chunk_appendf(output, "%s", addr_str) < 0)
547
0
              goto fail_unlock;
548
0
          }
549
0
        } else if (var->data.type == SMP_T_METH) {
550
          /* HTTP Method */
551
0
          if (var->data.u.meth.meth == HTTP_METH_OTHER) {
552
0
            if (chunk_escape_string(output, var->data.u.meth.str.area, var->data.u.meth.str.data) < 0)
553
0
              goto fail_unlock;
554
0
          } else {
555
0
            const char *method_str = http_known_methods[var->data.u.meth.meth].ptr;
556
0
            if (chunk_appendf(output, "\"%s\"", method_str) < 0)
557
0
              goto fail_unlock;
558
0
          }
559
0
        } else {
560
          /* Other types: show type number */
561
0
          if (chunk_appendf(output, "(type:%d)", var->data.type) < 0)
562
0
            goto fail_unlock;
563
0
        }
564
565
0
        var = cebu64_item_next(&vars->name_root[i], name_node, name_hash, var);
566
0
      }
567
0
    }
568
569
0
    vars_rdunlock(vars);
570
0
  }
571
572
  /* Set output sample */
573
0
  smp->data.type = SMP_T_STR;
574
0
  smp->data.u.str.area = output->area;
575
0
  smp->data.u.str.data = output->data;
576
0
  smp->flags &= ~SMP_F_CONST;
577
578
0
  return 1;
579
580
0
fail_unlock:
581
0
  vars_rdunlock(vars);
582
0
  output->data = 0;
583
0
  return 0;
584
0
}
585
586
/*
587
 * Clear the contents of a variable so that it can be reset directly.
588
 * This function is used just before a variable is filled out of a sample's
589
 * content.
590
 */
591
static inline void var_clear_buffer(struct sample *smp, struct vars *vars, struct var *var, int var_type)
592
0
{
593
0
  if (var_type == SMP_T_STR || var_type == SMP_T_BIN) {
594
0
    ha_free(&var->data.u.str.area);
595
0
    var_accounting_diff(vars, smp->sess, smp->strm,
596
0
                        -var->data.u.str.data);
597
0
  }
598
0
  else if (var_type == SMP_T_METH && var->data.u.meth.meth == HTTP_METH_OTHER) {
599
0
    ha_free(&var->data.u.meth.str.area);
600
0
    var_accounting_diff(vars, smp->sess, smp->strm,
601
0
                        -var->data.u.meth.str.data);
602
0
  }
603
0
}
604
605
/* This function tries to create a variable whose name hash is <name_hash> in
606
 * scope <scope> and store sample <smp> as its value.
607
 *
608
 * The stream and session are extracted from <smp>, whose stream may be NULL
609
 * when scope is SCOPE_SESS. In case there wouldn't be enough memory to store
610
 * the sample while the variable was already created, it would be changed to
611
 * a bool (which is memory-less).
612
 *
613
 * Flags is a bitfield that may contain one of the following flags:
614
 *   - VF_CREATEONLY: do nothing if the variable already exists (success).
615
 *   - VF_PERMANENT: this flag will be passed to the variable upon creation
616
 *
617
 *   - VF_COND_IFEXISTS: only set variable if it already exists
618
 *   - VF_COND_IFNOTEXISTS: only set variable if it did not exist yet
619
 *   - VF_COND_IFEMPTY: only set variable if sample is empty
620
 *   - VF_COND_IFNOTEMPTY: only set variable if sample is not empty
621
 *   - VF_COND_IFSET: only set variable if its type is not SMP_TYPE_ANY
622
 *   - VF_COND_IFNOTSET: only set variable if its type is ANY
623
 *   - VF_COND_IFGT: only set variable if its value is greater than the sample's
624
 *   - VF_COND_IFLT: only set variable if its value is less than the sample's
625
 *
626
 * It returns 0 on failure, non-zero on success.
627
 */
628
int var_set(const struct var_desc *desc, struct sample *smp, uint flags)
629
0
{
630
0
  struct vars *vars;
631
0
  struct var *var;
632
0
  int ret = 0;
633
0
  int previous_type = SMP_T_ANY;
634
635
0
  if (!desc || (desc->flags & VDF_PARENT_CTX))
636
0
    return 0;
637
638
0
  vars = get_vars(smp->sess, smp->strm, desc);
639
0
  if (!vars || vars->scope != desc->scope)
640
0
    return 0;
641
642
0
  vars_wrlock(vars);
643
644
  /* Look for existing variable name. */
645
0
  var = var_get(vars, desc->name_hash);
646
647
0
  if (var) {
648
0
    if (flags & VF_CREATEONLY) {
649
0
      ret = 1;
650
0
      goto unlock;
651
0
    }
652
653
0
    if (flags & VF_COND_IFNOTEXISTS)
654
0
      goto unlock;
655
0
  } else {
656
0
    if (flags & VF_COND_IFEXISTS)
657
0
      goto unlock;
658
659
    /* Check memory available. */
660
0
    if (!var_accounting_add(vars, smp->sess, smp->strm, sizeof(struct var)))
661
0
      goto unlock;
662
663
    /* Create new entry. */
664
0
    var = pool_alloc(var_pool);
665
0
    if (!var)
666
0
      goto unlock;
667
0
    var->name_hash = desc->name_hash;
668
0
    var->flags = flags & VF_PERMANENT;
669
670
    /* Save variable name */
671
0
    var->name = NULL;
672
0
    if (desc->name && desc->name_len > 0) {
673
0
      var->name = my_strndup(desc->name, desc->name_len);
674
0
      if (!var->name) {
675
0
        pool_free(var_pool, var);
676
0
        goto unlock;
677
0
      }
678
0
    }
679
0
    var->data.type = SMP_T_ANY;
680
0
    cebu64_item_insert(&vars->name_root[var->name_hash % VAR_NAME_ROOTS], name_node, name_hash, var);
681
0
  }
682
683
  /* A variable of type SMP_T_ANY is considered as unset (either created
684
   * and never set or unset-var was called on it).
685
   */
686
0
  if ((flags & VF_COND_IFSET && var->data.type == SMP_T_ANY) ||
687
0
      (flags & VF_COND_IFNOTSET && var->data.type != SMP_T_ANY))
688
0
    goto unlock;
689
690
  /* Set type. */
691
0
  previous_type = var->data.type;
692
0
  var->data.type = smp->data.type;
693
694
0
  if (flags & VF_COND_IFEMPTY) {
695
0
    switch(smp->data.type) {
696
0
    case SMP_T_ANY:
697
0
    case SMP_T_STR:
698
0
    case SMP_T_BIN:
699
      /* The actual test on the contents of the sample will be
700
       * performed later.
701
       */
702
0
      break;
703
0
    default:
704
      /* The sample cannot be empty since it has a scalar type. */
705
0
      var->data.type = previous_type;
706
0
      goto unlock;
707
0
    }
708
0
  }
709
710
  /* Copy data. If the data needs memory, the function can fail. */
711
0
  switch (var->data.type) {
712
0
  case SMP_T_BOOL:
713
0
    var_clear_buffer(smp, vars, var, previous_type);
714
0
    var->data.u.sint = smp->data.u.sint;
715
0
    break;
716
0
  case SMP_T_SINT:
717
0
    if (previous_type == var->data.type) {
718
0
      if (((flags & VF_COND_IFGT) && !(var->data.u.sint > smp->data.u.sint)) ||
719
0
          ((flags & VF_COND_IFLT) && !(var->data.u.sint < smp->data.u.sint)))
720
0
        goto unlock;
721
0
    }
722
0
    var_clear_buffer(smp, vars, var, previous_type);
723
0
    var->data.u.sint = smp->data.u.sint;
724
0
    break;
725
0
  case SMP_T_IPV4:
726
0
    var_clear_buffer(smp, vars, var, previous_type);
727
0
    var->data.u.ipv4 = smp->data.u.ipv4;
728
0
    break;
729
0
  case SMP_T_IPV6:
730
0
    var_clear_buffer(smp, vars, var, previous_type);
731
0
    var->data.u.ipv6 = smp->data.u.ipv6;
732
0
    break;
733
0
  case SMP_T_STR:
734
0
  case SMP_T_BIN:
735
0
    if ((flags & VF_COND_IFNOTEMPTY && !smp->data.u.str.data) ||
736
0
        (flags & VF_COND_IFEMPTY && smp->data.u.str.data)) {
737
0
      var->data.type = previous_type;
738
0
      goto unlock;
739
0
    }
740
0
    var_clear_buffer(smp, vars, var, previous_type);
741
0
    if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.str.data)) {
742
0
      var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
743
0
      goto unlock;
744
0
    }
745
746
0
    var->data.u.str.area = malloc(smp->data.u.str.data);
747
0
    if (!var->data.u.str.area) {
748
0
      var_accounting_diff(vars, smp->sess, smp->strm,
749
0
              -smp->data.u.str.data);
750
0
      var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
751
0
      goto unlock;
752
0
    }
753
0
    var->data.u.str.data = smp->data.u.str.data;
754
0
    memcpy(var->data.u.str.area, smp->data.u.str.area,
755
0
           var->data.u.str.data);
756
0
    break;
757
0
  case SMP_T_METH:
758
0
    var_clear_buffer(smp, vars, var, previous_type);
759
0
    var->data.u.meth.meth = smp->data.u.meth.meth;
760
0
    if (smp->data.u.meth.meth != HTTP_METH_OTHER)
761
0
      break;
762
763
0
    if (!var_accounting_add(vars, smp->sess, smp->strm, smp->data.u.meth.str.data)) {
764
0
      var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
765
0
      goto unlock;
766
0
    }
767
768
0
    var->data.u.meth.str.area = malloc(smp->data.u.meth.str.data);
769
0
    if (!var->data.u.meth.str.area) {
770
0
      var_accounting_diff(vars, smp->sess, smp->strm,
771
0
              -smp->data.u.meth.str.data);
772
0
      var->data.type = SMP_T_BOOL; /* This type doesn't use additional memory. */
773
0
      goto unlock;
774
0
    }
775
0
    var->data.u.meth.str.data = smp->data.u.meth.str.data;
776
0
    var->data.u.meth.str.size = smp->data.u.meth.str.data;
777
0
    memcpy(var->data.u.meth.str.area, smp->data.u.meth.str.area,
778
0
           var->data.u.meth.str.data);
779
0
    break;
780
0
  }
781
782
  /* OK, now done */
783
0
  ret = 1;
784
0
 unlock:
785
0
  vars_wrunlock(vars);
786
0
  return ret;
787
0
}
788
789
/* Deletes a variable matching name hash <name_hash> and scope <scope> for the
790
 * session and stream found in <smp>. Note that stream may be null for
791
 * SCOPE_SESS. Returns 0 if the scope was not found otherwise 1.
792
 */
793
int var_unset(const struct var_desc *desc, struct sample *smp)
794
0
{
795
0
  struct vars *vars;
796
0
  struct var  *var;
797
0
  unsigned int size = 0;
798
799
0
  if (!desc || (desc->flags & VDF_PARENT_CTX))
800
0
    return 0;
801
802
0
  vars = get_vars(smp->sess, smp->strm, desc);
803
0
  if (!vars || vars->scope != desc->scope)
804
0
    return 0;
805
806
  /* Look for existing variable name. */
807
0
  vars_wrlock(vars);
808
0
  var = var_get(vars, desc->name_hash);
809
0
  if (var) {
810
0
    size = var_clear(vars, var, 0);
811
0
    var_accounting_diff(vars, smp->sess, smp->strm, -size);
812
0
  }
813
0
  vars_wrunlock(vars);
814
0
  return 1;
815
0
}
816
817
818
/*
819
 * Convert a string set-var condition into its numerical value.
820
 * The corresponding bit is set in the <cond_bitmap> parameter if the
821
 * <cond> is known.
822
 * Returns 1 in case of success.
823
 */
824
static int vars_parse_cond_param(const struct buffer *cond, uint *cond_bitmap, char **err)
825
0
{
826
0
  struct var_set_condition *cond_elt = &conditions_array[0];
827
828
  /* The conditions array is NULL terminated. */
829
0
  while (cond_elt->cond_str) {
830
0
    if (chunk_strcmp(cond, cond_elt->cond_str) == 0) {
831
0
      *cond_bitmap |= cond_elt->flag;
832
0
      break;
833
0
    }
834
0
    ++cond_elt;
835
0
  }
836
837
0
  if (cond_elt->cond_str == NULL && err)
838
0
    memprintf(err, "unknown condition \"%.*s\"", (int)cond->data, cond->area);
839
840
0
  return cond_elt->cond_str != NULL;
841
0
}
842
843
/* Returns 0 if fails, else returns 1. */
844
static int smp_conv_store(const struct arg *args, struct sample *smp, void *private)
845
0
{
846
0
  uint conditions = 0;
847
0
  int cond_idx = 1;
848
849
0
  while (args[cond_idx].type == ARGT_STR) {
850
0
    if (vars_parse_cond_param(&args[cond_idx++].data.str, &conditions, NULL) == 0)
851
0
      break;
852
0
  }
853
854
0
  return var_set(&args[0].data.var, smp, conditions);
855
0
}
856
857
/* Returns 0 if fails, else returns 1. */
858
static int smp_conv_clear(const struct arg *args, struct sample *smp, void *private)
859
0
{
860
0
  return var_unset(&args[0].data.var, smp);
861
0
}
862
863
/* This functions check an argument entry and fill it with a variable
864
 * type. The argument must be a string. If the variable lookup fails,
865
 * the function returns 0 and fill <err>, otherwise it returns 1.
866
 */
867
int vars_check_arg(struct arg *arg, char **err)
868
0
{
869
0
  struct sample empty_smp = { };
870
0
  struct var_desc desc;
871
0
  char *saved_name = NULL;
872
873
  /* Check arg type. */
874
0
  if (arg->type != ARGT_STR) {
875
0
    memprintf(err, "unexpected argument type");
876
0
    return 0;
877
0
  }
878
879
  /* Register new variable name. */
880
0
  if (!vars_fill_desc(arg->data.str.area, arg->data.str.data, &desc, err))
881
0
    return 0;
882
883
  /* Save variable name before destroying the chunk */
884
0
  if (desc.name && desc.name_len > 0) {
885
0
    saved_name = my_strndup(desc.name, desc.name_len);
886
0
    if (!saved_name) {
887
0
      memprintf(err, "out of memory");
888
0
      return 0;
889
0
    }
890
891
0
    desc.name = saved_name;
892
0
    desc.flags |= VDF_NAME_ALLOCATED;
893
0
  }
894
895
0
  if (desc.scope == SCOPE_PROC && !var_set(&desc, &empty_smp, VF_CREATEONLY|VF_PERMANENT)) {
896
0
    if (desc.flags & VDF_NAME_ALLOCATED)
897
0
      ha_free(&saved_name);
898
0
    return 0;
899
0
  }
900
901
  /* properly destroy the chunk */
902
0
  chunk_destroy(&arg->data.str);
903
904
  /* Use the global variable name pointer. */
905
0
  arg->type = ARGT_VAR;
906
0
  arg->data.var = desc;  /* desc.name already points to saved_name */
907
0
  return 1;
908
0
}
909
910
/* This function stores a sample in a variable unless it is of type "proc" and
911
 * not defined yet.
912
 * Returns zero on failure and non-zero otherwise. The variable not being
913
 * defined is treated as a failure.
914
 */
915
int vars_set_by_name_ifexist(const char *name, size_t len, struct sample *smp)
916
0
{
917
0
  struct var_desc desc;
918
919
  /* Resolve name and scope. */
920
0
  if (!vars_fill_desc(name, len, &desc, NULL))
921
0
    return 0;
922
923
  /* Variable creation is allowed for all scopes apart from the PROC one. */
924
0
  return var_set(&desc, smp, (desc.scope == SCOPE_PROC) ? VF_COND_IFEXISTS : 0);
925
0
}
926
927
928
/* This function stores a sample in a variable.
929
 * Returns zero on failure and non-zero otherwise.
930
 */
931
int vars_set_by_name(const char *name, size_t len, struct sample *smp)
932
0
{
933
0
  struct var_desc desc;
934
935
  /* Resolve name and scope. */
936
0
  if (!vars_fill_desc(name, len, &desc, NULL))
937
0
    return 0;
938
939
0
  return var_set(&desc, smp, 0);
940
0
}
941
942
/* This function unsets a variable if it was already defined.
943
 * Returns zero on failure and non-zero otherwise.
944
 */
945
int vars_unset_by_name_ifexist(const char *name, size_t len, struct sample *smp)
946
0
{
947
0
  struct var_desc desc;
948
949
  /* Resolve name and scope. */
950
0
  if (!vars_fill_desc(name, len, &desc, NULL))
951
0
    return 0;
952
953
0
  return var_unset(&desc, smp);
954
0
}
955
956
957
/* This retrieves variable whose hash matches <name_hash> from variables <vars>,
958
 * and if found and not empty, duplicates the result into sample <smp>.
959
 * smp_dup() is used in order to release the variables lock ASAP (so a pre-
960
 * allocated chunk is obtained via get_trash_shunk()). The variables' lock is
961
 * used for reads.
962
 *
963
 * The function returns 0 if the variable was not found and no default
964
 * value was provided in <def>, otherwise 1 with the sample filled.
965
 * Default values are always returned as strings.
966
 */
967
static int var_to_smp(struct vars *vars, uint64_t name_hash, struct sample *smp, const struct buffer *def)
968
0
{
969
0
  struct var *var;
970
971
  /* Get the variable entry. */
972
0
  vars_rdlock(vars);
973
0
  var = var_get(vars, name_hash);
974
0
  if (!var || !var->data.type) {
975
0
    if (!def) {
976
0
      vars_rdunlock(vars);
977
0
      return 0;
978
0
    }
979
980
    /* not found but we have a default value */
981
0
    smp->data.type = SMP_T_STR;
982
0
    smp->data.u.str = *def;
983
0
  }
984
0
  else
985
0
    smp->data = var->data;
986
987
  /* Copy sample. */
988
0
  smp_dup(smp);
989
990
0
  vars_rdunlock(vars);
991
0
  return 1;
992
0
}
993
994
/* This function fills a sample with the variable content.
995
 *
996
 * Keep in mind that a sample content is duplicated by using smp_dup()
997
 * and it therefore uses a pre-allocated trash chunk as returned by
998
 * get_trash_chunk().
999
 *
1000
 * If the variable is not valid in this scope, 0 is always returned.
1001
 * If the variable is valid but not found, either the default value
1002
 * <def> is returned if not NULL, or zero is returned.
1003
 *
1004
 * Returns 1 if the sample is filled, otherwise it returns 0.
1005
 */
1006
int vars_get_by_name(const char *name, size_t len, struct sample *smp, const struct buffer *def)
1007
0
{
1008
0
  struct vars *vars;
1009
0
  struct var_desc desc;
1010
1011
  /* Resolve name and scope. */
1012
0
  if (!vars_fill_desc(name, len, &desc, NULL))
1013
0
    return 0;
1014
1015
  /* Select "vars" pool according with the scope. */
1016
0
  vars = get_vars(smp->sess, smp->strm, &desc);
1017
0
  if (!vars || vars->scope != desc.scope)
1018
0
    return 0;
1019
1020
0
  return var_to_smp(vars, desc.name_hash, smp, def);
1021
0
}
1022
1023
/* This function fills a sample with the content of the variable described
1024
 * by <var_desc>.
1025
 *
1026
 * Keep in mind that a sample content is duplicated by using smp_dup()
1027
 * and it therefore uses a pre-allocated trash chunk as returned by
1028
 * get_trash_chunk().
1029
 *
1030
 * If the variable is not valid in this scope, 0 is always returned.
1031
 * If the variable is valid but not found, either the default value
1032
 * <def> is returned if not NULL, or zero is returned.
1033
 *
1034
 * Returns 1 if the sample is filled, otherwise it returns 0.
1035
 */
1036
int vars_get_by_desc(const struct var_desc *var_desc, struct sample *smp, const struct buffer *def)
1037
0
{
1038
0
  struct vars *vars;
1039
1040
  /* Select "vars" pool according with the scope. */
1041
0
  vars = get_vars(smp->sess, smp->strm, var_desc);
1042
1043
  /* Check if the scope is available a this point of processing. */
1044
0
  if (!vars || vars->scope != var_desc->scope)
1045
0
    return 0;
1046
1047
0
  return var_to_smp(vars, var_desc->name_hash, smp, def);
1048
0
}
1049
1050
/* Always returns ACT_RET_CONT even if an error occurs. */
1051
static enum act_return action_store(struct act_rule *rule, struct proxy *px,
1052
                                    struct session *sess, struct stream *s, int flags)
1053
0
{
1054
0
  struct buffer *fmtstr = NULL;
1055
0
  struct sample smp;
1056
0
  int dir;
1057
1058
0
  switch (rule->from) {
1059
0
  case ACT_F_TCP_REQ_CON: dir = SMP_OPT_DIR_REQ; break;
1060
0
  case ACT_F_TCP_REQ_SES: dir = SMP_OPT_DIR_REQ; break;
1061
0
  case ACT_F_TCP_REQ_CNT: dir = SMP_OPT_DIR_REQ; break;
1062
0
  case ACT_F_TCP_RES_CNT: dir = SMP_OPT_DIR_RES; break;
1063
0
  case ACT_F_HTTP_REQ:    dir = SMP_OPT_DIR_REQ; break;
1064
0
  case ACT_F_HTTP_RES:    dir = SMP_OPT_DIR_RES; break;
1065
0
  case ACT_F_TCP_CHK:     dir = SMP_OPT_DIR_REQ; break;
1066
0
  case ACT_F_CFG_PARSER:  dir = SMP_OPT_DIR_REQ;  break; /* not used anyway */
1067
0
  case ACT_F_CLI_PARSER:  dir = SMP_OPT_DIR_REQ;  break; /* not used anyway */
1068
0
  default:
1069
0
    send_log(px, LOG_ERR, "Vars: internal error while execute action store.");
1070
0
    if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
1071
0
      ha_alert("Vars: internal error while execute action store.\n");
1072
0
    return ACT_RET_CONT;
1073
0
  }
1074
1075
  /* Process the expression. */
1076
0
  memset(&smp, 0, sizeof(smp));
1077
1078
0
  if (rule->action == 2) {  /* set-var-fmt */
1079
    /* a format-string is used */
1080
1081
0
    fmtstr = alloc_trash_chunk();
1082
0
    if (!fmtstr) {
1083
0
      send_log(px, LOG_ERR, "Vars: memory allocation failure while processing store rule.");
1084
0
      if (!(global.mode & MODE_QUIET) || (global.mode & MODE_VERBOSE))
1085
0
        ha_alert("Vars: memory allocation failure while processing store rule.\n");
1086
0
      return ACT_RET_CONT;
1087
0
    }
1088
1089
    /* execute the log-format expression */
1090
0
    fmtstr->data = sess_build_logline(sess, s, fmtstr->area, fmtstr->size, &rule->arg.vars.fmt);
1091
1092
    /* convert it to a sample of type string as it's what the vars
1093
     * API consumes, and store it.
1094
     */
1095
0
    smp_set_owner(&smp, px, sess, s, 0);
1096
0
    smp.data.type = SMP_T_STR;
1097
0
    smp.data.u.str = *fmtstr;
1098
0
  }
1099
0
  else { /* set-var */
1100
    /* an expression is used */
1101
0
    if (!sample_process(px, sess, s, dir|SMP_OPT_FINAL,
1102
0
                              rule->arg.vars.expr, &smp))
1103
0
      return ACT_RET_CONT;
1104
0
  }
1105
1106
  /* Store the sample, and ignore errors. */
1107
0
  var_set(&rule->arg.vars.desc, &smp, rule->arg.vars.conditions);
1108
0
  free_trash_chunk(fmtstr);
1109
0
  return ACT_RET_CONT;
1110
0
}
1111
1112
/* Always returns ACT_RET_CONT even if an error occurs. */
1113
static enum act_return action_clear(struct act_rule *rule, struct proxy *px,
1114
                                    struct session *sess, struct stream *s, int flags)
1115
0
{
1116
0
  struct sample smp;
1117
1118
0
  memset(&smp, 0, sizeof(smp));
1119
0
  smp_set_owner(&smp, px, sess, s, SMP_OPT_FINAL);
1120
1121
  /* Clear the variable using the sample context, and ignore errors. */
1122
0
  var_unset(&rule->arg.vars.desc, &smp);
1123
0
  return ACT_RET_CONT;
1124
0
}
1125
1126
static void release_store_rule(struct act_rule *rule)
1127
0
{
1128
0
  lf_expr_deinit(&rule->arg.vars.fmt);
1129
1130
0
  release_sample_expr(rule->arg.vars.expr);
1131
1132
  /* Free variable name if allocated */
1133
0
  if (rule->arg.vars.desc.flags & VDF_NAME_ALLOCATED)
1134
0
    ha_free((char **)&rule->arg.vars.desc.name);
1135
0
}
1136
1137
/*
1138
 * Cast a string sample to an integer, but unlike c_str2int() reject any
1139
 * trailing garbage after the number.
1140
 */
1141
static int c_str2int_strict(struct sample *smp)
1142
0
{
1143
0
  const char *str;
1144
0
  const char *end;
1145
1146
0
  if (smp->data.u.str.data == 0)
1147
0
    return 0;
1148
1149
0
  str = smp->data.u.str.area;
1150
0
  end = smp->data.u.str.area + smp->data.u.str.data;
1151
1152
0
  smp->data.u.sint = read_int64(&str, end);
1153
0
  if (str != end)
1154
0
    return 0;
1155
0
  smp->data.type = SMP_T_SINT;
1156
0
  smp->flags &= ~SMP_F_CONST;
1157
0
  return 1;
1158
0
}
1159
1160
/*
1161
 * Cast a string sample to a boolean. The textual "true"/"false" spellings
1162
 * are accepted in any case, as are the strict integers 0 and 1. Anything else
1163
 * is rejected.
1164
 */
1165
static int c_str2bool(struct sample *smp)
1166
0
{
1167
0
  if (strcasecmp(smp->data.u.str.area, "true") == 0) {
1168
0
    smp->data.type = SMP_T_BOOL;
1169
0
    smp->data.u.sint = 1;
1170
0
    smp->flags &= ~SMP_F_CONST;
1171
0
    return 1;
1172
0
  }
1173
0
  if (strcasecmp(smp->data.u.str.area, "false") == 0) {
1174
0
    smp->data.type = SMP_T_BOOL;
1175
0
    smp->data.u.sint = 0;
1176
0
    smp->flags &= ~SMP_F_CONST;
1177
0
    return 1;
1178
0
  }
1179
1180
0
  if (!c_str2int_strict(smp))
1181
0
    return 0;
1182
1183
0
  if (smp->data.u.sint != 0 && smp->data.u.sint != 1)
1184
0
    return 0;
1185
1186
0
  smp->data.type = SMP_T_BOOL;
1187
0
  return 1;
1188
0
}
1189
1190
/*
1191
 * Conversion array used by set-vars-from-map(). Map values are always parsed as
1192
 * strings. Unlike the global sample_casts matrix, string-to-number conversion
1193
 * uses the strict variants above.
1194
 */
1195
static sample_cast_fct svfm_sample_casts[SMP_TYPES] = {
1196
  [SMP_T_STR]  = c_none,
1197
  [SMP_T_BOOL] = c_str2bool,
1198
  [SMP_T_SINT] = c_str2int_strict,
1199
};
1200
1201
/*
1202
 * Same as sample_convert() but based on the local svfm_sample_casts array
1203
 * above.
1204
 */
1205
static int svfm_convert(struct sample *sample, int req_type)
1206
0
{
1207
0
  if (!svfm_sample_casts[req_type])
1208
0
    return 0;
1209
0
  if (svfm_sample_casts[req_type] == c_none)
1210
0
    return 1;
1211
0
  return svfm_sample_casts[req_type](sample);
1212
0
}
1213
1214
/*
1215
 * Parses the map-entry value string <str> into sample <smp> according to the
1216
 * requested sample data type <type>.
1217
 * Returns 0 on success, 1 on failure. For strings the sample references <str>
1218
 * directly (flagged const), so <str> must outlive the sample; here it always
1219
 * does since it points into the map entry, which lives as long as the process.
1220
 */
1221
static int svfm_parse_value(const char *str, int type, struct sample *smp)
1222
0
{
1223
0
  smp->data.type = SMP_T_STR;
1224
0
  smp->flags |= SMP_F_CONST;
1225
0
  smp->data.u.str.area = (char *)str;
1226
0
  smp->data.u.str.data = strlen(str);
1227
0
  smp->data.u.str.size = 0; /* const, not writable */
1228
1229
0
  if (!svfm_convert(smp, type))
1230
0
    return 1;
1231
1232
0
  return 0;
1233
0
}
1234
1235
/*
1236
 * Runtime handler for the "set-vars-from-map" action. It walks the current
1237
 * generation of the map entries and, for each one, sets a variable named
1238
 * "<scope>.<key>" to the value parsed from the entry according to the
1239
 * configured type. It always returns ACT_RET_CONT, ignoring per-entry errors
1240
 * so that a single malformed line does not abort the whole batch.
1241
 */
1242
static enum act_return action_set_vars_from_map(struct act_rule *rule, struct proxy *px,
1243
                                                struct session *sess, struct stream *s, int flags)
1244
0
{
1245
0
  struct pat_ref *ref = rule->arg.act.p[0];
1246
0
  enum vars_scope scope = (enum vars_scope)(uintptr_t)rule->arg.act.p[1];
1247
0
  int type = (int)(uintptr_t)rule->arg.act.p[2];
1248
0
  unsigned int count = 0;
1249
1250
0
  struct pat_ref_gen *gen;
1251
0
  struct pat_ref_elt *elt;
1252
1253
0
  HA_RWLOCK_RDLOCK(PATREF_LOCK, &ref->lock);
1254
1255
0
  gen = pat_ref_gen_get(ref, ref->curr_gen);
1256
0
  if (!gen)
1257
0
    goto done;
1258
1259
0
  list_for_each_entry(elt, &gen->head, list) {
1260
0
    struct var_desc desc;
1261
0
    struct sample smp;
1262
0
    const char *value = elt->sample ? elt->sample : "";
1263
0
    size_t name_len = strlen(elt->pattern);
1264
1265
    /* Too many entries processed, stop */
1266
0
    if (++count > svfm_max)
1267
0
      break;
1268
1269
    /* Silently ignore entries whose name would not be a valid
1270
     * variable name, as well as empty names.
1271
     */
1272
0
    if (!var_name_is_valid(elt->pattern, name_len, NULL))
1273
0
      continue;
1274
1275
    /* The scope was already resolved at parse time, so the
1276
     * descriptor can be filled directly from the map key without
1277
     * going through vars_fill_desc() again.
1278
     */
1279
0
    desc.scope = scope;
1280
0
    desc.flags = 0;
1281
0
    desc.name = elt->pattern;
1282
0
    desc.name_len = name_len;
1283
0
    desc.name_hash = XXH3(elt->pattern, name_len, var_name_hash_seed);
1284
1285
0
    memset(&smp, 0, sizeof(smp));
1286
0
    smp_set_owner(&smp, px, sess, s, SMP_OPT_FINAL);
1287
1288
0
    if (svfm_parse_value(value, type, &smp))
1289
      /* Value parsing failed, skipping */
1290
0
      continue;
1291
1292
0
    var_set(&desc, &smp, 0);
1293
0
  }
1294
1295
0
done:
1296
0
  HA_RWLOCK_RDUNLOCK(PATREF_LOCK, &ref->lock);
1297
0
  return ACT_RET_CONT;
1298
0
}
1299
1300
/* This two function checks the variable name and replace the
1301
 * configuration string name by the global string name. its
1302
 * the same string, but the global pointer can be easy to
1303
 * compare. They return non-zero on success, zero on failure.
1304
 *
1305
 * The first function checks a sample-fetch and the second
1306
 * checks a converter.
1307
 */
1308
static int smp_check_var(struct arg *args, char **err)
1309
0
{
1310
0
  return vars_check_arg(&args[0], err);
1311
0
}
1312
1313
/* This function checks all arguments for dump_all_vars()
1314
 * Args: [scope], [prefix], [delimiter]
1315
 * Both arguments are optional
1316
 */
1317
static int smp_check_dump_all_vars(struct arg *args, char **err)
1318
0
{
1319
  /* First argument (scope) is optional */
1320
1321
0
  if (args[0].type == ARGT_STR) {
1322
0
    const char *scope = args[0].data.str.area;
1323
0
    int scope_id = -1;
1324
0
    int i;
1325
0
    char buf[16];
1326
1327
0
    if (args[0].data.str.data == 0) {
1328
0
      chunk_destroy(&args[0].data.str);
1329
0
      args[0].type = ARGT_SINT;
1330
0
      args[0].data.sint = scope_id;
1331
0
      return 1;
1332
0
    }
1333
1334
0
    if (args[0].data.str.data < sizeof(buf) - 1) {
1335
0
      snprintf(buf, sizeof(buf), "%s.", scope);
1336
1337
0
      for (i = 0; i <= SCOPE_CHECK; i++) {
1338
0
        if (strcmp(buf, var_scope_names[i]) == 0) {
1339
0
          scope_id = i;
1340
0
          break;
1341
0
        }
1342
0
      }
1343
0
    }
1344
1345
0
    if (scope_id == -1) {
1346
0
      memprintf(err, "invalid scope '%s', must be one of: sess, txn, req, res, proc", scope);
1347
0
      return 0;
1348
0
    }
1349
1350
0
    chunk_destroy(&args[0].data.str);
1351
0
    args[0].type = ARGT_SINT;
1352
0
    args[0].data.sint = scope_id;
1353
0
  }
1354
0
  else if (args[0].type != ARGT_STOP) {
1355
0
    memprintf(err, "first argument must be a string (scope) or omitted");
1356
0
    return 0;
1357
0
  } else {
1358
0
    args[0].type = ARGT_SINT;
1359
0
    args[0].data.sint = -1;
1360
0
  }
1361
1362
  /* Second argument (prefix) is optional */
1363
0
  if (args[1].type != ARGT_STR && args[1].type != ARGT_STOP) {
1364
0
    memprintf(err, "second argument must be a string (prefix) or omitted");
1365
0
    return 0;
1366
0
  }
1367
1368
  /* Third argument (delimiter) is optional */
1369
0
  if (args[2].type != ARGT_STR && args[2].type != ARGT_STOP) {
1370
0
    memprintf(err, "third argument must be a string (delimiter) or omitted");
1371
0
    return 0;
1372
0
  }
1373
1374
0
  return 1;
1375
0
}
1376
1377
static int conv_check_var(struct arg *args, struct sample_conv *conv,
1378
                          const char *file, int line, char **err_msg)
1379
0
{
1380
0
  int cond_idx = 1;
1381
0
  uint conditions = 0;
1382
0
  int retval = vars_check_arg(&args[0], err_msg);
1383
1384
0
  while (retval && args[cond_idx].type == ARGT_STR)
1385
0
    retval = vars_parse_cond_param(&args[cond_idx++].data.str, &conditions, err_msg);
1386
1387
0
  return retval;
1388
0
}
1389
1390
/*
1391
 * Parses the "set-vars-from-map(<scope>,<map-file>[,<type>])" action.
1392
 *
1393
 * <scope> is one of sess/txn/req/res/check. <map-file> is a two-column map file
1394
 * whose first column holds unqualified variable names and whose second column
1395
 * holds their values. <type> is an optional value type among "str" (default),
1396
 * "int" and "bool"; it tells how to parse the values, which matters in
1397
 * particular for arithmetic on numeric variables.
1398
 *
1399
 * Returns ACT_RET_PRS_OK on success, ACT_RET_PRS_ERR with <err> set on error.
1400
 */
1401
static enum act_parse_ret parse_set_vars_from_map(const char **args, int *arg, struct proxy *px,
1402
                                                  struct act_rule *rule, char **err)
1403
0
{
1404
0
  const char *kw = args[*arg - 1];
1405
0
  const char *p;
1406
0
  int len;
1407
0
  struct ist params, scope_ist, map_ist, type_ist;
1408
0
  char *map_name = NULL;
1409
0
  char scope_buf[6];
1410
0
  int scope;
1411
0
  int type = SMP_T_STR;
1412
0
  struct pat_ref *ref;
1413
0
  const char *endptr = NULL;
1414
1415
  /* the whole "set-vars-from-map(...)" token is a single argument */
1416
0
  p = strchr(kw, '(');
1417
0
  if (!p) {
1418
0
    memprintf(err, "invalid or incomplete action '%s'. Expects 'set-vars-from-map(<scope>,<map-file>[,<type>])'", kw);
1419
0
    return ACT_RET_PRS_ERR;
1420
0
  }
1421
0
  p++; /* jump the '(' */
1422
0
  len = strlen(p);
1423
0
  if (len == 0 || p[len - 1] != ')') {
1424
0
    memprintf(err, "incomplete argument after action '%s'. Expects 'set-vars-from-map(<scope>,<map-file>[,<type>])'", kw);
1425
0
    return ACT_RET_PRS_ERR;
1426
0
  }
1427
0
  len--; /* drop the ')' */
1428
1429
  /* split "<scope>,<map-file>[,<type>]" */
1430
0
  params = ist2(p, len);
1431
0
  scope_ist = istsplit(&params, ',');
1432
0
  map_ist   = istsplit(&params, ',');
1433
0
  type_ist  = istsplit(&params, ',');
1434
1435
0
  if (!istlen(scope_ist) || !istlen(map_ist)) {
1436
0
    memprintf(err, "action '%s' expects at least a scope and a map file name", kw);
1437
0
    return ACT_RET_PRS_ERR;
1438
0
  }
1439
1440
  /* Resolve the scope name. It is a slice of the whole action argument so
1441
   * it is copied into a local buffer to be nul-terminated before being
1442
   * parsed.
1443
   */
1444
0
  if (istlen(scope_ist) >= sizeof(scope_buf)) {
1445
0
    memprintf(err, "invalid scope '%.*s' in action '%s'. Expects one of 'sess', 'txn', 'req', 'res' or 'check'",
1446
0
              (int)istlen(scope_ist), istptr(scope_ist), kw);
1447
0
    return ACT_RET_PRS_ERR;
1448
0
  }
1449
0
  memcpy(scope_buf, istptr(scope_ist), istlen(scope_ist));
1450
0
  scope_buf[istlen(scope_ist)] = '\0';
1451
1452
0
  scope = var_parse_scope_str(scope_buf, &endptr);
1453
0
  if (scope == -1 || scope == SCOPE_PROC || (endptr && *endptr)) {
1454
0
    memprintf(err, "invalid scope '%.*s' in action '%s'. Expects one of 'sess', 'txn', 'req', 'res' or 'check'",
1455
0
              (int)istlen(scope_ist), istptr(scope_ist), kw);
1456
0
    return ACT_RET_PRS_ERR;
1457
0
  }
1458
1459
  /* resolve the optional value type */
1460
0
  if (istlen(type_ist)) {
1461
0
    if (isteq(type_ist, ist("str")))
1462
0
      type = SMP_T_STR;
1463
0
    else if (isteq(type_ist, ist("int")))
1464
0
      type = SMP_T_SINT;
1465
0
    else if (isteq(type_ist, ist("bool")))
1466
0
      type = SMP_T_BOOL;
1467
0
    else {
1468
0
      memprintf(err, "invalid type '%.*s' in action '%s'. Expects 'str', 'int' or 'bool'",
1469
0
                (int)istlen(type_ist), istptr(type_ist), kw);
1470
0
      return ACT_RET_PRS_ERR;
1471
0
    }
1472
0
  }
1473
1474
0
  map_name = my_strndup(istptr(map_ist), istlen(map_ist));
1475
0
  if (!map_name)
1476
0
    goto err;
1477
1478
  /* Load the map file as a two-column pattern reference, or reuse it if it
1479
   * was already loaded (e.g. by a map() converter or another such action).
1480
   */
1481
0
  ref = pat_ref_lookup(map_name);
1482
0
  if (!ref) {
1483
0
    chunk_printf(&trash, "map file '%s' loaded by set-vars-from-map at %s:%d",
1484
0
                 map_name, px->conf.args.file, px->conf.args.line);
1485
0
    ref = pat_ref_new(map_name, trash.area, PAT_REF_MAP | PAT_REF_SMP);
1486
0
    if (!ref) {
1487
0
      memprintf(err, "out of memory while loading map file '%s'", map_name);
1488
0
      goto err;
1489
0
    } else if (!(ref->flags & PAT_REF_FILE)) {
1490
0
      memprintf(err, "set-vars-from-map only accepts file-based maps ('%s')", map_name);
1491
0
      goto err;
1492
0
    }
1493
1494
0
    if (!pat_ref_read_from_file_smp(ref, err))
1495
0
      goto err;
1496
0
  } else if (!(ref->flags & PAT_REF_SMP)) {
1497
0
    memprintf(err, "the file '%s' is used as a one-column file elsewhere and cannot be used "
1498
0
                   "by 'set-vars-from-map' which requires a two-column (key/value) file", map_name);
1499
0
    goto err;
1500
0
  }
1501
1502
0
  rule->action = ACT_CUSTOM;
1503
0
  rule->action_ptr = action_set_vars_from_map;
1504
0
  rule->arg.act.p[0] = ref;
1505
0
  rule->arg.act.p[1] = (void *)(uintptr_t)scope;
1506
0
  rule->arg.act.p[2] = (void *)(uintptr_t)type;
1507
1508
0
  free(map_name);
1509
0
  return ACT_RET_PRS_OK;
1510
1511
0
err:
1512
0
  free(map_name);
1513
0
  return ACT_RET_PRS_ERR;
1514
0
}
1515
1516
/* This function is a common parser for using variables. It understands
1517
 * the format:
1518
 *
1519
 *   set-var-fmt(<variable-name>[,<cond> ...]) <format-string>
1520
 *   set-var(<variable-name>[,<cond> ...]) <expression>
1521
 *   unset-var(<variable-name>)
1522
 *
1523
 * It returns ACT_RET_PRS_ERR if fails and <err> is filled with an error
1524
 * message. Otherwise, it returns ACT_RET_PRS_OK and the variable <expr>
1525
 * is filled with the pointer to the expression to execute. The proxy is
1526
 * only used to retrieve the ->conf entries.
1527
 */
1528
static enum act_parse_ret parse_store(const char **args, int *arg, struct proxy *px,
1529
                                      struct act_rule *rule, char **err)
1530
0
{
1531
0
  const char *var_name = args[*arg-1];
1532
0
  int var_len;
1533
0
  const char *kw_name;
1534
0
  int flags = 0, set_var = 0; /* 0=unset-var, 1=set-var, 2=set-var-fmt */
1535
0
  struct sample empty_smp = { };
1536
0
  struct ist condition = IST_NULL;
1537
0
  struct ist var = IST_NULL;
1538
0
  struct ist varname_ist = IST_NULL;
1539
0
  char *saved_name = NULL;
1540
1541
0
  if (strncmp(var_name, "set-var-fmt", 11) == 0) {
1542
0
    var_name += 11;
1543
0
    set_var   = 2;
1544
0
  }
1545
0
  else if (strncmp(var_name, "set-var", 7) == 0) {
1546
0
    var_name += 7;
1547
0
    set_var   = 1;
1548
0
  }
1549
0
  else if (strncmp(var_name, "unset-var", 9) == 0) {
1550
0
    var_name += 9;
1551
0
    set_var   = 0;
1552
0
  }
1553
1554
0
  if (*var_name != '(') {
1555
0
    memprintf(err, "invalid or incomplete action '%s'. Expects 'set-var(<var-name>)', 'set-var-fmt(<var-name>)' or 'unset-var(<var-name>)'",
1556
0
        args[*arg-1]);
1557
0
    return ACT_RET_PRS_ERR;
1558
0
  }
1559
0
  var_name++; /* jump the '(' */
1560
0
  var_len = strlen(var_name);
1561
0
  var_len--; /* remove the ')' */
1562
0
  if (var_name[var_len] != ')') {
1563
0
    memprintf(err, "incomplete argument after action '%s'. Expects 'set-var(<var-name>)', 'set-var-fmt(<var-name>)' or 'unset-var(<var-name>)'",
1564
0
        args[*arg-1]);
1565
0
    return ACT_RET_PRS_ERR;
1566
0
  }
1567
1568
  /* Parse the optional conditions. */
1569
0
  var = ist2(var_name, var_len);
1570
0
  varname_ist = istsplit(&var, ',');
1571
0
  var_len = istlen(varname_ist);
1572
1573
0
  condition = istsplit(&var, ',');
1574
1575
0
  if (istlen(condition) && set_var == 0) {
1576
0
    memprintf(err, "unset-var does not expect parameters after the variable name. Only \"set-var\" and \"set-var-fmt\" manage conditions");
1577
0
    return ACT_RET_PRS_ERR;
1578
0
  }
1579
1580
0
  while (istlen(condition)) {
1581
0
    struct buffer cond = {};
1582
1583
0
    chunk_initlen(&cond, istptr(condition), 0, istlen(condition));
1584
0
    if (vars_parse_cond_param(&cond, &rule->arg.vars.conditions, err) == 0)
1585
0
      return ACT_RET_PRS_ERR;
1586
1587
0
    condition = istsplit(&var, ',');
1588
0
  }
1589
1590
0
  lf_expr_init(&rule->arg.vars.fmt);
1591
0
  if (!vars_fill_desc(var_name, var_len, &rule->arg.vars.desc, err))
1592
0
    return ACT_RET_PRS_ERR;
1593
1594
  /* Save variable name for runtime use */
1595
0
  if (rule->arg.vars.desc.name && rule->arg.vars.desc.name_len > 0) {
1596
0
    saved_name = my_strndup(rule->arg.vars.desc.name, rule->arg.vars.desc.name_len);
1597
0
    if (!saved_name) {
1598
0
      memprintf(err, "out of memory");
1599
0
      return ACT_RET_PRS_ERR;
1600
0
    }
1601
0
    rule->arg.vars.desc.name = saved_name;
1602
0
    rule->arg.vars.desc.flags |= VDF_NAME_ALLOCATED;
1603
0
  }
1604
1605
0
  if (rule->arg.vars.desc.scope == SCOPE_PROC &&
1606
0
      !var_set(&rule->arg.vars.desc, &empty_smp, VF_CREATEONLY|VF_PERMANENT))
1607
0
    return ACT_RET_PRS_ERR;
1608
1609
  /* There is no fetch method when variable is unset. Just set the right
1610
   * action and return. */
1611
0
  if (!set_var) {
1612
0
    rule->action     = ACT_CUSTOM;
1613
0
    rule->action_ptr = action_clear;
1614
0
    rule->release_ptr = release_store_rule;
1615
0
    return ACT_RET_PRS_OK;
1616
0
  }
1617
1618
0
  kw_name = args[*arg-1];
1619
1620
0
  switch (rule->from) {
1621
0
  case ACT_F_TCP_REQ_CON:
1622
0
    flags = SMP_VAL_FE_CON_ACC;
1623
0
    px->conf.args.ctx = ARGC_TCO;
1624
0
    break;
1625
0
  case ACT_F_TCP_REQ_SES:
1626
0
    flags = SMP_VAL_FE_SES_ACC;
1627
0
    px->conf.args.ctx = ARGC_TSE;
1628
0
    break;
1629
0
  case ACT_F_TCP_REQ_CNT:
1630
0
    if (px->cap & PR_CAP_FE)
1631
0
      flags |= SMP_VAL_FE_REQ_CNT;
1632
0
    if (px->cap & PR_CAP_BE)
1633
0
      flags |= SMP_VAL_BE_REQ_CNT;
1634
0
    px->conf.args.ctx = ARGC_TRQ;
1635
0
    break;
1636
0
  case ACT_F_TCP_RES_CNT:
1637
0
    if (px->cap & PR_CAP_FE)
1638
0
      flags |= SMP_VAL_FE_RES_CNT;
1639
0
    if (px->cap & PR_CAP_BE)
1640
0
      flags |= SMP_VAL_BE_RES_CNT;
1641
0
    px->conf.args.ctx = ARGC_TRS;
1642
0
    break;
1643
0
  case ACT_F_HTTP_REQ:
1644
0
    if (px->cap & PR_CAP_FE)
1645
0
      flags |= SMP_VAL_FE_HRQ_HDR;
1646
0
    if (px->cap & PR_CAP_BE)
1647
0
      flags |= SMP_VAL_BE_HRQ_HDR;
1648
0
    px->conf.args.ctx = ARGC_HRQ;
1649
0
    break;
1650
0
  case ACT_F_HTTP_RES:
1651
0
    if (px->cap & PR_CAP_FE)
1652
0
      flags |= SMP_VAL_FE_HRS_HDR;
1653
0
    if (px->cap & PR_CAP_BE)
1654
0
      flags |= SMP_VAL_BE_HRS_HDR;
1655
0
    px->conf.args.ctx =  ARGC_HRS;
1656
0
    break;
1657
0
  case ACT_F_TCP_CHK:
1658
0
    flags = SMP_VAL_BE_CHK_RUL;
1659
0
    px->conf.args.ctx = ARGC_TCK;
1660
0
    break;
1661
0
  case ACT_F_CFG_PARSER:
1662
0
    flags = SMP_VAL_CFG_PARSER;
1663
0
    px->conf.args.ctx = ARGC_CFG;
1664
0
    break;
1665
0
  case ACT_F_CLI_PARSER:
1666
0
    flags = SMP_VAL_CLI_PARSER;
1667
0
    px->conf.args.ctx = ARGC_CLI;
1668
0
    break;
1669
0
  default:
1670
0
    memprintf(err,
1671
0
        "internal error, unexpected rule->from=%d, please report this bug!",
1672
0
        rule->from);
1673
0
    return ACT_RET_PRS_ERR;
1674
0
  }
1675
1676
0
  if (set_var == 2) { /* set-var-fmt */
1677
0
    if (!parse_logformat_string(args[*arg], px, &rule->arg.vars.fmt, 0, flags, err))
1678
0
      return ACT_RET_PRS_ERR;
1679
1680
0
    (*arg)++;
1681
0
  } else {
1682
    /* set-var */
1683
0
    rule->arg.vars.expr = sample_parse_expr((char **)args, arg, px->conf.args.file,
1684
0
                                                  px->conf.args.line, err, &px->conf.args, NULL);
1685
0
    if (!rule->arg.vars.expr)
1686
0
      return ACT_RET_PRS_ERR;
1687
1688
0
    if (!(rule->arg.vars.expr->fetch->val & flags)) {
1689
0
      memprintf(err,
1690
0
                "fetch method '%s' extracts information from '%s', none of which is available here",
1691
0
                kw_name, sample_src_names(rule->arg.vars.expr->fetch->use));
1692
0
      free(rule->arg.vars.expr);
1693
0
      return ACT_RET_PRS_ERR;
1694
0
    }
1695
0
  }
1696
1697
0
  rule->action     = set_var;
1698
0
  rule->action_ptr = action_store;
1699
0
  rule->release_ptr = release_store_rule;
1700
0
  return ACT_RET_PRS_OK;
1701
0
}
1702
1703
1704
/* parses a global "set-var" directive. It will create a temporary rule and
1705
 * expression that are parsed, processed, and released on the fly so that we
1706
 * respect the real set-var syntax. These directives take the following format:
1707
 *    set-var <name> <expression>
1708
 *    set-var-fmt <name> <fmt>
1709
 * Note that parse_store() expects "set-var(name) <expression>" so we have to
1710
 * temporarily replace the keyword here.
1711
 */
1712
static int vars_parse_global_set_var(char **args, int section_type, struct proxy *curpx,
1713
                                     const struct proxy *defpx, const char *file, int line,
1714
                                     char **err)
1715
0
{
1716
0
  struct proxy px = {
1717
0
    .id = "CFG",
1718
0
    .conf.args = { .file = file, .line = line, },
1719
0
    .flags = PR_FL_CHECKED,
1720
0
  };
1721
0
  struct act_rule rule = {
1722
0
    .arg.vars.desc.scope = SCOPE_PROC,
1723
0
    .from = ACT_F_CFG_PARSER,
1724
0
    .conf = { .file = (char *)file, .line = line, },
1725
0
  };
1726
0
  enum obj_type objt = OBJ_TYPE_NONE;
1727
0
  struct session *sess = NULL;
1728
0
  enum act_parse_ret p_ret;
1729
0
  char *old_arg1;
1730
0
  char *tmp_arg1;
1731
0
  int arg = 2; // variable name
1732
0
  int ret = -1;
1733
0
  int use_fmt = 0;
1734
1735
0
  LIST_INIT(&px.conf.args.list);
1736
1737
0
  use_fmt = strcmp(args[0], "set-var-fmt") == 0;
1738
1739
0
  if (!*args[1] || !*args[2]) {
1740
0
    if (use_fmt)
1741
0
      memprintf(err, "'%s' requires a process-wide variable name ('proc.<name>') and a format string.", args[0]);
1742
0
    else
1743
0
      memprintf(err, "'%s' requires a process-wide variable name ('proc.<name>') and a sample expression.", args[0]);
1744
0
    goto end;
1745
0
  }
1746
1747
0
  tmp_arg1 = NULL;
1748
0
  if (!memprintf(&tmp_arg1, "set-var%s(%s)", use_fmt ? "-fmt" : "", args[1]))
1749
0
    goto end;
1750
1751
  /* parse_store() will always return a message in <err> on error */
1752
0
  old_arg1 = args[1]; args[1] = tmp_arg1;
1753
0
  p_ret = parse_store((const char **)args, &arg, &px, &rule, err);
1754
0
  free(args[1]); args[1] = old_arg1;
1755
1756
0
  if (p_ret != ACT_RET_PRS_OK)
1757
0
    goto end;
1758
1759
0
  if (rule.arg.vars.desc.scope != SCOPE_PROC) {
1760
0
    memprintf(err, "'%s': cannot set variable '%s', only scope 'proc' is permitted in the global section.", args[0], args[1]);
1761
0
    goto end;
1762
0
  }
1763
1764
0
  if (smp_resolve_args(&px, err) != 0) {
1765
0
    release_sample_expr(rule.arg.vars.expr);
1766
0
    indent_msg(err, 2);
1767
0
    goto end;
1768
0
  }
1769
1770
0
  if (use_fmt && !(sess = session_new(&px, NULL, &objt))) {
1771
0
    release_sample_expr(rule.arg.vars.expr);
1772
0
    memprintf(err, "'%s': out of memory when trying to set variable '%s' in the global section.", args[0], args[1]);
1773
0
    goto end;
1774
0
  }
1775
1776
0
  action_store(&rule, &px, sess, NULL, 0);
1777
0
  release_sample_expr(rule.arg.vars.expr);
1778
0
  if (sess)
1779
0
    session_free(sess);
1780
1781
0
  ret = 0;
1782
0
 end:
1783
0
  return ret;
1784
0
}
1785
1786
/* parse CLI's "get var <name>" */
1787
static int vars_parse_cli_get_var(char **args, char *payload, struct appctx *appctx, void *private)
1788
0
{
1789
0
  struct vars *vars;
1790
0
  struct var_desc desc;
1791
0
  struct sample smp = { };
1792
0
  int i;
1793
1794
0
  if (!cli_has_level(appctx, ACCESS_LVL_OPER))
1795
0
    return 1;
1796
1797
0
  if (!*args[2])
1798
0
    return cli_err(appctx, "Missing process-wide variable identifier.\n");
1799
1800
0
  desc.name_hash = 0;
1801
0
  desc.scope = SCOPE_PROC;
1802
0
  desc.flags = 0;
1803
0
  vars = get_vars(NULL, NULL, &desc);
1804
0
  if (!vars || vars->scope != desc.scope)
1805
0
    return 0;
1806
1807
0
  if (!vars_get_by_name(args[2], strlen(args[2]), &smp, NULL))
1808
0
    return cli_err(appctx, "Variable not found.\n");
1809
1810
  /* the sample returned by vars_get_by_name() is allocated into a trash
1811
   * chunk so we have no constraint to manipulate it.
1812
   */
1813
0
  chunk_printf(&trash, "%s: type=%s value=", args[2], smp_to_type[smp.data.type]);
1814
1815
0
  if (!sample_casts[smp.data.type][SMP_T_STR] ||
1816
0
      !sample_casts[smp.data.type][SMP_T_STR](&smp)) {
1817
0
    chunk_appendf(&trash, "(undisplayable)\n");
1818
0
  } else {
1819
    /* Display the displayable chars*. */
1820
0
    b_putchr(&trash, '<');
1821
0
    for (i = 0; i < smp.data.u.str.data; i++) {
1822
0
      if (isprint((unsigned char)smp.data.u.str.area[i]))
1823
0
        b_putchr(&trash, smp.data.u.str.area[i]);
1824
0
      else
1825
0
        b_putchr(&trash, '.');
1826
0
    }
1827
0
    b_putchr(&trash, '>');
1828
0
    b_putchr(&trash, '\n');
1829
0
    b_putchr(&trash, 0);
1830
0
  }
1831
0
  return cli_msg(appctx, LOG_INFO, trash.area);
1832
0
}
1833
1834
/* parse CLI's "set var <name>". It accepts:
1835
 *  - set var <name> <expression>
1836
 *  - set var <name> expr <expression>
1837
 *  - set var <name> fmt <format>
1838
 */
1839
static int vars_parse_cli_set_var(char **args, char *payload, struct appctx *appctx, void *private)
1840
0
{
1841
0
  struct proxy px = {
1842
0
    .id = "CLI",
1843
0
    .conf.args = { .file = "CLI", .line = 0, },
1844
0
    .flags = PR_FL_CHECKED,
1845
0
  };
1846
0
  struct act_rule rule = {
1847
0
    .arg.vars.desc.scope = SCOPE_PROC,
1848
0
    .from = ACT_F_CLI_PARSER,
1849
0
    .conf = { .file = "CLI", .line = 0, },
1850
0
  };
1851
0
  enum obj_type objt = OBJ_TYPE_NONE;
1852
0
  struct session *sess = NULL;
1853
0
  enum act_parse_ret p_ret;
1854
0
  const char *tmp_args[3];
1855
0
  int tmp_arg;
1856
0
  char *tmp_act;
1857
0
  char *err = NULL;
1858
0
  int nberr;
1859
0
  int use_fmt = 0;
1860
1861
0
  LIST_INIT(&px.conf.args.list);
1862
1863
0
  if (!cli_has_level(appctx, ACCESS_LVL_OPER))
1864
0
    return 1;
1865
1866
0
  if (!*args[2])
1867
0
    return cli_err(appctx, "Missing process-wide variable identifier.\n");
1868
1869
0
  if (!*args[3])
1870
0
    return cli_err(appctx, "Missing either 'expr', 'fmt' or expression.\n");
1871
1872
0
  if (*args[4]) {
1873
    /* this is the long format */
1874
0
    if (strcmp(args[3], "fmt") == 0)
1875
0
      use_fmt = 1;
1876
0
    else if (strcmp(args[3], "expr") != 0) {
1877
0
      memprintf(&err, "'%s %s': arg type must be either 'expr' or 'fmt' but got '%s'.", args[0], args[1], args[3]);
1878
0
      goto fail;
1879
0
    }
1880
0
  }
1881
1882
0
  tmp_act = NULL;
1883
0
  if (!memprintf(&tmp_act, "set-var%s(%s)", use_fmt ? "-fmt" : "", args[2])) {
1884
0
    memprintf(&err, "memory allocation error.");
1885
0
    goto fail;
1886
0
  }
1887
1888
  /* parse_store() will always return a message in <err> on error */
1889
0
  tmp_args[0] = tmp_act;
1890
0
  tmp_args[1] = (*args[4]) ? args[4] : args[3];
1891
0
  tmp_args[2] = "";
1892
0
  tmp_arg = 1; // must point to the first arg after the action
1893
0
  p_ret = parse_store(tmp_args, &tmp_arg, &px, &rule, &err);
1894
0
  free(tmp_act);
1895
1896
0
  if (p_ret != ACT_RET_PRS_OK)
1897
0
    goto fail;
1898
1899
0
  if (rule.arg.vars.desc.scope != SCOPE_PROC) {
1900
0
    memprintf(&err, "'%s %s': cannot set variable '%s', only scope 'proc' is permitted here.", args[0], args[1], args[2]);
1901
0
    goto fail;
1902
0
  }
1903
1904
0
  err = NULL;
1905
0
  nberr = smp_resolve_args(&px, &err);
1906
0
  if (nberr) {
1907
0
    release_sample_expr(rule.arg.vars.expr);
1908
0
    indent_msg(&err, 2);
1909
0
    goto fail;
1910
0
  }
1911
1912
0
  if (use_fmt && !(sess = session_new(&px, NULL, &objt))) {
1913
0
    release_sample_expr(rule.arg.vars.expr);
1914
0
    memprintf(&err, "memory allocation error.");
1915
0
    goto fail;
1916
0
  }
1917
1918
0
  action_store(&rule, &px, sess, NULL, 0);
1919
0
  release_sample_expr(rule.arg.vars.expr);
1920
0
  if (sess)
1921
0
    session_free(sess);
1922
1923
0
  appctx->st0 = CLI_ST_PROMPT;
1924
0
  return 0;
1925
0
 fail:
1926
0
  return cli_dynerr(appctx, err);
1927
0
}
1928
1929
static int vars_max_size(char **args, int section_type, struct proxy *curpx,
1930
                         const struct proxy *defpx, const char *file, int line,
1931
                         char **err, unsigned int *limit)
1932
0
{
1933
0
  char *error;
1934
1935
0
  *limit = strtol(args[1], &error, 10);
1936
0
  if (*error != 0) {
1937
0
    memprintf(err, "%s: '%s' is an invalid size", args[0], args[1]);
1938
0
    return -1;
1939
0
  }
1940
0
  return 0;
1941
0
}
1942
1943
static int vars_max_size_global(char **args, int section_type, struct proxy *curpx,
1944
                                const struct proxy *defpx, const char *file, int line,
1945
                                char **err)
1946
0
{
1947
0
  return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_global_limit);
1948
0
}
1949
1950
static int vars_max_size_proc(char **args, int section_type, struct proxy *curpx,
1951
                                const struct proxy *defpx, const char *file, int line,
1952
                                char **err)
1953
0
{
1954
0
  return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_proc_limit);
1955
0
}
1956
1957
static int vars_max_size_sess(char **args, int section_type, struct proxy *curpx,
1958
                              const struct proxy *defpx, const char *file, int line,
1959
                              char **err)
1960
0
{
1961
0
  return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_sess_limit);
1962
0
}
1963
1964
static int vars_max_size_txn(char **args, int section_type, struct proxy *curpx,
1965
                             const struct proxy *defpx, const char *file, int line,
1966
                             char **err)
1967
0
{
1968
0
  return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_txn_limit);
1969
0
}
1970
1971
static int vars_max_size_reqres(char **args, int section_type, struct proxy *curpx,
1972
                                const struct proxy *defpx, const char *file, int line,
1973
                                char **err)
1974
0
{
1975
0
  return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_reqres_limit);
1976
0
}
1977
1978
static int vars_max_size_check(char **args, int section_type, struct proxy *curpx,
1979
                                const struct proxy *defpx, const char *file, int line,
1980
                                char **err)
1981
0
{
1982
0
  return vars_max_size(args, section_type, curpx, defpx, file, line, err, &var_check_limit);
1983
0
}
1984
1985
static int vars_from_map_max(char **args, int section_type, struct proxy *curpx,
1986
                             const struct proxy *defpx, const char *file, int line,
1987
                             char **err)
1988
0
{
1989
0
  return vars_max_size(args, section_type, curpx, defpx, file, line, err, &svfm_max);
1990
0
}
1991
1992
/* early boot initialization */
1993
static void vars_init()
1994
0
{
1995
0
  var_name_hash_seed = ha_random64();
1996
  /* Initialize process vars */
1997
0
  vars_init_head(&proc_vars, SCOPE_PROC);
1998
0
}
1999
2000
INITCALL0(STG_PREPARE, vars_init);
2001
2002
static struct sample_fetch_kw_list sample_fetch_keywords = {ILH, {
2003
2004
  { "dump_all_vars", smp_fetch_dump_all_vars, ARG3(0,STR,STR,STR), smp_check_dump_all_vars, SMP_T_STR, SMP_USE_CONST },
2005
  { "var", smp_fetch_var, ARG2(1,STR,STR), smp_check_var, SMP_T_ANY, SMP_USE_CONST },
2006
  { /* END */ },
2007
}};
2008
2009
INITCALL1(STG_REGISTER, sample_register_fetches, &sample_fetch_keywords);
2010
2011
static struct sample_conv_kw_list sample_conv_kws = {ILH, {
2012
  { "set-var",   smp_conv_store, ARG5(1,STR,STR,STR,STR,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
2013
  { "unset-var", smp_conv_clear, ARG1(1,STR), conv_check_var, SMP_T_ANY, SMP_T_ANY },
2014
  { /* END */ },
2015
}};
2016
2017
INITCALL1(STG_REGISTER, sample_register_convs, &sample_conv_kws);
2018
2019
static struct action_kw_list tcp_req_conn_kws = { { }, {
2020
  { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
2021
  { "set-var",   parse_store, KWF_MATCH_PREFIX },
2022
  { "unset-var", parse_store, KWF_MATCH_PREFIX },
2023
  { "set-vars-from-map", parse_set_vars_from_map, KWF_MATCH_PREFIX },
2024
  { /* END */ }
2025
}};
2026
2027
INITCALL1(STG_REGISTER, tcp_req_conn_keywords_register, &tcp_req_conn_kws);
2028
2029
static struct action_kw_list tcp_req_sess_kws = { { }, {
2030
  { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
2031
  { "set-var",   parse_store, KWF_MATCH_PREFIX },
2032
  { "unset-var", parse_store, KWF_MATCH_PREFIX },
2033
  { "set-vars-from-map", parse_set_vars_from_map, KWF_MATCH_PREFIX },
2034
  { /* END */ }
2035
}};
2036
2037
INITCALL1(STG_REGISTER, tcp_req_sess_keywords_register, &tcp_req_sess_kws);
2038
2039
static struct action_kw_list tcp_req_cont_kws = { { }, {
2040
  { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
2041
  { "set-var",   parse_store, KWF_MATCH_PREFIX },
2042
  { "unset-var", parse_store, KWF_MATCH_PREFIX },
2043
  { "set-vars-from-map", parse_set_vars_from_map, KWF_MATCH_PREFIX },
2044
  { /* END */ }
2045
}};
2046
2047
INITCALL1(STG_REGISTER, tcp_req_cont_keywords_register, &tcp_req_cont_kws);
2048
2049
static struct action_kw_list tcp_res_kws = { { }, {
2050
  { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
2051
  { "set-var",   parse_store, KWF_MATCH_PREFIX },
2052
  { "unset-var", parse_store, KWF_MATCH_PREFIX },
2053
  { "set-vars-from-map", parse_set_vars_from_map, KWF_MATCH_PREFIX },
2054
  { /* END */ }
2055
}};
2056
2057
INITCALL1(STG_REGISTER, tcp_res_cont_keywords_register, &tcp_res_kws);
2058
2059
static struct action_kw_list tcp_check_kws = {ILH, {
2060
  { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
2061
  { "set-var",   parse_store, KWF_MATCH_PREFIX },
2062
  { "unset-var", parse_store, KWF_MATCH_PREFIX },
2063
  { "set-vars-from-map", parse_set_vars_from_map, KWF_MATCH_PREFIX },
2064
  { /* END */ }
2065
}};
2066
2067
INITCALL1(STG_REGISTER, tcp_check_keywords_register, &tcp_check_kws);
2068
2069
static struct action_kw_list http_req_kws = { { }, {
2070
  { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
2071
  { "set-var",   parse_store, KWF_MATCH_PREFIX },
2072
  { "unset-var", parse_store, KWF_MATCH_PREFIX },
2073
  { "set-vars-from-map", parse_set_vars_from_map, KWF_MATCH_PREFIX },
2074
  { /* END */ }
2075
}};
2076
2077
INITCALL1(STG_REGISTER, http_req_keywords_register, &http_req_kws);
2078
2079
static struct action_kw_list http_res_kws = { { }, {
2080
  { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
2081
  { "set-var",   parse_store, KWF_MATCH_PREFIX },
2082
  { "unset-var", parse_store, KWF_MATCH_PREFIX },
2083
  { "set-vars-from-map", parse_set_vars_from_map, KWF_MATCH_PREFIX },
2084
  { /* END */ }
2085
}};
2086
2087
INITCALL1(STG_REGISTER, http_res_keywords_register, &http_res_kws);
2088
2089
static struct action_kw_list http_after_res_kws = { { }, {
2090
  { "set-var-fmt", parse_store, KWF_MATCH_PREFIX },
2091
  { "set-var",   parse_store, KWF_MATCH_PREFIX },
2092
  { "unset-var", parse_store, KWF_MATCH_PREFIX },
2093
  { "set-vars-from-map", parse_set_vars_from_map, KWF_MATCH_PREFIX },
2094
  { /* END */ }
2095
}};
2096
2097
INITCALL1(STG_REGISTER, http_after_res_keywords_register, &http_after_res_kws);
2098
2099
static struct cfg_kw_list cfg_kws = {{ },{
2100
  { CFG_GLOBAL, "set-var",              vars_parse_global_set_var },
2101
  { CFG_GLOBAL, "set-var-fmt",          vars_parse_global_set_var },
2102
  { CFG_GLOBAL, "tune.vars.global-max-size", vars_max_size_global },
2103
  { CFG_GLOBAL, "tune.vars.proc-max-size",   vars_max_size_proc   },
2104
  { CFG_GLOBAL, "tune.vars.sess-max-size",   vars_max_size_sess   },
2105
  { CFG_GLOBAL, "tune.vars.txn-max-size",    vars_max_size_txn    },
2106
  { CFG_GLOBAL, "tune.vars.reqres-max-size", vars_max_size_reqres },
2107
  { CFG_GLOBAL, "tune.vars.check-max-size",  vars_max_size_check  },
2108
  { CFG_GLOBAL, "tune.vars.from-map.max",    vars_from_map_max    },
2109
  { /* END */ }
2110
}};
2111
2112
INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
2113
2114
2115
/* register cli keywords */
2116
static struct cli_kw_list cli_kws = {{ },{
2117
  { { "get",   "var", NULL }, "get var <name>                          : retrieve contents of a process-wide variable", vars_parse_cli_get_var, NULL },
2118
  { { "set",   "var", NULL }, "set var <name> [fmt|expr] {<fmt>|<expr>}: set variable from an expression or a format",  vars_parse_cli_set_var, NULL, NULL, NULL, ACCESS_EXPERIMENTAL },
2119
  { { NULL }, NULL, NULL, NULL }
2120
}};
2121
INITCALL1(STG_REGISTER, cli_register_kw, &cli_kws);