MINOR: cfgparse-listen: add and use cfg_parse_listen_match_option() helper

cfg_parse_listen_match_option() takes cfg_opt array as parameter, as well
current args, expected mode and cap bitfields.

It is expected to be used under cfg_parse_listen() function or similar.
Its goal is to remove code duplication around proxy->options and
proxy->options2 handling, since the same checks are performed for the
two. Also, this function could help to evaluate proxy options for
mode-specific proxies such as log-forward section for instance:
by giving the expected mode and capatiblity as input, the function
would only match compatible options.
This commit is contained in:
Aurelien DARRAGON 2025-03-05 20:55:41 +01:00
parent d9aa199100
commit 0746f6bde0
2 changed files with 77 additions and 66 deletions

View File

@ -23,6 +23,7 @@
#define _HAPROXY_CFGPARSE_H #define _HAPROXY_CFGPARSE_H
#include <haproxy/api.h> #include <haproxy/api.h>
#include <haproxy/proxy-t.h>
struct hap_cpuset; struct hap_cpuset;
struct proxy; struct proxy;
@ -112,6 +113,10 @@ extern struct proxy *curproxy;
int cfg_parse_global(const char *file, int linenum, char **args, int inv); int cfg_parse_global(const char *file, int linenum, char **args, int inv);
int cfg_parse_listen(const char *file, int linenum, char **args, int inv); int cfg_parse_listen(const char *file, int linenum, char **args, int inv);
int cfg_parse_listen_match_option(const char *file, int linenum, int kwm,
const struct cfg_opt config_opts[], int *err_code,
char **args, int mode, int cap,
int *options, int *no_options);
int cfg_parse_traces(const char *file, int linenum, char **args, int inv); int cfg_parse_traces(const char *file, int linenum, char **args, int inv);
int cfg_parse_track_sc_num(unsigned int *track_sc_num, int cfg_parse_track_sc_num(unsigned int *track_sc_num,
const char *arg, const char *end, char **err); const char *arg, const char *end, char **err);

View File

