]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MAJOR: sample: maintain a per-proxy list of the fetch args to resolve
authorWilly Tarreau <w@1wt.eu>
Tue, 2 Apr 2013 14:34:32 +0000 (16:34 +0200)
committerWilly Tarreau <w@1wt.eu>
Wed, 3 Apr 2013 00:13:02 +0000 (02:13 +0200)
While ACL args were resolved after all the config was parsed, it was not the
case with sample fetch args because they're almost everywhere now.

The issue is that ACLs now solely rely on sample fetches, so their args
resolving doesn't work anymore. And many fetches involving a server, a
proxy or a userlist don't work at all.

The real issue is that at the bottom layers we have no information about
proxies, line numbers, even ACLs in order to report understandable errors,
and that at the top layers we have no visibility over the locations where
fetches are referenced (think log node).

After failing multiple unsatisfying solutions attempts, we now have a new
concept of args list. The principle is that every proxy has a list head
which contains a number of indications such as the config keyword, the
context where it's used, the file and line number, etc... and a list of
arguments. This list head is of the same type as the elements, so it
serves as a template for adding new elements. This way, it is filled from
top to bottom by the callers with the information they have (eg: line
numbers, ACL name, ...) and the lower layers just have to duplicate it and
add an element when they face an argument they cannot resolve yet.

Then at the end of the configuration parsing, a loop passes over each
proxy's list and resolves all the args in sequence. And this way there is
all necessary information to report verbose errors.

The first immediate benefit is that for the first time we got very precise
location of issues (arg number in a keyword in its context, ...). Second,
in order to do this we had to parse log-format and unique-id-format a bit
earlier, so that was a great opportunity for doing so when the directives
are encountered (unless it's a default section). This way, the recorded
line numbers for these args are the ones of the place where the log format
is declared, not the end of the file.

Userlists report slightly more information now. They're the only remaining
ones in the ACL resolving function.

13 files changed:
include/proto/acl.h
include/proto/arg.h
include/proto/sample.h
include/types/arg.h
include/types/proxy.h
src/acl.c
src/arg.c
src/cfgparse.c
src/log.c
src/proto_http.c
src/proto_tcp.c
src/proxy.c
src/sample.c

index edd4648032d6114eb48b1ad2d98fe1e9cc4f97cd..6859b3d1c4ad265e38cbddc66829c69b993ab3ae 100644 (file)
@@ -59,7 +59,7 @@ struct acl_keyword *find_acl_kw(const char *kw);
  * Right now, the only accepted syntax is :
  * <subject> [<value>...]
  */
-struct acl_expr *parse_acl_expr(const char **args, char **err);
+struct acl_expr *parse_acl_expr(const char **args, char **err, struct arg_list *al);
 
 /* Purge everything in the acl <acl>, then return <acl>. */
 struct acl *prune_acl(struct acl *acl);
@@ -70,7 +70,7 @@ struct acl *prune_acl(struct acl *acl);
  *
  * args syntax: <aclname> <acl_expr>
  */
-struct acl *parse_acl(const char **args, struct list *known_acl, char **err);
+struct acl *parse_acl(const char **args, struct list *known_acl, char **err, struct arg_list *al);
 
 /* Purge everything in the acl_cond <cond>, then return <cond>. */
 struct acl_cond *prune_acl_cond(struct acl_cond *cond);
@@ -79,7 +79,7 @@ struct acl_cond *prune_acl_cond(struct acl_cond *cond);
  * known ACLs passed in <known_acl>. The new condition is returned (or NULL in
  * case of low memory). Supports multiple conditions separated by "or".
  */
-struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl, int pol, char **err);
+struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl, int pol, char **err, struct arg_list *al);
 
 /* Builds an ACL condition starting at the if/unless keyword. The complete
  * condition is returned. NULL is returned in case of error or if the first
index 8e0a81b075f50fbba33db1c79072ca10c78d12c7..78c1105abb34c392c8e978aae7685276555bb645 100644 (file)
  */
 extern struct arg empty_arg_list[8];
 
+struct arg_list *arg_list_clone(const struct arg_list *orig);
+struct arg_list *arg_list_add(struct arg_list *orig, struct arg *arg, int pos);
 int make_arg_list(const char *in, int len, unsigned int mask, struct arg **argp,
-                 char **err_msg, const char **err_ptr, int *err_arg);
+                  char **err_msg, const char **err_ptr, int *err_arg,
+                  struct arg_list *al);
 
 #endif /* _PROTO_ARG_H */
 
index a5696f6fb28035df3cb488dcb7795d6b7d7a398d..3043662d96536a8808254e57881fa656751dad8e 100644 (file)
@@ -26,7 +26,7 @@
 #include <types/sample.h>
 #include <types/stick_table.h>
 
-struct sample_expr *sample_parse_expr(char **str, int *idx, char *err, int err_size);
+struct sample_expr *sample_parse_expr(char **str, int *idx, char *err, int err_size, struct arg_list *al);
 struct sample *sample_process(struct proxy *px, struct session *l4,
                                void *l7, unsigned int dir, struct sample_expr *expr,
                                struct sample *p);
@@ -37,5 +37,6 @@ void sample_register_convs(struct sample_conv_kw_list *psl);
 const char *sample_src_names(unsigned int use);
 const char *sample_ckp_names(unsigned int use);
 struct sample_fetch *find_sample_fetch(const char *kw, int len);
+int smp_resolve_args(struct proxy *p);
 
 #endif /* _PROTO_SAMPLE_H */
index 666a51f0fc4c34d45985c8c3ef04612c1e0510ca..a53d29375049d3c77580e9e7e034642dbe1cc753 100644 (file)
@@ -26,6 +26,7 @@
 #include <netinet/in.h>
 
 #include <common/chunk.h>
