Hello Hyeonggeun, Thanks for your first patch, you should proceed in multiple steps to make the changes more readable:
- first a REORG patch that move the code to cfgparse-peers.c with minimal changes so we can review easily - second the removal of peers_register_keywords() and the new implementation with cfg_kw_list - then a final patch that add the new keywords I made a few comments inline: On Wed, Jan 14, 2026 at 04:08:23AM +0900, Hyeonggeun Oh wrote: > Subject: [PATCH v2] MINOR: cfgparse: Refactor "peers" to print it in -dKall > operation > --- > > Hello haproxy team. > > Changed few lines and cleaned up cfgparse-peers.c file. > Please ignore the previous patch mail. > I'll send mail more carefully next time. > > Thanks for reviewing. > > Regards, > Hyeonggeun Oh. > --- > > Hello haproxy team. > > This patch covers issue https://github.com/haproxy/haproxy/issues/3221. > And also following > https://www.mail-archive.com/[email protected]/msg46391.html. > > I've fixed bug that -dKall doesn't prints out "peers" section by registering > all sub keywords of "peers" and set new detect logic with > list_for_each_entry(). > > Since there are several branches and complex logics, I've created dedicate > file with cfg_parse_peers() function and new register logics in > src/cfgparse-peers.c. > > Also changed Makefile to link cfgparse-peers.o in build time. > > Since there are a little bit of difference between cfg_keyword and > peers_keyword structure, I wrote down a new list_for_each_entry() logic for > cfg_keyword parsing in parallel in cfgparse-peers.c. > > Regards, > Hyeonggeun. The commit message should use a neutral tone describing the changes in the patch. Instead of "I've fixed...", you should use "This commit fixes...", or use imperative sentences. Avoid the Hello and Regards, this is the commit message that will end up in the git repository. It also must be wrapped to 80 columns. > --- > Makefile | 2 +- > src/cfgparse-peers.c | 617 +++++++++++++++++++++++++++++++++++++++++++ > src/cfgparse.c | 576 +--------------------------------------- > 3 files changed, 619 insertions(+), 576 deletions(-) > create mode 100644 src/cfgparse-peers.c > > diff --git a/Makefile b/Makefile > index c4d1d0b72..41f99f3cb 100644 > --- a/Makefile > +++ b/Makefile > @@ -964,7 +964,7 @@ endif > OBJS += src/mux_h2.o src/mux_h1.o src/mux_fcgi.o src/log.o \ > src/server.o src/stream.o src/tcpcheck.o src/http_ana.o > \ > src/stick_table.o src/tools.o src/mux_spop.o src/sample.o \ > - src/activity.o src/cfgparse.o src/peers.o src/cli.o \ > + src/activity.o src/cfgparse.o src/cfgparse-peers.o src/peers.o > src/cli.o \ > src/backend.o src/connection.o src/resolvers.o src/proxy.o \ > src/cache.o src/stconn.o src/http_htx.o src/debug.o \ > src/check.o src/stats-html.o src/haproxy.o src/listener.o \ > diff --git a/src/cfgparse-peers.c b/src/cfgparse-peers.c > new file mode 100644 > index 000000000..244723765 > --- /dev/null > +++ b/src/cfgparse-peers.c Any new .c files must contain the SPDX entry for the licence: /* SPDX-License-Identifier: GPL-2.0-or-later */ > @@ -0,0 +1,617 @@ > +/* > + * Configuration parser for peers > + * > + * This file contains the moved logic from src/cfgparse.c for handling > "peers" sections. > + */ > + > +#define _GNU_SOURCE > + > +#include <ctype.h> > +#include <errno.h> > +#include <stdio.h> > +#include <stdlib.h> > +#include <string.h> > + > +#include <haproxy/api.h> > +#include <haproxy/cfgparse.h> > +#include <haproxy/errors.h> > +#include <haproxy/global.h> > +#include <haproxy/list.h> > +#include <haproxy/listener.h> > +#include <haproxy/log.h> > +#include <haproxy/peers.h> > +#include <haproxy/proxy.h> > +#include <haproxy/server.h> > +#include <haproxy/stick_table.h> > +#include <haproxy/tools.h> > + > +extern int parse_logger(char **args, struct list *logs, int reuse, const > char *file, int linenum, char **err); > +extern int alertif_too_many_args(int last, const char *file, int linenum, > char **args, int *err_code); > +extern int parse_server(const char *file, int linenum, char **args, struct > proxy *curproxy, const struct proxy *defproxy, int parse_flags); > +extern int parse_stick_table(const char *file, int linenum, char **args, > struct stktable *tab, char *id, char *name, struct peers *peers); > +extern char *cursection; > +extern struct cfg_kw_list cfg_keywords; > + You must not use "extern" directives, but include the right .h containing the definitions, I think you already have the required one, so you can just remove these extern lines. > +static struct peers *curpeers = NULL; > +static struct sockaddr_storage *bind_addr = NULL; > +static int nb_shards = 0; > +static int bind_line = 0; > +static int peer_line = 0; > +static struct peer *newpeer = NULL; > +static struct bind_conf *bind_conf = NULL; > +static char *errmsg = NULL; > +static int err_code = 0; > +static const char *err_msg = NULL; > + These ones must be local to the functions, not in global. Only the one that were already static in cfg_parse_peers() could be kept in global. > +/* Allocate and initialize the frontend of a "peers" section found in > + * file <file> at line <linenum> with <id> as ID. > + * Return 0 if succeeded, -1 if not. > + * Note that this function may be called from "default-server" > + * or "peer" lines. > + */ > +static int init_peers_frontend(const char *file, int linenum, > + const char *id, struct peers *peers) > +{ > + struct proxy *p; > + char *errmsg = NULL; > + > + if (peers->peers_fe) { > + p = peers->peers_fe; > + goto out; > + } > + > + p = alloc_new_proxy(NULL, PR_CAP_FE | PR_CAP_BE, &errmsg); > + if (!p) { > + ha_alert("parsing [%s:%d] : %s\n", file, linenum, errmsg); > + ha_free(&errmsg); > + return -1; > + } > + > + peers_setup_frontend(p); > + p->parent = peers; > + /* Finally store this frontend. */ > + peers->peers_fe = p; > + > + out: > + if (id && !p->id) > + p->id = strdup(id); > + drop_file_name(&p->conf.file); > + p->conf.args.file = p->conf.file = copy_file_name(file); > + if (linenum != -1) > + p->conf.args.line = p->conf.line = linenum; > + > + return 0; > +} > + > +/* Only change ->file, ->line and ->arg struct bind_conf member values > + * if already present. > + */ > +static struct bind_conf *bind_conf_uniq_alloc(struct proxy *p, > + const char *file, int line, > + const char *arg, struct > xprt_ops *xprt) > +{ > + struct bind_conf *bind_conf; > + > + if (!LIST_ISEMPTY(&p->conf.bind)) { > + bind_conf = LIST_ELEM((&p->conf.bind)->n, typeof(bind_conf), > by_fe); > + /* > + * We keep bind_conf->file and bind_conf->line unchanged > + * to make them available for error messages > + */ > + if (arg) { > + ha_free(&bind_conf->arg); > + bind_conf->arg = strdup(arg); > + } > + } > + else { > + bind_conf = bind_conf_alloc(p, file, line, arg, xprt); > + } > + > + return bind_conf; > +} > + > +/* > + * Allocate a new struct peer parsed at line <linenum> in file <file> > + * to be added to <peers>. > + * Returns the new allocated structure if succeeded, NULL if not. > + */ > +static struct peer *cfg_peers_add_peer(struct peers *peers, > + const char *file, int linenum, > + const char *id, int local) > +{ > + struct peer *p; > + > + p = calloc(1, sizeof *p); > + if (!p) { > + ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum); > + return NULL; > + } > + > + peers->count++; > + p->peers = peers; > + p->next = peers->remote; > + peers->remote = p; > + p->conf.file = strdup(file); > + p->conf.line = linenum; > + p->last_change = ns_to_sec(now_ns); > + HA_SPIN_INIT(&p->lock); > + if (id) > + p->id = strdup(id); > + if (local) { > + p->local = 1; > + peers->local = p; > + } > + > + return p; > +} > + > +int cfg_parse_peers_bind_default_bind(char **args, int section_type, struct > proxy *curproxy, const struct proxy *defproxy, const char *file, int linenum, > char **err) > +{ > + int cur_arg; > + int ret; > + > + cur_arg = 1; > + > + if (init_peers_frontend(file, linenum, NULL, curpeers) != 0) { > + err_code |= ERR_ALERT | ERR_ABORT; > + goto out; > + } > + > + bind_conf = bind_conf_uniq_alloc(curpeers->peers_fe, file, linenum, > + > args[1], xprt_get(XPRT_RAW)); > + if (!bind_conf) { > + ha_alert("parsing [%s:%d] : '%s %s' : cannot allocate > memory.\n", file, linenum, args[0], args[1]); > + err_code |= ERR_FATAL; > + goto out; > + } > + > + bind_conf->maxaccept = 1; > + bind_conf->accept = session_accept_fd; > + bind_conf->options |= BC_O_UNLIMITED; > + > + if (*args[0] == 'b') { > + struct listener *l; > + > + if (peer_line) { > + ha_alert("parsing [%s:%d] : mixing \"peer\" and > \"bind\" line is forbidden\n", file, linenum); > + err_code |= ERR_ALERT | ERR_FATAL; > + goto out; > + } > + > + if (!LIST_ISEMPTY(&bind_conf->listeners)) { > + ha_alert("parsing [%s:%d] : One listener per \"peers\" > section is authorized but another is already configured at [%s:%d].\n", file, > linenum, bind_conf->file, bind_conf->line); > + err_code |= ERR_FATAL; > + } > + > + if (!str2listener(args[1], curpeers->peers_fe, bind_conf, file, > linenum, &errmsg)) { > + if (errmsg) { > + indent_msg(&errmsg, 2); > + ha_alert("parsing [%s:%d] : '%s %s' : %s\n", > file, linenum, args[0], args[1], errmsg); > + } > + else > + ha_alert("parsing [%s:%d] : '%s %s' : error > encountered while parsing listening address %s.\n", > + file, linenum, args[0], > args[1], args[1]); > + err_code |= ERR_FATAL; > + goto out; > + } > + > + /* Only one listener supported. Compare first listener > + * against the last one. It must be the same one. > + */ > + if (bind_conf->listeners.n != bind_conf->listeners.p) { > + ha_alert("parsing [%s:%d] : Only one listener per > \"peers\" section is authorized. Multiple listening addresses or port range > are not supported.\n", file, linenum); > + err_code |= ERR_ALERT | ERR_FATAL; > + goto out; > + } > + /* > + * Newly allocated listener is at the end of the list > + */ > + l = LIST_ELEM(bind_conf->listeners.p, typeof(l), by_bind); > + bind_addr = &l->rx.addr; > + > + global.maxsock++; /* for the listening socket */ > + > + bind_line = 1; > + if (cfg_peers->local) { > + /* Local peer already defined using "server" line has no > + * address yet, we should update its server's addr:port > + * settings > + */ > + newpeer = cfg_peers->local; > + BUG_ON(!newpeer->srv); > + newpeer->srv->addr = *bind_addr; > + newpeer->srv->svc_port = get_host_port(bind_addr); > + } > + else { > + /* This peer is local. > + * Note that we do not set the peer ID. This latter is > initialized > + * when parsing "peer" or "server" line. > + */ > + newpeer = cfg_peers_add_peer(curpeers, file, linenum, > NULL, 1); > + if (!newpeer) { > + err_code |= ERR_ALERT | ERR_ABORT; > + goto out; > + } > + } > + cur_arg++; > + } > + > + ret = bind_parse_args_list(bind_conf, args, cur_arg, cursection, file, > linenum); > + err_code |= ret; > + if (ret != 0) > + goto out; > + > +out: > + ha_free(&errmsg); > + return err_code; > +} > + > + > +int cfg_parse_peers_log(char **args, int section_type, struct proxy *curpx, > const struct proxy *defpx, const char *file, int linenum, char **err) { > + if (init_peers_frontend(file, linenum, NULL, curpeers) != 0) { > + err_code |= ERR_ALERT | ERR_ABORT; > + goto out; > + } > + if (!parse_logger(args, &curpeers->peers_fe->loggers, (strcmp(args[0], > "no") == 0), file, linenum, &errmsg)) { > + ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], > errmsg); > + err_code |= ERR_ALERT | ERR_FATAL; > + goto out; > + } > +out: > + ha_free(&errmsg); > + return err_code; > +} > + > +int cfg_parse_peers_peers(char **args, int section_type, struct proxy > *curpx, const struct proxy *defpx, const char *file, int linenum, char **err) > { > + /* Initialize these static variables when entering a new "peers" > section*/ > + bind_line = peer_line = 0; > + bind_addr = NULL; > + if (!*args[1]) { > + ha_alert("parsing [%s:%d] : missing name for peers section.\n", > file, linenum); > + err_code |= ERR_ALERT | ERR_ABORT; > + goto out; > + } > + > + if (alertif_too_many_args(1, file, linenum, args, &err_code)) { > + err_code |= ERR_ABORT; > + goto out; > + } > + > + err_msg = invalid_char(args[1]); > + if (err_msg) { > + ha_alert("parsing [%s:%d] : character '%c' is not permitted in '%s' > name '%s'.\n", > + file, linenum, *err_msg, args[0], args[1]); > + err_code |= ERR_ALERT | ERR_ABORT; > + goto out; > + } > + > + for (curpeers = cfg_peers; curpeers != NULL; curpeers = curpeers->next) { > + if (strcmp(curpeers->id, args[1]) == 0) { > + ha_alert("Parsing [%s:%d]: peers section '%s' has the same name > as another peers section declared at %s:%d.\n", > + file, linenum, args[1], curpeers->conf.file, > curpeers->conf.line); > + err_code |= ERR_ALERT | ERR_FATAL; > + } > + } > + > + if ((curpeers = calloc(1, sizeof(*curpeers))) == NULL) { > + ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum); > + err_code |= ERR_ALERT | ERR_ABORT; > + goto out; > + } > + > + curpeers->next = cfg_peers; > + cfg_peers = curpeers; > + curpeers->conf.file = strdup(file); > + curpeers->conf.line = linenum; > + curpeers->last_change = ns_to_sec(now_ns); > + curpeers->id = strdup(args[1]); > + curpeers->disabled = 0; > + > +out: > + ha_free(&errmsg); > + return err_code; > +} > + > +int cfg_parse_peers_peer_server(char **args, int section_type, struct proxy > *curpx, const struct proxy *defpx, const char *file, int linenum, char **err) > { > + struct server *prev_srv; > + int local_peer, peer; > + int parse_addr = 0; > + > + peer = *args[0] == 'p'; > + local_peer = strcmp(args[1], localpeer) == 0; > + > + if (*args[0] == 'p') { > + if (bind_line) { > + ha_alert("parsing [%s:%d] : mixing \"peer\" and \"bind\" line is > forbidden\n", file, linenum); > + err_code |= ERR_ALERT | ERR_FATAL; > + goto out; > + } > + peer_line = 1; > + } > + if (cfg_peers->local && !cfg_peers->local->id && local_peer) { > + newpeer = cfg_peers->local; > + newpeer->id = strdup(localpeer); > + } > + else { > + if (local_peer && cfg_peers->local) { > + ha_alert("parsing [%s:%d] : '%s %s' : local peer name already > referenced at %s:%d. %s\n", > + file, linenum, args[0], args[1], > + curpeers->peers_fe->conf.file, curpeers->peers_fe->conf.line, > cfg_peers->local->id); > + err_code |= ERR_FATAL; > + goto out; > + } > + newpeer = cfg_peers_add_peer(curpeers, file, linenum, args[1], > local_peer); > + if (!newpeer) { > + err_code |= ERR_ALERT | ERR_ABORT; > + goto out; > + } > + } > + > + if (init_peers_frontend(file, > + newpeer->local ? linenum: -1, > + newpeer->local ? newpeer->id : NULL, > + curpeers) != 0) { > + err_code |= ERR_ALERT | ERR_ABORT; > + goto out; > + } > + > + parse_addr = (peer || !local_peer) ? SRV_PARSE_PARSE_ADDR : 0; > + prev_srv = curpeers->peers_fe->srv; > + err_code |= parse_server(file, linenum, args, curpeers->peers_fe, NULL, > + > SRV_PARSE_IN_PEER_SECTION|parse_addr|SRV_PARSE_INITIAL_RESOLVE); > + if (curpeers->peers_fe->srv == prev_srv) { > + struct peer *p; > + if (peer) { > + ha_warning("parsing [%s:%d] : '%s %s' : ignoring invalid peer > definition (missing address:port)\n", > + file, linenum, args[0], args[1]); > + err_code |= ERR_WARN; > + } > + else { > + ha_diag_warning("parsing [%s:%d] : '%s %s' : ignoring server > (not a local peer, valid address:port is expected)\n", > + file, linenum, args[0], args[1]); > + } > + > + p = curpeers->remote; > + curpeers->remote = curpeers->remote->next; > + ha_free(&p->id); > + ha_free(&p); > + if (local_peer) { > + curpeers->local = NULL; > + ha_free(&curpeers->peers_fe->id); > + } > + goto out; > + } > + > + if (!parse_addr && bind_addr) { > + curpeers->peers_fe->srv->addr = *bind_addr; > + curpeers->peers_fe->srv->svc_port = get_host_port(bind_addr); > + } > + > + if (nb_shards && curpeers->peers_fe->srv->shard > nb_shards) { > + ha_warning("parsing [%s:%d] : '%s %s' : %d peer shard greater value > than %d shards value is ignored.\n", > + file, linenum, args[0], args[1], > curpeers->peers_fe->srv->shard, nb_shards); > + curpeers->peers_fe->srv->shard = 0; > + err_code |= ERR_WARN; > + } > + > + if (curpeers->peers_fe->srv->init_addr_methods || > curpeers->peers_fe->srv->resolvers_id || > + curpeers->peers_fe->srv->do_check || > curpeers->peers_fe->srv->do_agent) { > + ha_warning("parsing [%s:%d] : '%s %s' : init_addr, resolvers, check > and agent are ignored for peers.\n", file, linenum, args[0], args[1]); > + err_code |= ERR_WARN; > + } > + > + HA_SPIN_INIT(&newpeer->lock); > + > + newpeer->srv = curpeers->peers_fe->srv; > + if (!newpeer->local) > + goto out; > + > + if (*args[0] == 's') > + goto out; > + > + bind_conf = bind_conf_uniq_alloc(curpeers->peers_fe, file, linenum, > args[2], xprt_get(XPRT_RAW)); > + if (!bind_conf) { > + ha_alert("parsing [%s:%d] : '%s %s' : Cannot allocate memory.\n", > file, linenum, args[0], args[1]); > + err_code |= ERR_FATAL; > + goto out; > + } > + > + bind_conf->maxaccept = 1; > + bind_conf->accept = session_accept_fd; > + bind_conf->options |= BC_O_UNLIMITED; > + > + if (!LIST_ISEMPTY(&bind_conf->listeners)) { > + ha_alert("parsing [%s:%d] : One listener per \"peers\" section is > authorized but another is already configured at [%s:%d].\n", file, linenum, > bind_conf->file, bind_conf->line); > + err_code |= ERR_FATAL; > + } > + > + if (!str2listener(args[2], curpeers->peers_fe, bind_conf, file, linenum, > &errmsg)) { > + if (errmsg) { > + indent_msg(&errmsg, 2); > + ha_alert("parsing [%s:%d] : '%s %s' : %s\n", file, linenum, > args[0], args[1], errmsg); > + } > + else > + ha_alert("parsing [%s:%d] : '%s %s' : error encountered while > parsing listening address %s.\n", > + file, linenum, args[0], args[1], args[2]); > + err_code |= ERR_FATAL; > + goto out; > + } > + > + global.maxsock++; > + goto out; > +out: > + ha_free(&errmsg); > + return err_code; > +} > + > +int cfg_parse_peers_shards(char **args, int section_type, struct proxy > *curpx, const struct proxy *defpx, const char *file, int linenum, char **err) > { > + char *endptr; > + > + if (!*args[1]) { > + ha_alert("parsing [%s:%d] : '%s' : missing value\n", file, linenum, > args[0]); > + err_code |= ERR_FATAL; > + goto out; > + } > + > + curpeers->nb_shards = strtol(args[1], &endptr, 10); > + if (*endptr != '\0') { > + ha_alert("parsing [%s:%d] : '%s' : expects an integer argument, > found '%s'\n", > + file, linenum, args[0], args[1]); > + err_code |= ERR_FATAL; > + goto out; > + } > + > + if (!curpeers->nb_shards) { > + ha_alert("parsing [%s:%d] : '%s' : expects a strictly positive > integer argument\n", > + file, linenum, args[0]); > + err_code |= ERR_FATAL; > + goto out; > + } > + > + nb_shards = curpeers->nb_shards; > +out: > + ha_free(&errmsg); > + return err_code; > +} > + > +int cfg_parse_peers_table(char **args, int section_type, struct proxy > *curpx, const struct proxy *defpx, const char *file, int linenum, char **err) > { > + struct stktable *t, *other; > + char *id; > + size_t prefix_len; > + > + if (init_peers_frontend(file, -1, NULL, curpeers) != 0) { > + err_code |= ERR_ALERT | ERR_ABORT; > + goto out; > + } > + > + chunk_reset(&trash); > + if (!chunk_strcpy(&trash, curpeers->id)) { > + ha_alert("parsing [%s:%d]: '%s %s' : stick-table name too long.\n", > + file, linenum, args[0], args[1]); > + err_code |= ERR_ALERT | ERR_FATAL; > + goto out; > + } > + > + prefix_len = trash.data; > + if (!chunk_memcat(&trash, "/", 1) || !chunk_strcat(&trash, args[1])) { > + ha_alert("parsing [%s:%d]: '%s %s' : stick-table name too long.\n", > + file, linenum, args[0], args[1]); > + err_code |= ERR_ALERT | ERR_FATAL; > + goto out; > + } > + > + t = calloc(1, sizeof *t); > + id = strdup(trash.area); > + if (!t || !id) { > + ha_alert("parsing [%s:%d]: '%s %s' : memory allocation failed\n", > + file, linenum, args[0], args[1]); > + ha_free(&t); > + ha_free(&id); > + err_code |= ERR_ALERT | ERR_FATAL; > + goto out; > + } > + > + other = stktable_find_by_name(trash.area); > + if (other) { > + ha_alert("parsing [%s:%d] : stick-table name '%s' conflicts with > table declared in %s '%s' at %s:%d.\n", > + file, linenum, args[1], > + other->proxy ? proxy_cap_str(other->proxy->cap) : "peers", > + other->proxy ? other->id : other->peers.p->id, > + other->conf.file, other->conf.line); > + err_code |= ERR_ALERT | ERR_FATAL; > + goto out; > + } > + > + > + err_code |= parse_stick_table(file, linenum, args, t, id, id + > prefix_len, curpeers); > + if (err_code & ERR_FATAL) { > + ha_free(&t); > + ha_free(&id); > + goto out; > + } > + > + stktable_store_name(t); > + t->next = stktables_list; > + stktables_list = t; > +out: > + ha_free(&errmsg); > + return err_code; > +} > + > +int cfg_parse_peers_disabled(char **args, int section_type, struct proxy > *curpx, const struct proxy *defpx, const char *file, int linenum, char **err) > { > + curpeers->disabled |= PR_FL_DISABLED; > + ha_free(&errmsg); > + return err_code; > +} > + > +int cfg_parse_peers_enabled(char **args, int section_type, struct proxy > *curpx, const struct proxy *defpx, const char *file, int linenum, char **err) > { > + curpeers->disabled = 0; > + ha_free(&errmsg); > + return err_code; > +} > + > +/* Helper for unknown keywords using peers_keywords list */ > +int cfg_parse_peers(const char *file, int linenum, char **args, int kwm) { > + struct peers_kw_list *pkwl; > + struct cfg_kw_list *kwl; > + int index; > + int rc = -1; > + > + list_for_each_entry(pkwl, &peers_keywords.list, list) { > + for (index = 0; pkwl->kw[index].kw != NULL; index++) { > + if (strcmp(pkwl->kw[index].kw, args[0]) == 0) { > + if (rc < 0) { > + ha_alert("parsing [%s:%d] : %s\n", file, linenum, > errmsg); /* errmsg might be confusing here, using global */ > + err_code |= ERR_ALERT | ERR_FATAL; > + goto out; > + } > + else if (rc > 0) { > + ha_warning("parsing [%s:%d] : %s\n", file, linenum, > errmsg); > + err_code |= ERR_WARN; > + goto out; > + } > + goto out; > + } > + } > + } > + > + list_for_each_entry(kwl, &cfg_keywords.list, list) { > + for (index = 0; kwl->kw[index].kw != NULL; index++) { > + if ((kwl->kw[index].section & CFG_PEERS) && > + (strcmp(kwl->kw[index].kw, args[0]) == 0)) { > + err_code = kwl->kw[index].parse(args, CFG_PEERS, NULL, > NULL, file, linenum, &errmsg); > + if (errmsg) { > + ha_alert("parsing [%s:%d] : %s\n", file, linenum, > errmsg); > + err_code |= ERR_ALERT | ERR_FATAL; > + goto out; > + } > + goto out; > + } > + } > + } > + I don't get what happened there, it seems like we already have a system for that. With your patch we now have 2 iterations for the the peers keywords. We only need one system, there's no need for 2. The previous system using peers_register_keywords() is probably not generic enough, and does not seem to use the cfg_kw_list but a specific peers_kw_list. We should probably have a patch that converts the previous system to cfg_kw_list. > + ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section\n", > file, linenum, args[0], cursection); > + err_code |= ERR_ALERT | ERR_FATAL; > + goto out; > + > +out: > + ha_free(&errmsg); > + return err_code; > +} > + > +REGISTER_CONFIG_SECTION("peers", cfg_parse_peers, NULL); > + > +static struct cfg_kw_list cfg_kws = {{ }, { > + { CFG_PEERS, "bind", cfg_parse_peers_bind_default_bind }, > + { CFG_PEERS, "default-bind", cfg_parse_peers_bind_default_bind }, > + { CFG_PEERS, "log", cfg_parse_peers_log }, > + { CFG_PEERS, "peers", cfg_parse_peers_peers }, > + { CFG_PEERS, "peer", cfg_parse_peers_peer_server }, > + { CFG_PEERS, "server", cfg_parse_peers_peer_server }, > + { CFG_PEERS, "shards", cfg_parse_peers_shards }, > + { CFG_PEERS, "table", cfg_parse_peers_table }, > + { CFG_PEERS, "disabled", cfg_parse_peers_disabled }, > + { CFG_PEERS, "enabled", cfg_parse_peers_enabled }, > +}}; > + > +INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws); > \ No newline at end of file > diff --git a/src/cfgparse.c b/src/cfgparse.c > index 4cda0e1ab..d6418b782 100644 > --- a/src/cfgparse.c > +++ b/src/cfgparse.c > @@ -549,579 +549,6 @@ int parse_process_number(const char *arg, unsigned long > *proc, int max, int *aut > return 0; > } > > -/* Allocate and initialize the frontend of a "peers" section found in > - * file <file> at line <linenum> with <id> as ID. > - * Return 0 if succeeded, -1 if not. > - * Note that this function may be called from "default-server" > - * or "peer" lines. > - */ > -static int init_peers_frontend(const char *file, int linenum, > - const char *id, struct peers *peers) > -{ > - struct proxy *p; > - char *errmsg = NULL; > - > - if (peers->peers_fe) { > - p = peers->peers_fe; > - goto out; > - } > - > - p = alloc_new_proxy(NULL, PR_CAP_FE | PR_CAP_BE, &errmsg); > - if (!p) { > - ha_alert("parsing [%s:%d] : %s\n", file, linenum, errmsg); > - ha_free(&errmsg); > - return -1; > - } > - > - peers_setup_frontend(p); > - p->parent = peers; > - /* Finally store this frontend. */ > - peers->peers_fe = p; > - > - out: > - if (id && !p->id) > - p->id = strdup(id); > - drop_file_name(&p->conf.file); > - p->conf.args.file = p->conf.file = copy_file_name(file); > - if (linenum != -1) > - p->conf.args.line = p->conf.line = linenum; > - > - return 0; > -} > - > -/* Only change ->file, ->line and ->arg struct bind_conf member values > - * if already present. > - */ > -static struct bind_conf *bind_conf_uniq_alloc(struct proxy *p, > - const char *file, int line, > - const char *arg, struct > xprt_ops *xprt) > -{ > - struct bind_conf *bind_conf; > - > - if (!LIST_ISEMPTY(&p->conf.bind)) { > - bind_conf = LIST_ELEM((&p->conf.bind)->n, typeof(bind_conf), > by_fe); > - /* > - * We keep bind_conf->file and bind_conf->line unchanged > - * to make them available for error messages > - */ > - if (arg) { > - free(bind_conf->arg); > - bind_conf->arg = strdup(arg); > - } > - } > - else { > - bind_conf = bind_conf_alloc(p, file, line, arg, xprt); > - } > - > - return bind_conf; > -} > - > -/* > - * Allocate a new struct peer parsed at line <linenum> in file <file> > - * to be added to <peers>. > - * Returns the new allocated structure if succeeded, NULL if not. > - */ > -static struct peer *cfg_peers_add_peer(struct peers *peers, > - const char *file, int linenum, > - const char *id, int local) > -{ > - struct peer *p; > - > - p = calloc(1, sizeof *p); > - if (!p) { > - ha_alert("parsing [%s:%d] : out of memory.\n", file, linenum); > - return NULL; > - } > - > - /* the peers are linked backwards first */ > - peers->count++; > - p->peers = peers; > - p->next = peers->remote; > - peers->remote = p; > - p->conf.file = strdup(file); > - p->conf.line = linenum; > - p->last_change = ns_to_sec(now_ns); > - HA_SPIN_INIT(&p->lock); > - if (id) > - p->id = strdup(id); > - if (local) { > - p->local = 1; > - peers->local = p; > - } > - > - return p; > -} > - > -/* > - * Parse a line in a <listen>, <frontend> or <backend> section. > - * Returns the error code, 0 if OK, or any combination of : > - * - ERR_ABORT: must abort ASAP > - * - ERR_FATAL: we can continue parsing but not start the service > - * - ERR_WARN: a warning has been emitted > - * - ERR_ALERT: an alert has been emitted > - * Only the two first ones can stop processing, the two others are just > - * indicators. > - */ > -int cfg_parse_peers(const char *file, int linenum, char **args, int kwm) > -{ > - static struct peers *curpeers = NULL; > - static struct sockaddr_storage *bind_addr = NULL; > - static int nb_shards = 0; > - struct peer *newpeer = NULL; > - const char *err; > - struct bind_conf *bind_conf; > - int err_code = 0; > - char *errmsg = NULL; > - static int bind_line, peer_line; > - > - if (strcmp(args[0], "bind") == 0 || strcmp(args[0], "default-bind") == > 0) { > - int cur_arg; > - struct bind_conf *bind_conf; > - int ret; > - > - cur_arg = 1; > - > - if (init_peers_frontend(file, linenum, NULL, curpeers) != 0) { > - err_code |= ERR_ALERT | ERR_ABORT; > - goto out; > - } > - > - bind_conf = bind_conf_uniq_alloc(curpeers->peers_fe, file, > linenum, > - args[1], xprt_get(XPRT_RAW)); > - if (!bind_conf) { > - ha_alert("parsing [%s:%d] : '%s %s' : cannot allocate > memory.\n", file, linenum, args[0], args[1]); > - err_code |= ERR_FATAL; > - goto out; > - } > - > - bind_conf->maxaccept = 1; > - bind_conf->accept = session_accept_fd; > - bind_conf->options |= BC_O_UNLIMITED; /* don't make the peers > subject to global limits */ > - > - if (*args[0] == 'b') { > - struct listener *l; > - > - if (peer_line) { > - ha_alert("parsing [%s:%d] : mixing \"peer\" and > \"bind\" line is forbidden\n", file, linenum); > - err_code |= ERR_ALERT | ERR_FATAL; > - goto out; > - } > - > - if (!LIST_ISEMPTY(&bind_conf->listeners)) { > - ha_alert("parsing [%s:%d] : One listener per > \"peers\" section is authorized but another is already configured at > [%s:%d].\n", file, linenum, bind_conf->file, bind_conf->line); > - err_code |= ERR_FATAL; > - } > - > - if (!str2listener(args[1], curpeers->peers_fe, > bind_conf, file, linenum, &errmsg)) { > - if (errmsg && *errmsg) { > - indent_msg(&errmsg, 2); > - ha_alert("parsing [%s:%d] : '%s %s' : > %s\n", file, linenum, args[0], args[1], errmsg); > - } > - else > - ha_alert("parsing [%s:%d] : '%s %s' : > error encountered while parsing listening address %s.\n", > - file, linenum, > args[0], args[1], args[1]); > - err_code |= ERR_FATAL; > - goto out; > - } > - > - /* Only one listener supported. Compare first listener > - * against the last one. It must be the same one. > - */ > - if (bind_conf->listeners.n != bind_conf->listeners.p) { > - ha_alert("parsing [%s:%d] : Only one listener > per \"peers\" section is authorized. Multiple listening addresses or port > range are not supported.\n", file, linenum); > - err_code |= ERR_ALERT | ERR_FATAL; > - goto out; > - } > - /* > - * Newly allocated listener is at the end of the list > - */ > - l = LIST_ELEM(bind_conf->listeners.p, typeof(l), > by_bind); > - bind_addr = &l->rx.addr; > - > - global.maxsock++; /* for the listening socket */ > - > - bind_line = 1; > - if (cfg_peers->local) { > - /* Local peer already defined using "server" > line has no > - * address yet, we should update its server's > addr:port > - * settings > - */ > - newpeer = cfg_peers->local; > - BUG_ON(!newpeer->srv); > - newpeer->srv->addr = *bind_addr; > - newpeer->srv->svc_port = > get_host_port(bind_addr); > - } > - else { > - /* This peer is local. > - * Note that we do not set the peer ID. This > latter is initialized > - * when parsing "peer" or "server" line. > - */ > - newpeer = cfg_peers_add_peer(curpeers, file, > linenum, NULL, 1); > - if (!newpeer) { > - err_code |= ERR_ALERT | ERR_ABORT; > - goto out; > - } > - } > - cur_arg++; > - } > - > - ret = bind_parse_args_list(bind_conf, args, cur_arg, > cursection, file, linenum); > - err_code |= ret; > - if (ret != 0) > - goto out; > - } > - else if (strcmp(args[0], "default-server") == 0) { > - if (init_peers_frontend(file, -1, NULL, curpeers) != 0) { > - err_code |= ERR_ALERT | ERR_ABORT; > - goto out; > - } > - err_code |= parse_server(file, linenum, args, > curpeers->peers_fe, NULL, > - > SRV_PARSE_DEFAULT_SERVER|SRV_PARSE_IN_PEER_SECTION|SRV_PARSE_INITIAL_RESOLVE); > - } > - else if (strcmp(args[0], "log") == 0) { > - if (init_peers_frontend(file, linenum, NULL, curpeers) != 0) { > - err_code |= ERR_ALERT | ERR_ABORT; > - goto out; > - } > - if (!parse_logger(args, &curpeers->peers_fe->loggers, (kwm == > KWM_NO), file, linenum, &errmsg)) { > - ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, > args[0], errmsg); > - err_code |= ERR_ALERT | ERR_FATAL; > - goto out; > - } > - } > - else if (strcmp(args[0], "peers") == 0) { /* new peers section */ > - /* Initialize these static variables when entering a new > "peers" section*/ > - bind_line = peer_line = 0; > - bind_addr = NULL; > - if (!*args[1]) { > - ha_alert("parsing [%s:%d] : missing name for peers > section.\n", file, linenum); > - err_code |= ERR_ALERT | ERR_ABORT; > - goto out; > - } > - > - if (alertif_too_many_args(1, file, linenum, args, &err_code)) { > - err_code |= ERR_ABORT; > - goto out; > - } > - > - err = invalid_char(args[1]); > - if (err) { > - ha_alert("parsing [%s:%d] : character '%c' is not > permitted in '%s' name '%s'.\n", > - file, linenum, *err, args[0], args[1]); > - err_code |= ERR_ALERT | ERR_ABORT; > - goto out; > - } > - > - for (curpeers = cfg_peers; curpeers != NULL; curpeers = > curpeers->next) { > - /* > - * If there are two proxies with the same name only > following > - * combinations are allowed: > - */ > - if (strcmp(curpeers->id, args[1]) == 0) { > - ha_alert("Parsing [%s:%d]: peers section '%s' > has the same name as another peers section declared at %s:%d.\n", > - file, linenum, args[1], > curpeers->conf.file, curpeers->conf.line); > - err_code |= ERR_ALERT | ERR_FATAL; > - } > - } > - > - if ((curpeers = calloc(1, sizeof(*curpeers))) == NULL) { > - ha_alert("parsing [%s:%d] : out of memory.\n", file, > linenum); > - err_code |= ERR_ALERT | ERR_ABORT; > - goto out; > - } > - > - curpeers->next = cfg_peers; > - cfg_peers = curpeers; > - curpeers->conf.file = strdup(file); > - curpeers->conf.line = linenum; > - curpeers->last_change = ns_to_sec(now_ns); > - curpeers->id = strdup(args[1]); > - curpeers->disabled = 0; > - } > - else if (strcmp(args[0], "peer") == 0 || > - strcmp(args[0], "server") == 0) { /* peer or server definition > */ > - struct server *prev_srv; > - int local_peer, peer; > - int parse_addr = 0; > - > - peer = *args[0] == 'p'; > - local_peer = strcmp(args[1], localpeer) == 0; > - /* The local peer may have already partially been parsed on a > "bind" line. */ > - if (*args[0] == 'p') { > - if (bind_line) { > - ha_alert("parsing [%s:%d] : mixing \"peer\" and > \"bind\" line is forbidden\n", file, linenum); > - err_code |= ERR_ALERT | ERR_FATAL; > - goto out; > - } > - peer_line = 1; > - } > - if (cfg_peers->local && !cfg_peers->local->id && local_peer) { > - /* The local peer has already been initialized on a > "bind" line. > - * Let's use it and store its ID. > - */ > - newpeer = cfg_peers->local; > - newpeer->id = strdup(localpeer); > - } > - else { > - if (local_peer && cfg_peers->local) { > - ha_alert("parsing [%s:%d] : '%s %s' : local > peer name already referenced at %s:%d. %s\n", > - file, linenum, args[0], args[1], > - curpeers->peers_fe->conf.file, > curpeers->peers_fe->conf.line, cfg_peers->local->id); > - err_code |= ERR_FATAL; > - goto out; > - } > - newpeer = cfg_peers_add_peer(curpeers, file, linenum, > args[1], local_peer); > - if (!newpeer) { > - err_code |= ERR_ALERT | ERR_ABORT; > - goto out; > - } > - } > - > - /* Line number and peer ID are updated only if this peer is the > local one. */ > - if (init_peers_frontend(file, > - newpeer->local ? linenum: -1, > - newpeer->local ? newpeer->id : NULL, > - curpeers) != 0) { > - err_code |= ERR_ALERT | ERR_ABORT; > - goto out; > - } > - > - /* This initializes curpeer->peers->peers_fe->srv. > - * The server address is parsed only if we are parsing a "peer" > line, > - * or if we are parsing a "server" line and the current peer is > not the local one. > - */ > - parse_addr = (peer || !local_peer) ? SRV_PARSE_PARSE_ADDR : 0; > - prev_srv = curpeers->peers_fe->srv; > - err_code |= parse_server(file, linenum, args, > curpeers->peers_fe, NULL, > - > SRV_PARSE_IN_PEER_SECTION|parse_addr|SRV_PARSE_INITIAL_RESOLVE); > - if (curpeers->peers_fe->srv == prev_srv) { > - /* parse_server didn't add a server: > - * Remove the newly allocated peer. > - */ > - struct peer *p; > - > - /* while it is tolerated to have a "server" line > without address, it isn't > - * the case for a "peer" line > - */ > - if (peer) { > - ha_warning("parsing [%s:%d] : '%s %s' : > ignoring invalid peer definition (missing address:port)\n", > - file, linenum, args[0], args[1]); > - err_code |= ERR_WARN; > - } > - else { > - ha_diag_warning("parsing [%s:%d] : '%s %s' : > ignoring server (not a local peer, valid address:port is expected)\n", > - file, linenum, args[0], > args[1]); > - } > - > - p = curpeers->remote; > - curpeers->remote = curpeers->remote->next; > - free(p->id); > - free(p); > - if (local_peer) { > - /* we only get there with incomplete "peer" > - * line for local peer (missing address): > - * > - * reset curpeers and curpeers fields > - * that are local peer related > - */ > - curpeers->local = NULL; > - ha_free(&curpeers->peers_fe->id); > - } > - goto out; > - } > - > - if (!parse_addr && bind_addr) { > - /* local peer declared using "server": has name but no > - * address: we use the known "bind" line addr settings > - * as implicit server's addr and port. > - */ > - curpeers->peers_fe->srv->addr = *bind_addr; > - curpeers->peers_fe->srv->svc_port = > get_host_port(bind_addr); > - } > - > - if (nb_shards && curpeers->peers_fe->srv->shard > nb_shards) { > - ha_warning("parsing [%s:%d] : '%s %s' : %d peer shard > greater value than %d shards value is ignored.\n", > - file, linenum, args[0], args[1], > curpeers->peers_fe->srv->shard, nb_shards); > - curpeers->peers_fe->srv->shard = 0; > - err_code |= ERR_WARN; > - } > - > - if (curpeers->peers_fe->srv->init_addr_methods || > curpeers->peers_fe->srv->resolvers_id || > - curpeers->peers_fe->srv->do_check || > curpeers->peers_fe->srv->do_agent) { > - ha_warning("parsing [%s:%d] : '%s %s' : init_addr, > resolvers, check and agent are ignored for peers.\n", file, linenum, args[0], > args[1]); > - err_code |= ERR_WARN; > - } > - > - HA_SPIN_INIT(&newpeer->lock); > - > - newpeer->srv = curpeers->peers_fe->srv; > - if (!newpeer->local) > - goto out; > - > - /* The lines above are reserved to "peer" lines. */ > - if (*args[0] == 's') > - goto out; > - > - bind_conf = bind_conf_uniq_alloc(curpeers->peers_fe, file, > linenum, args[2], xprt_get(XPRT_RAW)); > - if (!bind_conf) { > - ha_alert("parsing [%s:%d] : '%s %s' : Cannot allocate > memory.\n", file, linenum, args[0], args[1]); > - err_code |= ERR_FATAL; > - goto out; > - } > - > - bind_conf->maxaccept = 1; > - bind_conf->accept = session_accept_fd; > - bind_conf->options |= BC_O_UNLIMITED; /* don't make the peers > subject to global limits */ > - > - if (!LIST_ISEMPTY(&bind_conf->listeners)) { > - ha_alert("parsing [%s:%d] : One listener per \"peers\" > section is authorized but another is already configured at [%s:%d].\n", file, > linenum, bind_conf->file, bind_conf->line); > - err_code |= ERR_FATAL; > - } > - > - if (!str2listener(args[2], curpeers->peers_fe, bind_conf, file, > linenum, &errmsg)) { > - if (errmsg && *errmsg) { > - indent_msg(&errmsg, 2); > - ha_alert("parsing [%s:%d] : '%s %s' : %s\n", > file, linenum, args[0], args[1], errmsg); > - } > - else > - ha_alert("parsing [%s:%d] : '%s %s' : error > encountered while parsing listening address %s.\n", > - file, linenum, args[0], args[1], > args[2]); > - err_code |= ERR_FATAL; > - goto out; > - } > - > - global.maxsock++; /* for the listening socket */ > - } > - else if (strcmp(args[0], "shards") == 0) { > - char *endptr; > - > - if (!*args[1]) { > - ha_alert("parsing [%s:%d] : '%s' : missing value\n", > file, linenum, args[0]); > - err_code |= ERR_FATAL; > - goto out; > - } > - > - curpeers->nb_shards = strtol(args[1], &endptr, 10); > - if (*endptr != '\0') { > - ha_alert("parsing [%s:%d] : '%s' : expects an integer > argument, found '%s'\n", > - file, linenum, args[0], args[1]); > - err_code |= ERR_FATAL; > - goto out; > - } > - > - if (!curpeers->nb_shards) { > - ha_alert("parsing [%s:%d] : '%s' : expects a strictly > positive integer argument\n", > - file, linenum, args[0]); > - err_code |= ERR_FATAL; > - goto out; > - } > - > - nb_shards = curpeers->nb_shards; > - } > - else if (strcmp(args[0], "table") == 0) { > - struct stktable *t, *other; > - char *id; > - size_t prefix_len; > - > - /* Line number and peer ID are updated only if this peer is the > local one. */ > - if (init_peers_frontend(file, -1, NULL, curpeers) != 0) { > - err_code |= ERR_ALERT | ERR_ABORT; > - goto out; > - } > - > - /* Build the stick-table name, concatenating the "peers" > section name > - * followed by a '/' character and the table name argument. > - */ > - chunk_reset(&trash); > - if (!chunk_strcpy(&trash, curpeers->id)) { > - ha_alert("parsing [%s:%d]: '%s %s' : stick-table name > too long.\n", > - file, linenum, args[0], args[1]); > - err_code |= ERR_ALERT | ERR_FATAL; > - goto out; > - } > - > - prefix_len = trash.data; > - if (!chunk_memcat(&trash, "/", 1) || !chunk_strcat(&trash, > args[1])) { > - ha_alert("parsing [%s:%d]: '%s %s' : stick-table name > too long.\n", > - file, linenum, args[0], args[1]); > - err_code |= ERR_ALERT | ERR_FATAL; > - goto out; > - } > - > - t = calloc(1, sizeof *t); > - id = strdup(trash.area); > - if (!t || !id) { > - ha_alert("parsing [%s:%d]: '%s %s' : memory allocation > failed\n", > - file, linenum, args[0], args[1]); > - free(t); > - free(id); > - err_code |= ERR_ALERT | ERR_FATAL; > - goto out; > - } > - > - other = stktable_find_by_name(trash.area); > - if (other) { > - ha_alert("parsing [%s:%d] : stick-table name '%s' > conflicts with table declared in %s '%s' at %s:%d.\n", > - file, linenum, args[1], > - other->proxy ? > proxy_cap_str(other->proxy->cap) : "peers", > - other->proxy ? other->id : other->peers.p->id, > - other->conf.file, other->conf.line); > - err_code |= ERR_ALERT | ERR_FATAL; > - goto out; > - } > - > - > - err_code |= parse_stick_table(file, linenum, args, t, id, id + > prefix_len, curpeers); > - if (err_code & ERR_FATAL) { > - free(t); > - free(id); > - goto out; > - } > - > - stktable_store_name(t); > - t->next = stktables_list; > - stktables_list = t; > - } > - else if (strcmp(args[0], "disabled") == 0) { /* disables this peers > section */ > - curpeers->disabled |= PR_FL_DISABLED; > - } > - else if (strcmp(args[0], "enabled") == 0) { /* enables this peers > section (used to revert a disabled default) */ > - curpeers->disabled = 0; > - } > - else if (*args[0] != 0) { > - struct peers_kw_list *pkwl; > - int index; > - int rc = -1; > - > - list_for_each_entry(pkwl, &peers_keywords.list, list) { > - for (index = 0; pkwl->kw[index].kw != NULL; index++) { > - if (strcmp(pkwl->kw[index].kw, args[0]) == 0) { > - rc = pkwl->kw[index].parse(args, > curpeers, file, linenum, &errmsg); > - if (rc < 0) { > - ha_alert("parsing [%s:%d] : > %s\n", file, linenum, errmsg); > - err_code |= ERR_ALERT | > ERR_FATAL; > - goto out; > - } > - else if (rc > 0) { > - ha_warning("parsing [%s:%d] : > %s\n", file, linenum, errmsg); > - err_code |= ERR_WARN; > - goto out; > - } > - goto out; > - } > - } > - } > - > - ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' > section\n", file, linenum, args[0], cursection); > - err_code |= ERR_ALERT | ERR_FATAL; > - goto out; > - } > - > -out: > - free(errmsg); > - return err_code; > -} > - > /* > * Parse a line in a <listen>, <frontend> or <backend> section. > * Returns the error code, 0 if OK, or any combination of : > @@ -4971,12 +4398,11 @@ REGISTER_CONFIG_SECTION("backend", > cfg_parse_listen, NULL); > REGISTER_CONFIG_SECTION("defaults", cfg_parse_listen, NULL); > REGISTER_CONFIG_SECTION("global", cfg_parse_global, NULL); > REGISTER_CONFIG_SECTION("userlist", cfg_parse_users, NULL); > -REGISTER_CONFIG_SECTION("peers", cfg_parse_peers, NULL); > REGISTER_CONFIG_SECTION("mailers", cfg_parse_mailers, NULL); > REGISTER_CONFIG_SECTION("namespace_list", cfg_parse_netns, NULL); > REGISTER_CONFIG_SECTION("traces", cfg_parse_traces, NULL); > > -static struct cfg_kw_list cfg_kws = {{ },{ > +static struct cfg_kw_list cfg_kws = {{ }, { > { CFG_GLOBAL, "default-path", cfg_parse_global_def_path }, > { CFG_USERLIST, "group", cfg_parse_users_group }, > { CFG_USERLIST, "user", cfg_parse_users_user }, > -- > 2.48.1 > > > -- William Lallemand

