Hello,

Thank you, looks good to me. I just changed a little bit your commit message 
and wrap it to 80 columns. I also added a
fix before your commit on the error message which was mentioning "users" 
instead of "userlist" when a keyword was not
found !

Merged in master!

Regards,

On Tue, Jan 06, 2026 at 01:22:12PM +0900, Hyeonggeun Oh wrote:
> Subject: [PATCH] MINOR: cfgparse: Refactor "userlist" parser to print it in 
> -dKall operation
> Hello haproxy team.
> This patch covers issue https://github.com/haproxy/haproxy/issues/3221.
> 
> Before working keyword "peers", which has bigger loggic than "userlist", I've 
> fixed "userlist" keyword first to check and make consensus with maintainers.
> 
> The parser for the "userlist" section did not use the standard keyword 
> registration mechanism. Instead, it relied on a series of strcmp() 
> comparisons to identify keywords such as "group" and "user".
> 
> This had two main drawbacks:
> 1. The keywords were not discoverable by the "-dKall" dump option, making it 
> difficult for users to see all available keywords for the section.
> 2. The implementation was inconsistent with the parsers for other sections, 
> which have been progressively refactored to use the standard cfg_kw_list 
> infrastructure.
> 
> This patch refactors the userlist parser to align it with the project's 
> standard conventions.
> 
> The parsing logic for the "group" and "user" keywords has been extracted from 
> the if/else block in cfg_parse_users() into two new dedicated functions:
> - cfg_parse_users_group()
> - cfg_parse_users_user()
> 
> These two keywords are now registered via a dedicated cfg_kw_list, making 
> them visible to the rest of the HAPorxy ecosystem, including the -dKall dump.
> 
> I hope that this change makes the userlist parser cleaner, and furthermore, 
> provides a clear pattern for refactoring other sections like "peers" or the 
> other keywords.
> ---
>  src/cfgparse.c | 259 ++++++++++++++++++++++++++++---------------------
>  1 file changed, 146 insertions(+), 113 deletions(-)
> 
> diff --git a/src/cfgparse.c b/src/cfgparse.c
> index 5a7ec7405..08cd3e0d1 100644
> --- a/src/cfgparse.c
> +++ b/src/cfgparse.c
> @@ -1389,153 +1389,184 @@ cfg_parse_users(const char *file, int linenum, char 
> **args, int kwm)
>               newul->next = userlist;
>               userlist = newul;
>  
> -     } else if (strcmp(args[0], "group") == 0) {     /* new group */
> -             int cur_arg;
> -             const char *err;
> -             struct auth_groups *ag;
> +     } else {
> +             const struct cfg_kw_list *kwl;
> +             char *errmsg = NULL;
> +             int index;
>  
> -             if (!*args[1]) {
> -                     ha_alert("parsing [%s:%d]: '%s' expects <name> as 
> arguments.\n",
> -                              file, linenum, args[0]);
> -                     err_code |= ERR_ALERT | ERR_FATAL;
> -                     goto out;
> +             list_for_each_entry(kwl, &cfg_keywords.list, list) {
> +                     for (index = 0; kwl->kw[index].kw; index++) {
> +                             if ((kwl->kw[index].section & CFG_USERLIST) &&
> +                                     (strcmp(kwl->kw[index].kw, args[0]) == 
> 0)) {
> +                                             err_code |= 
> kwl->kw[index].parse(args, CFG_USERLIST, NULL, NULL, file, linenum, &errmsg);
> +                                             if (errmsg) {
> +                                                     ha_alert("parsing 
> [%s:%d] : %s\n", file, linenum, errmsg);
> +                                                     ha_free(&errmsg);
> +                                             }
> +                                             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_FATAL;
> -                     goto out;
> -             }
> +             ha_alert("parsing [%s:%d]: unknown keyword '%s' in '%s' 
> section\n", file, linenum, args[0], "users");
> +             err_code |= ERR_ALERT | ERR_FATAL;
> +     }
>  
> -             if (!userlist)
> -                     goto out;
> +out:
> +     return err_code;
> +}
>  
> -             for (ag = userlist->groups; ag; ag = ag->next)
> -                     if (strcmp(ag->name, args[1]) == 0) {
> -                             ha_warning("parsing [%s:%d]: ignoring 
> duplicated group '%s' in userlist '%s'.\n",
> -                                        file, linenum, args[1], 
> userlist->name);
> -                             err_code |= ERR_ALERT;
> -                             goto out;
> -                     }
> +int cfg_parse_users_group(char **args, int section_type, struct proxy 
> *curproxy, const struct proxy *defproxy, const char *file, int linenum, char 
> **err)
> +{
> +     int cur_arg;
> +     const char *err_str;
> +     struct auth_groups *ag;
> +     int err_code = 0;
>  
> -             ag = calloc(1, sizeof(*ag));
> -             if (!ag) {
> -                     ha_alert("parsing [%s:%d]: out of memory.\n", file, 
> linenum);
> -                     err_code |= ERR_ALERT | ERR_ABORT;
> -                     goto out;
> -             }
> +     if (!*args[1]) {
> +             ha_alert("parsing [%s:%d]: '%s' expects <name> as arguments.\n",
> +                             file, linenum, args[0]);
> +             err_code |= ERR_ALERT | ERR_FATAL;
> +             goto out;
> +     }
>  
> -             ag->name = strdup(args[1]);
> -             if (!ag->name) {
> -                     ha_alert("parsing [%s:%d]: out of memory.\n", file, 
> linenum);
> -                     err_code |= ERR_ALERT | ERR_ABORT;
> -                     free(ag);
> +     err_str = invalid_char(args[1]);
> +     if (err_str) {
> +             ha_alert("parsing [%s:%d]: character '%c' is not permitted in 
> '%s' name '%s'.\n",
> +                             file, linenum, *err_str, args[0], args[1]);
> +             err_code |= ERR_ALERT | ERR_FATAL;
> +             goto out;
> +     }
> +
> +     if (!userlist)
> +             goto out;
> +
> +     for (ag = userlist->groups; ag; ag = ag->next)
> +             if (strcmp(ag->name, args[1]) == 0) {
> +                     ha_warning("parsing [%s:%d]: ignoring duplicated group 
> '%s' in userlist '%s'.\n",
> +                                     file, linenum, args[1], userlist->name);
> +                     err_code |= ERR_ALERT;
>                       goto out;
>               }
>  
> -             cur_arg = 2;
> +     ag = calloc(1, sizeof(*ag));
> +     if (!ag) {
> +             ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
> +             err_code |= ERR_ALERT | ERR_ABORT;
> +             goto out;
> +     }
>  
> -             while (*args[cur_arg]) {
> -                     if (strcmp(args[cur_arg], "users") == 0) {
> -                             if (ag->groupusers) {
> -                                     ha_alert("parsing [%s:%d]: 'users' 
> option already defined in '%s' name '%s'.\n",
> -                                              file, linenum, args[0], 
> args[1]);
> -                                     err_code |= ERR_ALERT | ERR_FATAL;
> -                                     free(ag->groupusers);
> -                                     free(ag->name);
> -                                     free(ag);
> -                                     goto out;
> -                             }
> -                             ag->groupusers = strdup(args[cur_arg + 1]);
> -                             cur_arg += 2;
> -                             continue;
> -                     } else {
> -                             ha_alert("parsing [%s:%d]: '%s' only supports 
> 'users' option.\n",
> -                                      file, linenum, args[0]);
> +     ag->name = strdup(args[1]);
> +     if (!ag->name) {
> +             ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
> +             err_code |= ERR_ALERT | ERR_ABORT;
> +             free(ag);
> +             goto out;
> +     }
> +
> +     cur_arg = 2;
> +
> +     while (*args[cur_arg]) {
> +             if (strcmp(args[cur_arg], "users") == 0) {
> +                     if (ag->groupusers) {
> +                             ha_alert("parsing [%s:%d]: 'users' option 
> already defined in '%s' name '%s'.\n",
> +                                             file, linenum, args[0], 
> args[1]);
>                               err_code |= ERR_ALERT | ERR_FATAL;
>                               free(ag->groupusers);
>                               free(ag->name);
>                               free(ag);
>                               goto out;
>                       }
> +                     ag->groupusers = strdup(args[cur_arg + 1]);
> +                     cur_arg += 2;
> +                     continue;
> +             } else {
> +                     ha_alert("parsing [%s:%d]: '%s' only supports 'users' 
> option.\n",
> +                                     file, linenum, args[0]);
> +                     err_code |= ERR_ALERT | ERR_FATAL;
> +                     free(ag->groupusers);
> +                     free(ag->name);
> +                     free(ag);
> +                     goto out;
>               }
> +     }
>  
> -             ag->next = userlist->groups;
> -             userlist->groups = ag;
> +     ag->next = userlist->groups;
> +     userlist->groups = ag;
>  
> -     } else if (strcmp(args[0], "user") == 0) {              /* new user */
> -             struct auth_users *newuser;
> -             int cur_arg;
> +out:
> +     return err_code;
> +}
>  
> -             if (!*args[1]) {
> -                     ha_alert("parsing [%s:%d]: '%s' expects <name> as 
> arguments.\n",
> -                              file, linenum, args[0]);
> -                     err_code |= ERR_ALERT | ERR_FATAL;
> -                     goto out;
> -             }
> -             if (!userlist)
> -                     goto out;
> +int cfg_parse_users_user(char **args, int section_type, struct proxy 
> *curproxy, const struct proxy *defproxy, const char *file, int linenum, char 
> **err)
> +{
> +     struct auth_users *newuser;
> +     int cur_arg;
> +     int err_code = 0;
>  
> -             for (newuser = userlist->users; newuser; newuser = 
> newuser->next)
> -                     if (strcmp(newuser->user, args[1]) == 0) {
> -                             ha_warning("parsing [%s:%d]: ignoring 
> duplicated user '%s' in userlist '%s'.\n",
> -                                        file, linenum, args[1], 
> userlist->name);
> -                             err_code |= ERR_ALERT;
> -                             goto out;
> -                     }
> +     if (!*args[1]) {
> +             ha_alert("parsing [%s:%d]: '%s' expects <name> as arguments.\n",
> +                      file, linenum, args[0]);
> +             err_code |= ERR_ALERT | ERR_FATAL;
> +             goto out;
> +     }
> +     if (!userlist)
> +             goto out;
>  
> -             newuser = calloc(1, sizeof(*newuser));
> -             if (!newuser) {
> -                     ha_alert("parsing [%s:%d]: out of memory.\n", file, 
> linenum);
> -                     err_code |= ERR_ALERT | ERR_ABORT;
> +     for (newuser = userlist->users; newuser; newuser = newuser->next)
> +             if (strcmp(newuser->user, args[1]) == 0) {
> +                     ha_warning("parsing [%s:%d]: ignoring duplicated user 
> '%s' in userlist '%s'.\n",
> +                                file, linenum, args[1], userlist->name);
> +                     err_code |= ERR_ALERT;
>                       goto out;
>               }
>  
> -             newuser->user = strdup(args[1]);
> +     newuser = calloc(1, sizeof(*newuser));
> +     if (!newuser) {
> +             ha_alert("parsing [%s:%d]: out of memory.\n", file, linenum);
> +             err_code |= ERR_ALERT | ERR_ABORT;
> +             goto out;
> +     }
> +
> +     newuser->user = strdup(args[1]);
>  
> -             newuser->next = userlist->users;
> -             userlist->users = newuser;
> +     newuser->next = userlist->users;
> +     userlist->users = newuser;
>  
> -             cur_arg = 2;
> +     cur_arg = 2;
>  
> -             while (*args[cur_arg]) {
> -                     if (strcmp(args[cur_arg], "password") == 0) {
> +     while (*args[cur_arg]) {
> +             if (strcmp(args[cur_arg], "password") == 0) {
>  #ifdef USE_LIBCRYPT
> -                             if (!crypt("", args[cur_arg + 1])) {
> -                                     ha_alert("parsing [%s:%d]: the 
> encrypted password used for user '%s' is not supported by crypt(3).\n",
> -                                              file, linenum, newuser->user);
> -                                     err_code |= ERR_ALERT | ERR_FATAL;
> -                                     goto out;
> -                             }
> -#else
> -                             ha_warning("parsing [%s:%d]: no crypt(3) 
> support compiled, encrypted passwords will not work.\n",
> -                                        file, linenum);
> -                             err_code |= ERR_ALERT;
> -#endif
> -                             newuser->pass = strdup(args[cur_arg + 1]);
> -                             cur_arg += 2;
> -                             continue;
> -                     } else if (strcmp(args[cur_arg], "insecure-password") 
> == 0) {
> -                             newuser->pass = strdup(args[cur_arg + 1]);
> -                             newuser->flags |= AU_O_INSECURE;
> -                             cur_arg += 2;
> -                             continue;
> -                     } else if (strcmp(args[cur_arg], "groups") == 0) {
> -                             newuser->u.groups_names = strdup(args[cur_arg + 
> 1]);
> -                             cur_arg += 2;
> -                             continue;
> -                     } else {
> -                             ha_alert("parsing [%s:%d]: '%s' only supports 
> 'password', 'insecure-password' and 'groups' options.\n",
> -                                      file, linenum, args[0]);
> +                     if (!crypt("", args[cur_arg + 1])) {
> +                             ha_alert("parsing [%s:%d]: the encrypted 
> password used for user '%s' is not supported by crypt(3).\n",
> +                                      file, linenum, newuser->user);
>                               err_code |= ERR_ALERT | ERR_FATAL;
>                               goto out;
>                       }
> +#else
> +                     ha_warning("parsing [%s:%d]: no crypt(3) support 
> compiled, encrypted passwords will not work.\n",
> +                                file, linenum);
> +                     err_code |= ERR_ALERT;
> +#endif
> +                     newuser->pass = strdup(args[cur_arg + 1]);
> +                     cur_arg += 2;
> +                     continue;
> +             } else if (strcmp(args[cur_arg], "insecure-password") == 0) {
> +                     newuser->pass = strdup(args[cur_arg + 1]);
> +                     newuser->flags |= AU_O_INSECURE;
> +                     cur_arg += 2;
> +                     continue;
> +             } else if (strcmp(args[cur_arg], "groups") == 0) {
> +                     newuser->u.groups_names = strdup(args[cur_arg + 1]);
> +                     cur_arg += 2;
> +                     continue;
> +             } else {
> +                     ha_alert("parsing [%s:%d]: '%s' only supports 
> 'password', 'insecure-password' and 'groups' options.\n",
> +                              file, linenum, args[0]);
> +                     err_code |= ERR_ALERT | ERR_FATAL;
> +                     goto out;
>               }
> -     } else {
> -             ha_alert("parsing [%s:%d]: unknown keyword '%s' in '%s' 
> section\n", file, linenum, args[0], "users");
> -             err_code |= ERR_ALERT | ERR_FATAL;
>       }
>  
>  out:
> @@ -4921,6 +4952,8 @@ REGISTER_CONFIG_SECTION("traces",         
> cfg_parse_traces,    NULL);
>  
>  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 },
>       { /* END */ }
>  }};
>  
> -- 
> 2.48.1
> 
> 
> 

-- 
William Lallemand


Reply via email to