]> git.kaiwu.me - haproxy.git/commitdiff
MINOR: cfgparse: always suggest the section names on an unknown keyword
authorWilly Tarreau <w@1wt.eu>
Thu, 8 Oct 2026 17:31:42 +0000 (19:31 +0200)
committerWilly Tarreau <w@1wt.eu>
Fri, 9 Oct 2026 08:30:59 +0000 (10:30 +0200)
When an unknown keyword is met in a section, cfg_find_best_match() looks
for the closest match among the keywords registered for that section,
plus an optional list of extra words passed by the caller, which
cfg_parse_global() and cfg_parse_listen() were using to pass a list of
a few known hard-coded section names. But this resulted several times
in some names not being matched (e.g. "resolvers" which was later added
but only for "global"), or others still not being known, e.g.

  $ printf 'global\n\tcrt-stor\n' | ./haproxy -c -f /dev/stdin
  [ALERT] : parsing [/dev/stdin:2] : unknown keyword 'crt-stor' in
    'global' section; did you mean 'crt-store' maybe ?

Let's have the function always consider all known section names since
they are always permitted, that is much more effective and will no
longer require to hard-code certain combinations. This completely drops
the extra argument that's no longer used.

include/haproxy/cfgparse.h
src/acme.c
src/cfgparse-global.c
src/cfgparse-listen.c
src/cfgparse.c
src/ssl_ckch.c
src/trace.c

index 926b583be697ec6606a59ca4239f1f5c98e6f916..3a1640b11f63cd59207ac0c0d8644bbe4abb3111 100644 (file)
@@ -149,7 +149,7 @@ int too_many_args(int maxarg, char **args, char **msg, int *err_code);
 int alertif_too_many_args_idx(int maxarg, int index, const char *file, int linenum, char **args, int *err_code);
 int alertif_too_many_args(int maxarg, const char *file, int linenum, char **args, int *err_code);
 int parse_process_number(const char *arg, unsigned long *proc, int max, int *autoinc, char **err);
-const char *cfg_find_best_match(const char *word, const struct list *list, int section, const char **extra);
+const char *cfg_find_best_match(const char *word, const struct list *list, int section);
 int warnifnotcap(struct proxy *proxy, int cap, const char *file, int line, const char *arg, const char *hint);
 int failifnotcap(struct proxy *proxy, int cap, const char *file, int line, const char *arg, const char *hint);
 void cfg_dump_registered_keywords();
index 63f6959aec912b2dc39cc27feac86400bb6fc1ab..ecf5367447209ad2e97f21a1d3568e0e6079ab50 100644 (file)
@@ -358,7 +358,7 @@ static int cfg_parse_acme(const char *file, int linenum, char **args, int kwm)
                }
        }
 
-       best = cfg_find_best_match(args[0], &cfg_keywords.list, CFG_ACME, NULL);
+       best = cfg_find_best_match(args[0], &cfg_keywords.list, CFG_ACME);
        if (best)
                ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n", file, linenum, args[0], cursection, best);
        else
index 90b0c037d0581feada4522a4243a62ebe508df16..09167daa08e043e893979c8378d79c40fc96c889 100644 (file)
@@ -102,7 +102,7 @@ int cfg_parse_global(const char *file, int linenum, char **args, int kwm)
        if (global.mode & MODE_DISCOVERY)
                goto out;
 
-       best = cfg_find_best_match(args[0], &cfg_keywords.list, CFG_GLOBAL, common_kw_list);
+       best = cfg_find_best_match(args[0], &cfg_keywords.list, CFG_GLOBAL);
        if (best)
                ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n", file, linenum, args[0], cursection, best);
        else
index c6bc23755eae5d5da46f7821ac4c4df9c9ad0895..0b116175d393aeca2132cce09648849bf50ffc8c 100644 (file)
@@ -568,7 +568,7 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm)
                }
        }
 
-       best = cfg_find_best_match(args[0], &cfg_keywords.list, CFG_LISTEN, common_kw_list);
+       best = cfg_find_best_match(args[0], &cfg_keywords.list, CFG_LISTEN);
        if (best)
                ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n", file, linenum, args[0], cursection, best);
        else
index ec181cd56e5613ac2847d039a3b3cb20e4466647..81e52d87150436f3716d48d0ec3543226b70ce3d 100644 (file)
@@ -480,15 +480,15 @@ int warnif_tcp_http_cond(const struct proxy *px, const struct acl_cond *cond)
 
 /* try to find in <list> the word that looks closest to <word> by counting
  * transitions between letters, digits and other characters. Will return the
- * best matching word if found, otherwise NULL. An optional array of extra
- * words to compare may be passed in <extra>, but it must then be terminated
- * by a NULL entry. If unused it may be NULL.
+ * best matching word if found, otherwise NULL. If no match is found, then the
+ * registered section names are checked and a matching one is proposed.
  */
-const char *cfg_find_best_match(const char *word, const struct list *list, int section, const char **extra)
+const char *cfg_find_best_match(const char *word, const struct list *list, int section)
 {
        uint8_t word_sig[1024]; // 0..25=letter, 26=digit, 27=other, 28=begin, 29=end
        uint8_t list_sig[1024];
        const struct cfg_kw_list *kwl;
+       const struct cfg_section *cs;
        int index;
        const char *best_ptr = NULL;
        int dist, best_dist = INT_MAX;
@@ -508,14 +508,16 @@ const char *cfg_find_best_match(const char *word, const struct list *list, int s
                }
        }
 
-       while (extra && *extra) {
-               make_word_fingerprint(list_sig, *extra);
+       /* the name of any section is a valid word at this place, and none of
+        * them is registered as a keyword.
+        */
+       list_for_each_entry(cs, &sections, list) {
+               make_word_fingerprint(list_sig, cs->section_name);
                dist = word_fingerprint_distance(word_sig, list_sig);
                if (dist < best_dist) {
                        best_dist = dist;
-                       best_ptr = *extra;
+                       best_ptr = cs->section_name;
                }
-               extra++;
        }
 
        if (best_dist > 2 * strlen(word) || (best_ptr && best_dist > 2 * strlen(best_ptr)))
index 3f808ee63f3f76ea79c96dd7a43c3b0ba8ee4c41..9353e772ab1e8a9596de392ea6e044421446deee 100644 (file)
@@ -5648,7 +5648,7 @@ static int cfg_parse_crtstore(const char *file, int linenum, char **args, int kw
                }
        }
 
-       best = cfg_find_best_match(args[0], &cfg_keywords.list, CFG_CRTSTORE, NULL);
+       best = cfg_find_best_match(args[0], &cfg_keywords.list, CFG_CRTSTORE);
        if (best)
                ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n", file, linenum, args[0], cursection, best);
        else
index c7570f726804865d0a6e04adb25ffcbebeea60f4..f97ed214b0570125f020b34931aa5c4ebee2ae84 100644 (file)
@@ -1264,7 +1264,7 @@ int cfg_parse_traces(const char *file, int linenum, char **args, int inv)
                        }
                }
 
-               best = cfg_find_best_match(args[0], &cfg_keywords.list, CFG_TRACES, NULL);
+               best = cfg_find_best_match(args[0], &cfg_keywords.list, CFG_TRACES);
                if (best)
                        ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n", file, linenum, args[0], cursection, best);
                else