* 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);
*
* 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);
* 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
*/
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 */
#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);
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 */
#include <netinet/in.h>
#include <common/chunk.h>
+#include <common/mini-clist.h>
enum {
ARGT_STOP = 0, /* end of the arg list */
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;
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 */
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 */
/* 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;
/* 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);
* 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;
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;
* 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;
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;
* 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;
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) {
*/
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;
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;
/*
* 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",
/* 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;
}
}
}
*/
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
* 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;
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) {
* 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
/* 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)
err:
free(word);
- free(arg_list);
+ free(*argp);
if (err_arg)
*err_arg = pos;
if (err_ptr)
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]);
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;
/* 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;
}
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 */
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;
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;
}
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) {
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) {
}
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) ||
}
}
- /* 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) {
/*
* 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)
{
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;
/*
* 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
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;
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",
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);
#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;
* 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;
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);
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);
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