@ -238,6 +238,64 @@ int warnif_misplaced_quic_init(struct proxy *proxy, const char *file, int line,
warnif_misplaced_tcp_req_conn(proxy, file, line, arg1, arg2); warnif_misplaced_tcp_req_conn(proxy, file, line, arg1, arg2);
} }
/* helper function that checks for a match in cfg_opt array, for a given
* input args, capability (if <cap> != PR_CAP_NONE) and mode (if <mode> != PR_MODES)
*
* <options> and <no_options> will be set according to <kwm> if an option matches
*
* Returns 1 on success and 0 if no match
* <err_code> is updated accordingly and must be checked upon return
*/
int cfg_parse_listen_match_option(const char *file, int linenum, int kwm,
const struct cfg_opt config_opts[], int *err_code,
char **args, int mode, int cap,
int *options, int *no_options)
{
int optnum;
for (optnum = 0; config_opts[optnum].name; optnum++) {
if (strcmp(args[1], config_opts[optnum].name) == 0) {
if (config_opts[optnum].cap == PR_CAP_NONE) {
ha_alert("parsing [%s:%d]: option '%s' is not supported due to build options.\n",
file, linenum, config_opts[optnum].name);
*err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
if ((mode != PR_MODES && !(config_opts[optnum].mode & mode)) ||
(cap != PR_CAP_NONE && !(config_opts[optnum].cap & cap))) {
ha_alert("parsing [%s:%d]: option '%s' is not supported in this section.\n",
file, linenum, config_opts[optnum].name);
*err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
if (alertif_too_many_args_idx(0, 1, file, linenum, args, err_code))
goto out;
if (warnifnotcap(curproxy, config_opts[optnum].cap, file, linenum, args[1], NULL)) {
*err_code |= ERR_WARN;
goto out;
}
*no_options &= ~config_opts[optnum].val;
*options &= ~config_opts[optnum].val;
switch (kwm) {
case KWM_STD:
*options |= config_opts[optnum].val;
break;
case KWM_NO:
*no_options |= config_opts[optnum].val;
break;
case KWM_DEF: /* already cleared */
break;
}
return 1;
}
}
out:
return 0;
}
int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
{ {
static struct proxy *curr_defproxy = NULL; static struct proxy *curr_defproxy = NULL;
@ -1985,8 +2043,6 @@ stats_error_parsing:
} }
} }
else if (strcmp(args[0], "option") == 0) { else if (strcmp(args[0], "option") == 0) {
int optnum;
if (*(args[1]) == '\0') { if (*(args[1]) == '\0') {
ha_alert("parsing [%s:%d]: '%s' expects an option name.\n", ha_alert("parsing [%s:%d]: '%s' expects an option name.\n",
file, linenum, args[0]); file, linenum, args[0]);
@ -1994,71 +2050,21 @@ stats_error_parsing:
goto out; goto out;
} }
for (optnum = 0; cfg_opts[optnum].name; optnum++) { /* try to match option within cfg_opts */
if (strcmp(args[1], cfg_opts[optnum].name) == 0) { if (cfg_parse_listen_match_option(file, linenum, kwm, cfg_opts, &err_code, args,
if (cfg_opts[optnum].cap == PR_CAP_NONE) { PR_MODES, PR_CAP_NONE,
ha_alert("parsing [%s:%d]: option '%s' is not supported due to build options.\n", &curproxy->options, &curproxy->no_options))
file, linenum, cfg_opts[optnum].name); goto out;
err_code |= ERR_ALERT | ERR_FATAL; if (err_code & ERR_CODE)
goto out; goto out;
}
if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code))
goto out;
if (warnifnotcap(curproxy, cfg_opts[optnum].cap, file, linenum, args[1], NULL)) { /* try to match option within cfg_opts2 */
err_code |= ERR_WARN; if (cfg_parse_listen_match_option(file, linenum, kwm, cfg_opts2, &err_code, args,
goto out; PR_MODES, PR_CAP_NONE,
} &curproxy->options2, &curproxy->no_options2))
goto out;
curproxy->no_options &= ~cfg_opts[optnum].val; if (err_code & ERR_CODE)
curproxy->options &= ~cfg_opts[optnum].val; goto out;
switch (kwm) {
case KWM_STD:
curproxy->options |= cfg_opts[optnum].val;
break;
case KWM_NO:
curproxy->no_options |= cfg_opts[optnum].val;
break;
case KWM_DEF: /* already cleared */
break;
}
goto out;
}
}
for (optnum = 0; cfg_opts2[optnum].name; optnum++) {
if (strcmp(args[1], cfg_opts2[optnum].name) == 0) {
if (cfg_opts2[optnum].cap == PR_CAP_NONE) {
ha_alert("parsing [%s:%d]: option '%s' is not supported due to build options.\n",
file, linenum, cfg_opts2[optnum].name);
err_code |= ERR_ALERT | ERR_FATAL;
goto out;
}
if (alertif_too_many_args_idx(0, 1, file, linenum, args, &err_code))
goto out;
if (warnifnotcap(curproxy, cfg_opts2[optnum].cap, file, linenum, args[1], NULL)) {
err_code |= ERR_WARN;
goto out;
}
curproxy->no_options2 &= ~cfg_opts2[optnum].val;
curproxy->options2 &= ~cfg_opts2[optnum].val;
switch (kwm) {
case KWM_STD:
curproxy->options2 |= cfg_opts2[optnum].val;
break;
case KWM_NO:
curproxy->no_options2 |= cfg_opts2[optnum].val;
break;
case KWM_DEF: /* already cleared */
break;
}
goto out;
}
}
/* HTTP options override each other. They can be cancelled using /* HTTP options override each other. They can be cancelled using
* "no option xxx" which only switches to default mode if the mode * "no option xxx" which only switches to default mode if the mode