MINOR: arg: Be able to forbid unresolved args when building an argument list

In make_arg_list() function, unresolved dependencies are pushed in an
argument list to be resolved later, during the configuration validity
check. It is now possible to forbid such unresolved dependencies by omitting
<al> parameter (setting it to NULL). It is usefull when the parsing context
is not the same than the running context or when the parsing context is lost
after the startup stage. For instance, an argument may be defined in
defaults section during parsing and executed in a frontend/backend section.
This commit is contained in:
Christopher Faulet 2021-09-30 08:48:56 +02:00
parent e3957f83e0
commit 35926a16ac
3 changed files with 43 additions and 22 deletions

View File

@ -121,7 +121,8 @@ static struct acl_expr *prune_acl_expr(struct acl_expr *expr)
/* Parse an ACL expression starting at <args>[0], and return it. If <err> is /* 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 * not NULL, it will be filled with a pointer to an error message in case of
* error. This pointer must be freeable or NULL. <al> is an arg_list serving * error. This pointer must be freeable or NULL. <al> is an arg_list serving
* as a list head to report missing dependencies. * as a list head to report missing dependencies. It may be NULL if such
* dependencies are not allowed.
* *
* Right now, the only accepted syntax is : * Right now, the only accepted syntax is :
* <subject> [<value>...] * <subject> [<value>...]
@ -162,9 +163,11 @@ struct acl_expr *parse_acl_expr(const char **args, char **err, struct arg_list *
* keyword. * keyword.
*/ */
al->ctx = ARGC_ACL; // to report errors while resolving args late if (al) {
al->kw = *args; al->ctx = ARGC_ACL; // to report errors while resolving args late
al->conv = NULL; al->kw = *args;
al->conv = NULL;
}
aclkw = find_acl_kw(args[0]); aclkw = find_acl_kw(args[0]);
if (aclkw) { if (aclkw) {
@ -280,8 +283,10 @@ struct acl_expr *parse_acl_expr(const char **args, char **err, struct arg_list *
conv_expr->conv = conv; conv_expr->conv = conv;
acl_conv_found = 1; acl_conv_found = 1;
al->kw = smp->fetch->kw; if (al) {
al->conv = conv_expr->conv->kw; al->kw = smp->fetch->kw;
al->conv = conv_expr->conv->kw;
}
argcnt = make_arg_list(endw, -1, conv->arg_mask, &conv_expr->arg_p, err, &arg, &err_arg, al); argcnt = make_arg_list(endw, -1, conv->arg_mask, &conv_expr->arg_p, err, &arg, &err_arg, al);
if (argcnt < 0) { if (argcnt < 0) {
memprintf(err, "ACL keyword '%s' : invalid arg %d in converter '%s' : %s.", memprintf(err, "ACL keyword '%s' : invalid arg %d in converter '%s' : %s.",
@ -665,7 +670,7 @@ struct acl *prune_acl(struct acl *acl) {
* an anonymous one and it won't be merged with any other one. If <err> is not * 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 * NULL, it will be filled with an appropriate error. This pointer must be
* freeable or NULL. <al> is the arg_list serving as a head for unresolved * freeable or NULL. <al> is the arg_list serving as a head for unresolved
* dependencies. * dependencies. It may be NULL if such dependencies are not allowed.
* *
* args syntax: <aclname> <acl_expr> * args syntax: <aclname> <acl_expr>
*/ */
@ -778,7 +783,8 @@ const struct {
* If <known_acl> is not NULL, the ACL will be queued at its tail. If <err> is * 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 * not NULL, it will be filled with an error message if an error occurs. This
* pointer must be freeable or NULL. <al> is an arg_list serving as a list head * pointer must be freeable or NULL. <al> is an arg_list serving as a list head
* to report missing dependencies. * to report missing dependencies. It may be NULL if such dependencies are not
* allowed.
*/ */
static struct acl *find_acl_default(const char *acl_name, struct list *known_acl, static struct acl *find_acl_default(const char *acl_name, struct list *known_acl,
char **err, struct arg_list *al, char **err, struct arg_list *al,
@ -858,7 +864,8 @@ struct acl_cond *prune_acl_cond(struct acl_cond *cond)
* <err> is not NULL, it will be filled with a pointer to an error message in * <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 * case of error, that the caller is responsible for freeing. The initial
* location must either be freeable or NULL. The list <al> serves as a list head * location must either be freeable or NULL. The list <al> serves as a list head
* for unresolved dependencies. * for unresolved dependencies. It may be NULL if such dependencies are not
* allowed.
*/ */
struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl, struct acl_cond *parse_acl_cond(const char **args, struct list *known_acl,
enum acl_cond_pol pol, char **err, struct arg_list *al, enum acl_cond_pol pol, char **err, struct arg_list *al,

View File

@ -97,14 +97,15 @@ struct arg_list *arg_list_add(struct arg_list *orig, struct arg *arg, int pos)
* The returned array is always terminated by an arg of type ARGT_STOP (0), * The returned array is always terminated by an arg of type ARGT_STOP (0),
* unless the mask indicates that no argument is supported. Unresolved arguments * unless the mask indicates that no argument is supported. Unresolved arguments
* are appended to arg list <al>, which also serves as a template to create new * 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 * entries. <al> may be NULL if unresolved arguments are not allowed. The mask
* ARGM_BITS bits, and a concatenation of each argument type in each subsequent * is composed of a number of mandatory arguments in its lower ARGM_BITS bits,
* ARGT_BITS-bit sblock. If <err_msg> is not NULL, it must point to a freeable * and a concatenation of each argument type in each subsequent ARGT_BITS-bit
* or NULL pointer. The caller is expected to restart the parsing from the new * sblock. If <err_msg> is not NULL, it must point to a freeable or NULL
* pointer set in <end_ptr>, which is the first character considered as not * pointer. The caller is expected to restart the parsing from the new pointer
* being part of the arg list. The input string ends on the first between <len> * set in <end_ptr>, which is the first character considered as not being part
* characters (when len is positive) or the first NUL character. Placing -1 in * of the arg list. The input string ends on the first between <len> characters
* <len> will make it virtually unbounded (~2GB long strings). * (when len is positive) or the first NUL character. Placing -1 in <len> will
* make it virtually unbounded (~2GB long strings).
*/ */
int make_arg_list(const char *in, int len, uint64_t mask, struct arg **argp, int make_arg_list(const char *in, int len, uint64_t mask, struct arg **argp,
char **err_msg, const char **end_ptr, int *err_arg, char **err_msg, const char **end_ptr, int *err_arg,
@ -245,6 +246,8 @@ int make_arg_list(const char *in, int len, uint64_t mask, struct arg **argp,
/* These argument types need to be stored as strings during /* These argument types need to be stored as strings during
* parsing then resolved later. * parsing then resolved later.
*/ */
if (!al)
goto resolve_err;
arg->unresolved = 1; arg->unresolved = 1;
new_al = arg_list_add(al, arg, pos); new_al = arg_list_add(al, arg, pos);
@ -447,6 +450,11 @@ int make_arg_list(const char *in, int len, uint64_t mask, struct arg **argp,
alloc_err: alloc_err:
memprintf(err_msg, "out of memory"); memprintf(err_msg, "out of memory");
goto err; goto err;
resolve_err:
memprintf(err_msg, "unresolved argument of type '%s' at position %d not allowed",
arg_type_names[(mask >> (pos * ARGT_BITS)) & ARGT_MASK], pos + 1);
goto err;
} }
/* Free all args of an args array, taking care of unresolved arguments as well. /* Free all args of an args array, taking care of unresolved arguments as well.

View File

@ -861,7 +861,9 @@ sample_cast_fct sample_casts[SMP_TYPES][SMP_TYPES] = {
* Parse a sample expression configuration: * Parse a sample expression configuration:
* fetch keyword followed by format conversion keywords. * fetch keyword followed by format conversion keywords.
* Returns a pointer on allocated sample expression structure. * Returns a pointer on allocated sample expression structure.
* The caller must have set al->ctx. * <al> is an arg_list serving as a list head to report missing dependencies.
* It may be NULL if such dependencies are not allowed. Otherwise, the caller
* must have set al->ctx if al is set.
* If <endptr> is non-nul, it will be set to the first unparsed character * If <endptr> is non-nul, it will be set to the first unparsed character
* (which may be the final '\0') on success. If it is nul, the expression * (which may be the final '\0') on success. If it is nul, the expression
* must be properly terminated by a '\0' otherwise an error is reported. * must be properly terminated by a '\0' otherwise an error is reported.
@ -920,8 +922,10 @@ struct sample_expr *sample_parse_expr(char **str, int *idx, const char *file, in
* this allows it to automatically create entries for mandatory * this allows it to automatically create entries for mandatory
* implicit arguments (eg: local proxy name). * implicit arguments (eg: local proxy name).
*/ */
al->kw = expr->fetch->kw; if (al) {
al->conv = NULL; al->kw = expr->fetch->kw;
al->conv = NULL;
}
if (make_arg_list(endw, -1, fetch->arg_mask, &expr->arg_p, err_msg, &endt, &err_arg, al) < 0) { if (make_arg_list(endw, -1, fetch->arg_mask, &expr->arg_p, err_msg, &endt, &err_arg, al) < 0) {
memprintf(err_msg, "fetch method '%s' : %s", fkw, *err_msg); memprintf(err_msg, "fetch method '%s' : %s", fkw, *err_msg);
goto out_error; goto out_error;
@ -1021,8 +1025,10 @@ struct sample_expr *sample_parse_expr(char **str, int *idx, const char *file, in
LIST_APPEND(&(expr->conv_exprs), &(conv_expr->list)); LIST_APPEND(&(expr->conv_exprs), &(conv_expr->list));
conv_expr->conv = conv; conv_expr->conv = conv;
al->kw = expr->fetch->kw; if (al) {
al->conv = conv_expr->conv->kw; al->kw = expr->fetch->kw;
al->conv = conv_expr->conv->kw;
}
argcnt = make_arg_list(endw, -1, conv->arg_mask, &conv_expr->arg_p, err_msg, &endt, &err_arg, al); argcnt = make_arg_list(endw, -1, conv->arg_mask, &conv_expr->arg_p, err_msg, &endt, &err_arg, al);
if (argcnt < 0) { if (argcnt < 0) {
memprintf(err_msg, "invalid arg %d in converter '%s' : %s", err_arg+1, ckw, *err_msg); memprintf(err_msg, "invalid arg %d in converter '%s' : %s", err_arg+1, ckw, *err_msg);