+#include <common/mini-clist.h>
 
 enum {
        ARGT_STOP = 0, /* end of the arg list */
@@ -47,6 +48,16 @@ enum {
        ARGT_NBTYPES   /* no more values past 15 */
 };
 
+/* context where arguments are used, in order to help error reporting */
+enum {
+       ARGC_ACL = 0,  /* ACL */
+       ARGC_STK,      /* sticking rule */
+       ARGC_TRK,      /* tracking rule */
+       ARGC_LOG,      /* log-format */
+       ARGC_HDR,      /* add-header */
+       ARGC_UIF,      /* unique-id-format */
+};
+
 /* some types that are externally defined */
 struct proxy;
 struct server;
@@ -69,6 +80,21 @@ struct arg {
        union arg_data data;      /* argument data */
 };
 
+/* arg lists are used to store information about arguments that could not be
+ * resolved when parsing the configuration. The head is an arg_list which
+ * serves as a template to create new entries. Nothing here is allocated,
+ * so plain copies are OK.
+ */
+struct arg_list {
+       struct list list;         /* chaining with other arg_list, or list head */
+       struct arg *arg;          /* pointer to the arg, NULL on list head */
+       int arg_pos;              /* argument position */
+       int ctx;                  /* context where the arg is used (ARGC_*) */
+       const char *kw;           /* keyword making use of these args */
+       const char *conv;         /* conv keyword when in conv, otherwise NULL */
+       const char *file;         /* file name where the args are referenced */
+       int line;                 /* line number where the args are referenced */
+};
 
 #endif /* _TYPES_ARG_H */
 
index 5abf35e344b9ee051881b104028011321fd5dffc..5729db80dcf609d52ccc26c768a4f40b8ad83f71 100644 (file)
@@ -350,6 +350,7 @@ struct proxy {
                struct eb_root used_server_id;  /* list of server IDs in use */
                struct list bind;               /* list of bind settings */
                struct list listeners;          /* list of listeners belonging to this frontend */
+               struct arg_list args;           /* sample arg list that need to be resolved */
        } conf;                                 /* config information */
        void *parent;                           /* parent of the proxy when applicable */
        struct comp *comp;                      /* http compression */
index 4a53072024e16e79f2b310bf332e2d988290e1ab..efd1ee662d444014b8e1ec0cdc6c8d84cc0149d7 100644 (file)
--- a/src/acl.c
+++ b/src/acl.c
@@ -1024,12 +1024,13 @@ static int acl_read_patterns_from_file(struct acl_expr *expr,
 
 /* Parse an ACL expression starting at <args>[0], and return it. If <err> is
  * not NULL, it will be filled with a pointer to an error message in case of
- * error. This pointer must be freeable or NULL.
+ * error. This pointer must be freeable or NULL. <al> is an arg_list serving
+ * as a list head to report missing dependencies.
  *
  * Right now, the only accepted syntax is :
  * <subject> [<value>...]
  */
-struct acl_expr *parse_acl_expr(const char **args, char **err)
+struct acl_expr *parse_acl_expr(const char **args, char **err, struct arg_list *al)
 {
        __label__ out_return, out_free_expr, out_free_pattern;
        struct acl_expr *expr;
@@ -1088,10 +1089,14 @@ struct acl_expr *parse_acl_expr(const char **args, char **err)
                        /* Parse the arguments. Note that currently we have no way to
                         * report parsing errors, hence the NULL in the error pointers.
                         * An error is also reported if some mandatory arguments are
-                        * missing.
+                        * missing. We prepare the args list to report unresolved
+                        * dependencies.
                         */
+                       al->ctx = ARGC_ACL;
+                       al->kw = expr->kw;
+                       al->conv = NULL;
                        nbargs = make_arg_list(arg, end - arg, expr->smp->arg_mask, &expr->args,
-                                              err, NULL, NULL);
+                                              err, NULL, NULL, al);
                        if (nbargs < 0) {
                                /* note that make_arg_list will have set <err> here */
                                memprintf(err, "in argument to '%s', %s", expr->kw, *err);
@@ -1273,11 +1278,12 @@ struct acl *prune_acl(struct acl *acl) {
  * A pointer to that ACL is returned. If the ACL has an empty name, then it's
  * an anonymous one and it won't be merged with any other one. If <err> is not
  * NULL, it will be filled with an appropriate error. This pointer must be
- * freeable or NULL.
+ * freeable or NULL. <al> is the arg_list serving as a head for unresolved
+ * dependencies.
  *
  * args syntax: <aclname> <acl_expr>
  */
-struct acl *parse_acl(const char **args, struct list *known_acl, char **err)
+struct acl *parse_acl(const char **args, struct list *known_acl, char **err, struct arg_list *al)
 {
        __label__ out_return, out_free_acl_expr, out_free_name;
        struct acl *cur_acl;
@@ -1290,7 +1296,7 @@ struct acl *parse_acl(const char **args, struct list *known_acl, char **err)
                goto out_return;
        }
 
-       acl_expr = parse_acl_expr(args + 1, err);
+       acl_expr = parse_acl_expr(args + 1, err, al);
        if (!acl_expr) {
                /* parse_acl_expr will have filled <err> here */
                goto out_return;
@@ -1381,9 +1387,11 @@ const struct {
  * except when default ACLs are broken, in which case it will return NULL.
  * If <known_acl> is not NULL, the ACL will be queued at its tail. If <err> is
  * not NULL, it will be filled with an error message if an error occurs. This
- * pointer must be freeable or NULL.
+ * pointer must be freeable or NULL. <al> is an arg_list serving as a list head
+ * to report missing dependencies.
  */
-struct acl *find_acl_default(const char *acl_name, struct list *known_acl, char **err)
+static struct acl *find_acl_default(const char *acl_name, struct list *known_acl,
+                                    char **err, struct arg_list *al)
 {
        __label__ out_return, out_free_acl_expr, out_free_name;
        struct acl *cur_acl;
@@ -1401,7 +1409,7 @@ struct acl *find_acl_default(const char *acl_name, struct list *known_acl, char
                return NULL;
        }
 
-       acl_expr = parse_acl_expr((const char **)default_acl_list[index].expr, err);
+       acl_expr = parse_acl_expr((const char **)default_acl_list[index].expr, err, al);
        if (!acl_expr) {
                /* parse_acl_expr must have filled err here */
                goto out_return;
@@ -1458,9 +1466,11 @@ struct acl_cond *prune_acl_cond(struct acl_cond *cond)
  * case of low memory). Supports multiple conditions separated by "or". If
  * <err> is not NULL, it will be filled with a pointer to an error message in
  * case of error, that the caller is responsible for freeing. The initial
- * location must either be freeable or NULL.
+ * location must either be freeable or NULL. The list <al> serves as a list head
+ * for unresolved dependencies.
  */
-struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl, int pol, char **err)
+struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl,
+                                int pol, char **err, struct arg_list *al)
 {
        __label__ out_return, out_free_suite, out_free_term;
        int arg, neg;
@@ -1533,7 +1543,7 @@ struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl, int p
                        args_new[0] = "";
                        memcpy(args_new + 1, args + arg + 1, (arg_end - arg) * sizeof(*args_new));
                        args_new[arg_end - arg] = "";
-                       cur_acl = parse_acl(args_new, known_acl, err);
+                       cur_acl = parse_acl(args_new, known_acl, err, al);
                        free(args_new);
 
                        if (!cur_acl) {
@@ -1551,7 +1561,7 @@ struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl, int p
                         */
                        cur_acl = find_acl_by_name(word, known_acl);
                        if (cur_acl == NULL) {
-                               cur_acl = find_acl_default(word, known_acl, err);
+                               cur_acl = find_acl_default(word, known_acl, err, al);
                                if (cur_acl == NULL) {
                                        /* note that find_acl_default() must have filled <err> here */
                                        goto out_free_suite;
@@ -1635,7 +1645,7 @@ struct acl_cond *build_acl_cond(const char *file, int line, struct proxy *px, co
                return NULL;
        }
 
-       cond = parse_acl_cond(args, &px->acl, pol, err);
+       cond = parse_acl_cond(args, &px->acl, pol, err, &px->conf.args);
        if (!cond) {
                /* note that parse_acl_cond must have filled <err> here */
                return NULL;
@@ -1838,191 +1848,29 @@ int acl_cond_kw_conflicts(const struct acl_cond *cond, unsigned int where, struc
 
 /*
  * Find targets for userlist and groups in acl. Function returns the number
- * of errors or OK if everything is fine.
+ * of errors or OK if everything is fine. It must be called only once sample
+ * fetch arguments have been resolved (after smp_resolve_args()).
  */
-int
-acl_find_targets(struct proxy *p)
+int acl_find_targets(struct proxy *p)
 {
 
        struct acl *acl;
        struct acl_expr *expr;
        struct acl_pattern *pattern;
-       struct userlist *ul;
-       struct arg *arg;
        int cfgerr = 0;
 
        list_for_each_entry(acl, &p->acl, list) {
                list_for_each_entry(expr, &acl->expr, list) {
-                       for (arg = expr->args; arg && arg->type != ARGT_STOP; arg++) {
-                               if (!arg->unresolved)
+                       if (!strcmp(expr->kw, "http_auth_group")) {
+                               /* Note: the ARGT_USR argument may only have been resolved earlier
+                                * by smp_resolve_args().
+                                */
+                               if (expr->args->unresolved) {
+                                       Alert("Internal bug in proxy %s: %sacl %s %s() makes use of unresolved userlist '%s'. Please report this.\n",
+                                             p->id, *acl->name ? "" : "anonymous ", acl->name, expr->kw, expr->args->data.str.str);
+                                       cfgerr++;
                                        continue;
-                               else if (arg->type == ARGT_SRV) {
-                                       struct proxy *px;
-                                       struct server *srv;
-                                       char *pname, *sname;
-
-                                       if (!arg->data.str.len) {
-                                               Alert("proxy %s: acl '%s' %s(): missing server name.\n",
-                                                     p->id, acl->name, expr->kw);
-                                               cfgerr++;
-                                               continue;
-                                       }
-
-                                       pname = arg->data.str.str;
-                                       sname = strrchr(pname, '/');
-
-                                       if (sname)
-                                               *sname++ = '\0';
-                                       else {
-                                               sname = pname;
-                                               pname = NULL;
-                                       }
-
-                                       px = p;
-                                       if (pname) {
-                                               px = findproxy(pname, PR_CAP_BE);
-                                               if (!px) {
-                                                       Alert("proxy %s: acl '%s' %s(): unable to find proxy '%s'.\n",
-                                                             p->id, acl->name, expr->kw, pname);
-                                                       cfgerr++;
-                                                       continue;
-                                               }
-                                       }
-
-                                       srv = findserver(px, sname);
-                                       if (!srv) {
-                                               Alert("proxy %s: acl '%s' %s(): unable to find server '%s'.\n",
-                                                     p->id, acl->name, expr->kw, sname);
-                                               cfgerr++;
-                                               continue;
-                                       }
-
-                                       free(arg->data.str.str);
-                                       arg->data.str.str = NULL;
-                                       arg->unresolved = 0;
-                                       arg->data.srv = srv;
                                }
-                               else if (arg->type == ARGT_FE) {
-                                       struct proxy *prx = p;
-                                       char *pname = p->id;
-
-                                       if (arg->data.str.len) {
-                                               pname = arg->data.str.str;
-                                               prx = findproxy(pname, PR_CAP_FE);
-                                       }
-
-                                       if (!prx) {
-                                               Alert("proxy %s: acl '%s' %s(): unable to find frontend '%s'.\n",
-                                                     p->id, acl->name, expr->kw, pname);
-                                               cfgerr++;
-                                               continue;
-                                       }
-
-                                       if (!(prx->cap & PR_CAP_FE)) {
-                                               Alert("proxy %s: acl '%s' %s(): proxy '%s' has no frontend capability.\n",
-                                                     p->id, acl->name, expr->kw, pname);
-                                               cfgerr++;
-                                               continue;
-                                       }
-
-                                       free(arg->data.str.str);
-                                       arg->data.str.str = NULL;
-                                       arg->unresolved = 0;
-                                       arg->data.prx = prx;
-                               }
-                               else if (arg->type == ARGT_BE) {
-                                       struct proxy *prx = p;
-                                       char *pname = p->id;
-
-                                       if (arg->data.str.len) {
-                                               pname = arg->data.str.str;
-                                               prx = findproxy(pname, PR_CAP_BE);
-                                       }
-
-                                       if (!prx) {
-                                               Alert("proxy %s: acl '%s' %s(): unable to find backend '%s'.\n",
-                                                     p->id, acl->name, expr->kw, pname);
-                                               cfgerr++;
-                                               continue;
-                                       }
-
-                                       if (!(prx->cap & PR_CAP_BE)) {
-                                               Alert("proxy %s: acl '%s' %s(): proxy '%s' has no backend capability.\n",
-                                                     p->id, acl->name, expr->kw, pname);
-                                               cfgerr++;
-                                               continue;
-                                       }
-
-                                       free(arg->data.str.str);
-                                       arg->data.str.str = NULL;
-                                       arg->unresolved = 0;
-                                       arg->data.prx = prx;
-                               }
-                               else if (arg->type == ARGT_TAB) {
-                                       struct proxy *prx = p;
-                                       char *pname = p->id;
-
-                                       if (arg->data.str.len) {
-                                               pname = arg->data.str.str;
-                                               prx = find_stktable(pname);
-                                       }
-
-                                       if (!prx) {
-                                               Alert("proxy %s: acl '%s' %s(): unable to find table '%s'.\n",
-                                                     p->id, acl->name, expr->kw, pname);
-                                               cfgerr++;
-                                               continue;
-                                       }
-
-
-                                       if (!prx->table.size) {
-                                               Alert("proxy %s: acl '%s' %s(): no table in proxy '%s'.\n",
-                                                     p->id, acl->name, expr->kw, pname);
-                                               cfgerr++;
-                                               continue;
-                                       }
-
-                                       free(arg->data.str.str);
-                                       arg->data.str.str = NULL;
-                                       arg->unresolved = 0;
-                                       arg->data.prx = prx;
-                               }
-                               else if (arg->type == ARGT_USR) {
-                                       if (!arg->data.str.len) {
-                                               Alert("proxy %s: acl '%s' %s(): missing userlist name.\n",
-                                                     p->id, acl->name, expr->kw);
-                                               cfgerr++;
-                                               continue;
-                                       }
-
-                                       if (p->uri_auth && p->uri_auth->userlist &&
-                                           !strcmp(p->uri_auth->userlist->name, arg->data.str.str))
-                                               ul = p->uri_auth->userlist;
-                                       else
-                                               ul = auth_find_userlist(arg->data.str.str);
-
-                                       if (!ul) {
-                                               Alert("proxy %s: acl '%s' %s(%s): unable to find userlist.\n",
-                                                     p->id, acl->name, expr->kw, arg->data.str.str);
-                                               cfgerr++;
-                                               continue;
-                                       }
-
-                                       free(arg->data.str.str);
-                                       arg->data.str.str = NULL;
-                                       arg->unresolved = 0;
-                                       arg->data.usr = ul;
-                               }
-                       } /* end of args processing */
-
-                       /* don't try to resolve groups if we're not certain of having
-                        * resolved userlists first.
-                        */
-                       if (cfgerr)
-                               break;
-
-                       if (!strcmp(expr->kw, "http_auth_group")) {
-                               /* note: argument resolved above thanks to ARGT_USR */
 
                                if (LIST_ISEMPTY(&expr->patterns)) {
                                        Alert("proxy %s: acl %s %s(): no groups specified.\n",
@@ -2035,16 +1883,14 @@ acl_find_targets(struct proxy *p)
                                        /* this keyword only has one argument */
                                        pattern->val.group_mask = auth_resolve_groups(expr->args->data.usr, pattern->ptr.str);
 
-                                       free(pattern->ptr.str);
-                                       pattern->ptr.str = NULL;
-                                       pattern->len = 0;
-
                                        if (!pattern->val.group_mask) {
-                                               Alert("proxy %s: acl %s %s(): invalid group(s).\n",
-                                                       p->id, acl->name, expr->kw);
+                                               Alert("proxy %s: acl %s %s(): invalid group '%s'.\n",
+                                                     p->id, acl->name, expr->kw, pattern->ptr.str);
                                                cfgerr++;
-                                               continue;
                                        }
+                                       free(pattern->ptr.str);
+                                       pattern->ptr.str = NULL;
+                                       pattern->len = 0;
                                }
                        }
                }
index f113ba3033a58fdeaa48f9aded7bd1a822037a8c..1bf6444d32b3bc1a665416095450e2375148d96b 100644 (file)
--- a/src/arg.c
+++ b/src/arg.c
@@ -41,6 +41,41 @@ static const char *arg_type_names[ARGT_NBTYPES] = {
  */
 struct arg empty_arg_list[8] = { };
 
+/* This function clones a struct arg_list template into a new one which is
+ * returned.
+ */
+struct arg_list *arg_list_clone(const struct arg_list *orig)
+{
+       struct arg_list *new;
+
+       if ((new = calloc(1, sizeof(*new))) != NULL) {
+               /* ->list will be set by the caller when inserting the element.
+                * ->arg and ->arg_pos will be set by the caller.
+                */
+               new->ctx = orig->ctx;
+               new->kw = orig->kw;
+               new->conv = orig->conv;
+               new->file = orig->file;
+               new->line = orig->line;
+       }
+       return new;
+}
+
+/* This function clones a struct <arg_list> template into a new one which is
+ * set to point to arg <arg> at pos <pos>, and which is returned if the caller
+ * wants to apply further changes.
+ */
+struct arg_list *arg_list_add(struct arg_list *orig, struct arg *arg, int pos)
+{
+       struct arg_list *new;
+
+       new = arg_list_clone(orig);
+       new->arg = arg;
+       new->arg_pos = pos;
+       LIST_ADDQ(&orig->list, &new->list);
+       return new;
+}
+
 /* This function builds an argument list from a config line. It returns the
  * number of arguments found, or <0 in case of any error. Everything needed
  * it automatically allocated. A pointer to an error message might be returned
@@ -48,21 +83,26 @@ struct arg empty_arg_list[8] = { };
  * will have to check it and free it. The output arg list is returned in argp
  * which must be valid. The returned array is always terminated by an arg of
  * type ARGT_STOP (0), unless the mask indicates that no argument is supported.
- * The mask is composed of a number of mandatory arguments in its lower 4 bits,
- * and a concatenation of each argument type in each subsequent 4-bit block. If
- * <err_msg> is not NULL, it must point to a freeable or NULL pointer.
+ * Unresolved arguments are appended to arg list <al>, which also serves as a
+ * template to create new entries. The mask is composed of a number of
+ * mandatory arguments in its lower 4 bits, and a concatenation of each
+ * argument type in each subsequent 4-bit block. If <err_msg> is not NULL, it
+ * must point to a freeable or NULL pointer.
  */
 int make_arg_list(const char *in, int len, unsigned int mask, struct arg **argp,
-                 char **err_msg, const char **err_ptr, int *err_arg)
+                  char **err_msg, const char **err_ptr, int *err_arg,
+                  struct arg_list *al)
 {
        int nbarg;
        int pos;
-       struct arg *arg, *arg_list = NULL;
+       struct arg *arg;
        const char *beg;
        char *word = NULL;
        const char *ptr_err = NULL;
        int min_arg;
 
+       *argp = NULL;
+
        min_arg = mask & 15;
        mask >>= 4;
 
@@ -79,7 +119,7 @@ int make_arg_list(const char *in, int len, unsigned int mask, struct arg **argp,
        if (!len && !min_arg)
                goto end_parse;
 
-       arg = arg_list = calloc(nbarg + 1, sizeof(*arg));
+       arg = *argp = calloc(nbarg + 1, sizeof(*arg));
 
        /* Note: empty arguments after a comma always exist. */
        while (pos < nbarg) {
@@ -136,6 +176,8 @@ int make_arg_list(const char *in, int len, unsigned int mask, struct arg **argp,
                         * parsing then resolved later.
                         */
                        arg->unresolved = 1;
+                       arg_list_add(al, arg, pos);
+
                        /* fall through */
                case ARGT_STR:
                        /* all types that must be resolved are stored as strings
@@ -239,8 +281,6 @@ int make_arg_list(const char *in, int len, unsigned int mask, struct arg **argp,
        /* note that pos might be < nbarg and this is not an error, it's up to the
         * caller to decide what to do with optional args.
         */
-       *argp = arg_list;
-
        if (err_arg)
                *err_arg = pos;
        if (err_ptr)
@@ -249,7 +289,7 @@ int make_arg_list(const char *in, int len, unsigned int mask, struct arg **argp,
 
  err:
        free(word);
-       free(arg_list);
+       free(*argp);
        if (err_arg)
                *err_arg = pos;
        if (err_ptr)
index b82a2dc68423cecf10f86bbdef5685b459c4cf69..49a91c046ee70b97394b2fbe81516019b5c629a1 100644 (file)
@@ -1572,8 +1572,8 @@ int cfg_parse_peers(const char *file, int linenum, char **args, int kwm)
                                curpeers->peers_fe->timeout.connect = 5000;
                                curpeers->peers_fe->accept = peer_accept;
                                curpeers->peers_fe->options2 |= PR_O2_INDEPSTR | PR_O2_SMARTCON | PR_O2_SMARTACC;
-                               curpeers->peers_fe->conf.file = strdup(file);
-                               curpeers->peers_fe->conf.line = linenum;
+                               curpeers->peers_fe->conf.args.file = curpeers->peers_fe->conf.file = strdup(file);
+                               curpeers->peers_fe->conf.args.line = curpeers->peers_fe->conf.line = linenum;
 
                                bind_conf = bind_conf_alloc(&curpeers->peers_fe->conf.bind, file, linenum, args[2]);
 
@@ -1692,8 +1692,8 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
                init_new_proxy(curproxy);
                curproxy->next = proxy;
                proxy = curproxy;
-               curproxy->conf.file = strdup(file);
-               curproxy->conf.line = linenum;
+               curproxy->conf.args.file = curproxy->conf.file = strdup(file);
+               curproxy->conf.args.line = curproxy->conf.line = linenum;
                curproxy->last_change = now.tv_sec;
                curproxy->id = strdup(args[1]);
                curproxy->cap = rc;
@@ -1925,6 +1925,8 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
                /* we cannot free uri_auth because it might already be used */
                init_default_instance();
                curproxy = &defproxy;
+               curproxy->conf.args.file = curproxy->conf.file = strdup(file);
+               curproxy->conf.args.line = curproxy->conf.line = linenum;
                defproxy.cap = PR_CAP_LISTEN; /* all caps for now */
                goto out;
        }
@@ -1933,7 +1935,10 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
                err_code |= ERR_ALERT | ERR_FATAL;
                goto out;
        }
-    
+
+       /* update the current file and line being parsed */
+       curproxy->conf.args.file = curproxy->conf.file;
+       curproxy->conf.args.line = linenum;
 
        /* Now let's parse the proxy-specific keywords */
        if (!strcmp(args[0], "bind")) {  /* new listen addresses */
@@ -2225,7 +2230,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
                        err_code |= ERR_ALERT | ERR_FATAL;
                }
 
-               if (parse_acl((const char **)args + 1, &curproxy->acl, &errmsg) == NULL) {
+               if (parse_acl((const char **)args + 1, &curproxy->acl, &errmsg, &curproxy->conf.args) == NULL) {
                        Alert("parsing [%s:%d] : error detected while parsing ACL '%s' : %s.\n",
                              file, linenum, args[1], errmsg);
                        err_code |= ERR_ALERT | ERR_FATAL;
@@ -3023,7 +3028,8 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
                        goto out;
                }
 
-               expr = sample_parse_expr(args, &myidx, trash.str, trash.size);
+               curproxy->conf.args.ctx = ARGC_STK;
+               expr = sample_parse_expr(args, &myidx, trash.str, trash.size, &curproxy->conf.args);
                if (!expr) {
                        Alert("parsing [%s:%d] : '%s': %s\n", file, linenum, args[0], trash.str);
                        err_code |= ERR_ALERT | ERR_FATAL;
@@ -4825,6 +4831,18 @@ stats_error_parsing:
                }
                free(curproxy->uniqueid_format_string);
                curproxy->uniqueid_format_string = strdup(args[1]);
+
+               /* get a chance to improve log-format error reporting by
+                * reporting the correct line-number when possible.
+                */
+               if (curproxy != &defproxy) {
+                       curproxy->conf.args.ctx = ARGC_UIF;
+                       if (curproxy->uniqueid_format_string)
+                               parse_logformat_string(curproxy->uniqueid_format_string, curproxy, &curproxy->format_unique_id, 0,
+                                                      (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR);
+                       free(curproxy->uniqueid_format_string);
+                       curproxy->uniqueid_format_string = NULL;
+               }
        }
 
        else if (strcmp(args[0], "unique-id-header") == 0) {
@@ -4854,6 +4872,23 @@ stats_error_parsing:
                    curproxy->logformat_string != clf_http_log_format)
                        free(curproxy->logformat_string);
                curproxy->logformat_string = strdup(args[1]);
+
+               /* get a chance to improve log-format error reporting by
+                * reporting the correct line-number when possible.
+                */
+               if (curproxy != &defproxy && !(curproxy->cap & PR_CAP_FE)) {
+                       Warning("parsing [%s:%d] : backend '%s' : 'log-format' directive is ignored in backends.\n",
+                               file, linenum, curproxy->id);
+                       err_code |= ERR_WARN;
+               }
+               else if (curproxy->cap & PR_CAP_FE) {
+                       curproxy->conf.args.ctx = ARGC_LOG;
+                       if (curproxy->logformat_string)
+                               parse_logformat_string(curproxy->logformat_string, curproxy, &curproxy->logformat, LOG_OPT_MANDATORY,
+                                                      SMP_VAL_FE_LOG_END);
+                       free(curproxy->logformat_string);
+                       curproxy->logformat_string = NULL;
+               }
        }
 
        else if (!strcmp(args[0], "log") && kwm == KWM_NO) {
@@ -6434,7 +6469,29 @@ int check_config_validity()
                }
 out_uri_auth_compat:
 
-               cfgerr += acl_find_targets(curproxy);
+               /* compile the log format */
+               if (!(curproxy->cap & PR_CAP_FE)) {
+                       if (curproxy->logformat_string != default_http_log_format &&
+                           curproxy->logformat_string != default_tcp_log_format &&
+                           curproxy->logformat_string != clf_http_log_format)
+                               free(curproxy->logformat_string);
+                       curproxy->logformat_string = NULL;
+               }
+
+               curproxy->conf.args.ctx = ARGC_LOG;
+               if (curproxy->logformat_string)
+                       parse_logformat_string(curproxy->logformat_string, curproxy, &curproxy->logformat, LOG_OPT_MANDATORY,
+                                              SMP_VAL_FE_LOG_END);
+
+               curproxy->conf.args.ctx = ARGC_UIF;
+               if (curproxy->uniqueid_format_string)
+                       parse_logformat_string(curproxy->uniqueid_format_string, curproxy, &curproxy->format_unique_id, 0,
+                                              (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR);
+
+               /* only now we can check if some args remain unresolved */
+               cfgerr += smp_resolve_args(curproxy);
+               if (!cfgerr)
+                       cfgerr += acl_find_targets(curproxy);
 
                if ((curproxy->mode == PR_MODE_TCP || curproxy->mode == PR_MODE_HTTP) &&
                    (((curproxy->cap & PR_CAP_FE) && !curproxy->timeout.client) ||
@@ -6524,23 +6581,6 @@ out_uri_auth_compat:
                        }
                }
 
-               /* compile the log format */
-               if (!(curproxy->cap & PR_CAP_FE)) {
-                       if (curproxy->logformat_string != default_http_log_format &&
-                           curproxy->logformat_string != default_tcp_log_format &&
-                           curproxy->logformat_string != clf_http_log_format)
-                               free(curproxy->logformat_string);
-                       curproxy->logformat_string = NULL;
-               }
-
-               if (curproxy->logformat_string)
-                       parse_logformat_string(curproxy->logformat_string, curproxy, &curproxy->logformat, LOG_OPT_MANDATORY,
-                                              SMP_VAL_FE_LOG_END);
-
-               if (curproxy->uniqueid_format_string)
-                       parse_logformat_string(curproxy->uniqueid_format_string, curproxy, &curproxy->format_unique_id, 0,
-                                              (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR);
-
                /* first, we will invert the servers list order */
                newsrv = NULL;
                while (curproxy->srv) {
index 165fd724e43d8ed801af40dad86085304bd87927..ed1f0612255edc97f3487571812d3a29d04488d4 100644 (file)
--- a/src/log.c
+++ b/src/log.c
@@ -307,7 +307,7 @@ void add_to_logformat_list(char *start, char *end, int type, struct list *list_f
 /*
  * Parse the sample fetch expression <text> and add a node to <list_format> upon
  * success. At the moment, sample converters are not yet supported but fetch arguments
- * should work.
+ * should work. The curpx->conf.args.ctx must be set by the caller.
  */
 void add_sample_to_logformat_list(char *text, char *arg, int arg_len, struct proxy *curpx, struct list *list_format, int options, int cap)
 {
@@ -320,7 +320,7 @@ void add_sample_to_logformat_list(char *text, char *arg, int arg_len, struct pro
        cmd[1] = "";
        cmd_arg = 0;
 
-       expr = sample_parse_expr(cmd, &cmd_arg, trash.str, trash.size);
+       expr = sample_parse_expr(cmd, &cmd_arg, trash.str, trash.size, &curpx->conf.args);
        if (!expr) {
                Warning("log-format: sample fetch <%s> failed with : %s\n", text, trash.str);
                return;
@@ -360,7 +360,8 @@ void add_sample_to_logformat_list(char *text, char *arg, int arg_len, struct pro
 /*
  * Parse the log_format string and fill a linked list.
  * Variable name are preceded by % and composed by characters [a-zA-Z0-9]* : %varname
- * You can set arguments using { } : %{many arguments}varname
+ * You can set arguments using { } : %{many arguments}varname.
+ * The curproxy->conf.args.ctx must be set by the caller.
  *
  *  str: the string to parse
  *  curproxy: the proxy affected
index 162b8f49396857bde97e6c1fe6abd31b12b60535..ff71659aa9b043d1529948b13663d2a38523f29d 100644 (file)
@@ -8139,6 +8139,8 @@ struct http_req_rule *parse_http_req_cond(const char **args, const char *file, i
                rule->arg.hdr_add.name = strdup(args[cur_arg]);
                rule->arg.hdr_add.name_len = strlen(rule->arg.hdr_add.name);
                LIST_INIT(&rule->arg.hdr_add.fmt);
+
+               proxy->conf.args.ctx = ARGC_HDR;
                parse_logformat_string(args[cur_arg + 1], proxy, &rule->arg.hdr_add.fmt, 0,
                                       (proxy->cap & PR_CAP_FE) ? SMP_VAL_FE_HRQ_HDR : SMP_VAL_BE_HRQ_HDR);
                cur_arg += 2;
index dc3817ea2bdb56b035c9e99a0e308a43644329e0..781b24a740bdcc1c08ffe2f9562ed8d38d0f2ed5 100644 (file)
@@ -1112,7 +1112,8 @@ static int tcp_parse_request_rule(char **args, int arg, int section_type,
 
                arg++;
 
-               expr = sample_parse_expr(args, &arg, trash.str, trash.size);
+               curpx->conf.args.ctx = ARGC_TRK;
+               expr = sample_parse_expr(args, &arg, trash.str, trash.size, &curpx->conf.args);
                if (!expr) {
                        memprintf(err,
                                  "'%s %s %s' : %s",
index 1986a7c2ba275c9b0e3f1eea7ee18d268835258d..7bfe954a492775b4b5ed20d1fa07544cb341f7e7 100644 (file)
@@ -449,6 +449,7 @@ void init_new_proxy(struct proxy *p)
        LIST_INIT(&p->format_unique_id);
        LIST_INIT(&p->conf.bind);
        LIST_INIT(&p->conf.listeners);
+       LIST_INIT(&p->conf.args.list);
 
        /* Timeouts are defined as -1 */
        proxy_reset_timeouts(p);
index 3067cc75825a074697283cb308a7d596c1860928..753b45737d1a27ade3a6da31a2de5a54fd49bf4a 100644 (file)
 
 #include <common/chunk.h>
 #include <common/standard.h>
+#include <common/uri_auth.h>
 
 #include <proto/arg.h>
+#include <proto/auth.h>
+#include <proto/log.h>
+#include <proto/proxy.h>
 #include <proto/sample.h>
+#include <proto/stick_table.h>
 
 /* static sample used in sample_process() when <p> is NULL */
 static struct sample temp_smp;
@@ -525,8 +530,9 @@ static sample_cast_fct sample_casts[SMP_TYPES][SMP_TYPES] = {
  * Parse a sample expression configuration:
  *        fetch keyword followed by format conversion keywords.
  * Returns a pointer on allocated sample expression structure.
+ * The caller must have set al->ctx.
  */
-struct sample_expr *sample_parse_expr(char **str, int *idx, char *err, int err_size)
+struct sample_expr *sample_parse_expr(char **str, int *idx, char *err, int err_size, struct arg_list *al)
 {
        const char *endw;
        const char *end;
@@ -598,7 +604,9 @@ struct sample_expr *sample_parse_expr(char **str, int *idx, char *err, int err_s
                        goto out_error;
                }
 
-               if (make_arg_list(endw + 1, end - endw - 2, fetch->arg_mask, &expr->arg_p, &err_msg, NULL, &err_arg) < 0) {
+               al->kw = expr->fetch->kw;
+               al->conv = NULL;
+               if (make_arg_list(endw + 1, end - endw - 2, fetch->arg_mask, &expr->arg_p, &err_msg, NULL, &err_arg, al) < 0) {
                        p = my_strndup(str[*idx], endw - str[*idx]);
                        if (p) {
                                snprintf(err, err_size, "invalid arg %d in fetch method '%s' : %s.", err_arg+1, p, err_msg);
@@ -693,7 +701,9 @@ struct sample_expr *sample_parse_expr(char **str, int *idx, char *err, int err_s
                                goto out_error;
                        }
 
-                       if (make_arg_list(endw + 1, end - endw - 2, conv->arg_mask, &conv_expr->arg_p, &err_msg, NULL, &err_arg) < 0) {
+                       al->kw = expr->fetch->kw;
+                       al->conv = conv_expr->conv->kw;
+                       if (make_arg_list(endw + 1, end - endw - 2, conv->arg_mask, &conv_expr->arg_p, &err_msg, NULL, &err_arg, al) < 0) {
                                p = my_strndup(str[*idx], endw - str[*idx]);
                                if (p) {
                                        snprintf(err, err_size, "invalid arg %d in conv method '%s' : %s.", err_arg+1, p, err_msg);
@@ -786,6 +796,214 @@ struct sample *sample_process(struct proxy *px, struct session *l4, void *l7,
        return p;
 }
 
+/*
+ * Resolve all remaining arguments in proxy <p>. Returns the number of
+ * errors or 0 if everything is fine.
+ */
+int smp_resolve_args(struct proxy *p)
+{
+       struct arg_list *cur, *bak;
+       const char *ctx, *where;
+       const char *conv_ctx, *conv_pre, *conv_pos;
+       struct userlist *ul;
+       struct arg *arg;
+       int cfgerr = 0;
+
+       list_for_each_entry_safe(cur, bak, &p->conf.args.list, list) {
+               struct proxy *px;
+               struct server *srv;
+               char *pname, *sname;
+
+               arg = cur->arg;
+
+               /* prepare output messages */
+               conv_pre = conv_pos = conv_ctx = "";
+               if (cur->conv) {
+                       conv_ctx = cur->conv;
+                       conv_pre = "conversion keyword '";
+                       conv_pos = "' for ";
+               }
+
+               where = "in";
+               ctx = "sample fetch keyword";
+               switch (cur->ctx) {
+               case ARGC_STK:where = "in stick rule in"; break;
+               case ARGC_TRK: where = "in tracking rule in"; break;
+               case ARGC_LOG: where = "in log-format string in"; break;
+               case ARGC_HDR: where = "in HTTP header format string in"; break;
+               case ARGC_UIF: where = "in unique-id-format string in"; break;
+               case ARGC_ACL: ctx = "ACL keyword"; break;
+               }
+
+               /* set a few default settings */
+               px = p;
+               pname = p->id;
+
+               switch (arg->type) {
+               case ARGT_SRV:
+                       if (!arg->data.str.len) {
+                               Alert("parsing [%s:%d] : missing server name in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
+                                     cur->file, cur->line,
+                                     cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
+                               cfgerr++;
+                               continue;
+                       }
+
+                       /* we support two formats : "bck/srv" and "srv" */
+                       sname = strrchr(arg->data.str.str, '/');
+
+                       if (sname) {
+                               *sname++ = '\0';
+                               pname = arg->data.str.str;
+
+                               px = findproxy(pname, PR_CAP_BE);
+                               if (!px) {
+                                       Alert("parsing [%s:%d] : unable to find proxy '%s' referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
+                                             cur->file, cur->line, pname,
+                                             cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
+                                       cfgerr++;
+                                       break;
+                               }
+                       }
+                       else
+                               sname = arg->data.str.str;
+
+                       srv = findserver(px, sname);
+                       if (!srv) {
+                               Alert("parsing [%s:%d] : unable to find server '%s' in proxy '%s', referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
+                                     cur->file, cur->line, sname, pname,
+                                     cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
+                               cfgerr++;
+                               break;
+                       }
+
+                       free(arg->data.str.str);
+                       arg->data.str.str = NULL;
+                       arg->unresolved = 0;
+                       arg->data.srv = srv;
+                       break;
+
+               case ARGT_FE:
+                       if (arg->data.str.len) {
+                               pname = arg->data.str.str;
+                               px = findproxy(pname, PR_CAP_FE);
+                       }
+
+                       if (!px) {
+                               Alert("parsing [%s:%d] : unable to find frontend '%s' referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
+                                     cur->file, cur->line, pname,
+                                     cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
+                               cfgerr++;
+                               break;
+                       }
+
+                       if (!(px->cap & PR_CAP_FE)) {
+                               Alert("parsing [%s:%d] : proxy '%s', referenced in arg %d of %s%s%s%s '%s' %s proxy '%s', has not frontend capability.\n",
+                                     cur->file, cur->line, pname,
+                                     cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
+                               cfgerr++;
+                               break;
+                       }
+
+                       free(arg->data.str.str);
+                       arg->data.str.str = NULL;
+                       arg->unresolved = 0;
+                       arg->data.prx = px;
+                       break;
+
+               case ARGT_BE:
+                       if (arg->data.str.len) {
+                               pname = arg->data.str.str;
+                               px = findproxy(pname, PR_CAP_BE);
+                       }
+
+                       if (!px) {
+                               Alert("parsing [%s:%d] : unable to find backend '%s' referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
+                                     cur->file, cur->line, pname,
+                                     cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
+                               cfgerr++;
+                               break;
+                       }
+
+                       if (!(px->cap & PR_CAP_BE)) {
+                               Alert("parsing [%s:%d] : proxy '%s', referenced in arg %d of %s%s%s%s '%s' %s proxy '%s', has not backend capability.\n",
+                                     cur->file, cur->line, pname,
+                                     cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
+                               cfgerr++;
+                               break;
+                       }
+
+                       free(arg->data.str.str);
+                       arg->data.str.str = NULL;
+                       arg->unresolved = 0;
+                       arg->data.prx = px;
+                       break;
+
+               case ARGT_TAB:
+                       if (arg->data.str.len) {
+                               pname = arg->data.str.str;
+                               px = find_stktable(pname);
+                       }
+
+                       if (!px) {
+                               Alert("parsing [%s:%d] : unable to find table '%s' referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
+                                     cur->file, cur->line, pname,
+                                     cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
+                               cfgerr++;
+                               break;
+                       }
+
+                       if (!px->table.size) {
+                               Alert("parsing [%s:%d] : no table in proxy '%s' referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
+                                     cur->file, cur->line, pname,
+                                     cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
+                               cfgerr++;
+                               break;
+                       }
+
+                       free(arg->data.str.str);
+                       arg->data.str.str = NULL;
+                       arg->unresolved = 0;
+                       arg->data.prx = px;
+                       break;
+
+               case ARGT_USR:
+                       if (!arg->data.str.len) {
+                               Alert("parsing [%s:%d] : missing userlist name in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
+                                     cur->file, cur->line,
+                                     cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
+                               cfgerr++;
+                               break;
+                       }
+
+                       if (p->uri_auth && p->uri_auth->userlist &&
+                           !strcmp(p->uri_auth->userlist->name, arg->data.str.str))
+                               ul = p->uri_auth->userlist;
+                       else
+                               ul = auth_find_userlist(arg->data.str.str);
+
+                       if (!ul) {
+                               Alert("parsing [%s:%d] : unable to find userlist '%s' referenced in arg %d of %s%s%s%s '%s' %s proxy '%s'.\n",
+                                     cur->file, cur->line, arg->data.str.str,
+                                     cur->arg_pos + 1, conv_pre, conv_ctx, conv_pos, ctx, cur->kw, where, p->id);
+                               cfgerr++;
+                               break;
+                       }
+
+                       free(arg->data.str.str);
+                       arg->data.str.str = NULL;
+                       arg->unresolved = 0;
+                       arg->data.usr = ul;
+                       break;
+               }
+
+               LIST_DEL(&cur->list);
+               free(cur);
+       } /* end of args processing */
+
+       return cfgerr;
+}
+
 /*
  * Process a fetch + format conversion as defined by the sample expression <expr>
  * on request or response considering the <opt> parameter. The output is always of