From: Willy Tarreau Date: Fri, 12 Mar 2021 09:14:07 +0000 (+0100) Subject: MINOR: cfgparse/bind: suggest correct spelling for unknown bind keywords X-Git-Tag: v2.4-dev12~37 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=433b05fa6448c1cda47dfadc5abcfa8d80ae1ff1;p=thirdparty%2Fhaproxy.git MINOR: cfgparse/bind: suggest correct spelling for unknown bind keywords Just like with the server keywords, now's the turn of "bind" keywords. The difference is that 100% of the bind keywords are registered, thus we do not need the list of extra keywords. There are multiple bind line parsers today, all were updated: - peers - log - dgram-bind - cli $ printf "listen f\nbind :8000 tcut\n" | ./haproxy -c -f /dev/stdin [NOTICE] 070/101358 (25146) : haproxy version is 2.4-dev11-7b8787-26 [NOTICE] 070/101358 (25146) : path to executable is ./haproxy [ALERT] 070/101358 (25146) : parsing [/dev/stdin:2] : 'bind :8000' unknown keyword 'tcut'; did you mean 'tcp-ut' maybe ? [ALERT] 070/101358 (25146) : Error(s) found in configuration file : /dev/stdin [ALERT] 070/101358 (25146) : Fatal errors found in configuration. --- diff --git a/include/haproxy/listener.h b/include/haproxy/listener.h index ee02b0cc2b..e4cea50fc6 100644 --- a/include/haproxy/listener.h +++ b/include/haproxy/listener.h @@ -167,6 +167,7 @@ struct bind_kw *bind_find_kw(const char *kw); /* Dumps all registered "bind" keywords to the string pointer. */ void bind_dump_kws(char **out); +const char *bind_find_best_kw(const char *word); void bind_recount_thread_bits(struct bind_conf *conf); unsigned int bind_map_thread_id(const struct bind_conf *conf, unsigned int r); diff --git a/src/cfgparse-listen.c b/src/cfgparse-listen.c index b60741aed0..ac5002536a 100644 --- a/src/cfgparse-listen.c +++ b/src/cfgparse-listen.c @@ -389,9 +389,8 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) cur_arg = 2; while (*(args[cur_arg])) { - static int bind_dumped; struct bind_kw *kw; - char *err; + const char *best; kw = bind_find_kw(args[cur_arg]); if (kw) { @@ -431,17 +430,13 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) continue; } - err = NULL; - if (!bind_dumped) { - bind_dump_kws(&err); - indent_msg(&err, 4); - bind_dumped = 1; - } - - ha_alert("parsing [%s:%d] : '%s %s' unknown keyword '%s'.%s%s\n", - file, linenum, args[0], args[1], args[cur_arg], - err ? " Registered keywords :" : "", err ? err : ""); - free(err); + best = bind_find_best_kw(args[cur_arg]); + if (best) + ha_alert("parsing [%s:%d] : '%s %s' unknown keyword '%s'; did you mean '%s' maybe ?\n", + file, linenum, args[0], args[1], args[cur_arg], best); + else + ha_alert("parsing [%s:%d] : '%s %s' unknown keyword '%s'.\n", + file, linenum, args[0], args[1], args[cur_arg]); err_code |= ERR_ALERT | ERR_FATAL; goto out; diff --git a/src/cfgparse.c b/src/cfgparse.c index 9c8efa2f6c..269b47d0b5 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -609,7 +609,6 @@ int cfg_parse_peers(const char *file, int linenum, char **args, int kwm) if (strcmp(args[0], "bind") == 0 || strcmp(args[0], "default-bind") == 0) { int cur_arg; - static int kws_dumped; struct bind_conf *bind_conf; struct bind_kw *kw; @@ -689,17 +688,13 @@ int cfg_parse_peers(const char *file, int linenum, char **args, int kwm) cur_arg += 1 + kw->skip; } if (*args[cur_arg] != 0) { - char *kws = NULL; - - if (!kws_dumped) { - kws_dumped = 1; - bind_dump_kws(&kws); - indent_msg(&kws, 4); - } - ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.%s%s\n", - file, linenum, args[cur_arg], cursection, - kws ? " Registered keywords :" : "", kws ? kws: ""); - free(kws); + const char *best = bind_find_best_kw(args[cur_arg]); + if (best) + ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n", + file, linenum, args[cur_arg], cursection, best); + else + ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.\n", + file, linenum, args[cur_arg], cursection); err_code |= ERR_ALERT | ERR_FATAL; goto out; } diff --git a/src/cli.c b/src/cli.c index 72919ce320..04c4e7a196 100644 --- a/src/cli.c +++ b/src/cli.c @@ -290,8 +290,8 @@ static int stats_parse_global(char **args, int section_type, struct proxy *curpx cur_arg = 3; while (*args[cur_arg]) { - static int bind_dumped; struct bind_kw *kw; + const char *best; kw = bind_find_kw(args[cur_arg]); if (kw) { @@ -314,15 +314,13 @@ static int stats_parse_global(char **args, int section_type, struct proxy *curpx continue; } - if (!bind_dumped) { - bind_dump_kws(err); - indent_msg(err, 4); - bind_dumped = 1; - } - - memprintf(err, "'%s %s' : unknown keyword '%s'.%s%s", - args[0], args[1], args[cur_arg], - err && *err ? " Registered keywords :" : "", err && *err ? *err : ""); + best = bind_find_best_kw(args[cur_arg]); + if (best) + memprintf(err, "'%s %s' : unknown keyword '%s'. Did you mean '%s' maybe ?", + args[0], args[1], args[cur_arg], best); + else + memprintf(err, "'%s %s' : unknown keyword '%s'.", + args[0], args[1], args[cur_arg]); return -1; } @@ -2656,8 +2654,8 @@ int mworker_cli_proxy_new_listener(char *line) cur_arg = 1; while (*args[cur_arg]) { - static int bind_dumped; struct bind_kw *kw; + const char *best; kw = bind_find_kw(args[cur_arg]); if (kw) { @@ -2680,15 +2678,13 @@ int mworker_cli_proxy_new_listener(char *line) continue; } - if (!bind_dumped) { - bind_dump_kws(&err); - indent_msg(&err, 4); - bind_dumped = 1; - } - - memprintf(&err, "'%s %s' : unknown keyword '%s'.%s%s", - args[0], args[1], args[cur_arg], - err ? " Registered keywords :" : "", err ? err : ""); + best = bind_find_best_kw(args[cur_arg]); + if (best) + memprintf(&err, "'%s %s' : unknown keyword '%s'. Did you mean '%s' maybe ?", + args[0], args[1], args[cur_arg], best); + else + memprintf(&err, "'%s %s' : unknown keyword '%s'.", + args[0], args[1], args[cur_arg]); goto err; } diff --git a/src/listener.c b/src/listener.c index 4dfaa7f23e..882d17fbe3 100644 --- a/src/listener.c +++ b/src/listener.c @@ -1261,6 +1261,37 @@ void bind_dump_kws(char **out) } } +/* Try to find in srv_keyword the word that looks closest to by counting + * transitions between letters, digits and other characters. Will return the + * best matching word if found, otherwise NULL. + */ +const char *bind_find_best_kw(const char *word) +{ + uint8_t word_sig[1024]; + uint8_t list_sig[1024]; + const struct bind_kw_list *kwl; + const char *best_ptr = NULL; + int dist, best_dist = INT_MAX; + int index; + + make_word_fingerprint(word_sig, word); + list_for_each_entry(kwl, &bind_keywords.list, list) { + for (index = 0; kwl->kw[index].kw != NULL; index++) { + make_word_fingerprint(list_sig, kwl->kw[index].kw); + dist = word_fingerprint_distance(word_sig, list_sig); + if (dist < best_dist) { + best_dist = dist; + best_ptr = kwl->kw[index].kw; + } + } + } + + if (best_dist > 2 * strlen(word) || (best_ptr && best_dist > 2 * strlen(best_ptr))) + best_ptr = NULL; + + return best_ptr; +} + /************************************************************************/ /* All supported sample and ACL keywords must be declared here. */ /************************************************************************/ diff --git a/src/log.c b/src/log.c index 2bf7f15805..c771218237 100644 --- a/src/log.c +++ b/src/log.c @@ -3894,7 +3894,6 @@ int cfg_parse_log_forward(const char *file, int linenum, char **args, int kwm) } else if (strcmp(args[0], "bind") == 0) { int cur_arg; - static int kws_dumped; struct bind_conf *bind_conf; struct bind_kw *kw; struct listener *l; @@ -3949,24 +3948,19 @@ int cfg_parse_log_forward(const char *file, int linenum, char **args, int kwm) cur_arg += 1 + kw->skip; } if (*args[cur_arg] != 0) { - char *kws = NULL; - - if (!kws_dumped) { - kws_dumped = 1; - bind_dump_kws(&kws); - indent_msg(&kws, 4); - } - ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.%s%s\n", - file, linenum, args[cur_arg], cursection, - kws ? " Registered keywords :" : "", kws ? kws: ""); - free(kws); + const char *best = bind_find_best_kw(args[cur_arg]); + if (best) + ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n", + file, linenum, args[cur_arg], cursection, best); + else + ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.\n", + file, linenum, args[cur_arg], cursection); err_code |= ERR_ALERT | ERR_FATAL; goto out; } } else if (strcmp(args[0], "dgram-bind") == 0) { int cur_arg; - static int kws_dumped; struct bind_conf *bind_conf; struct bind_kw *kw; struct listener *l; @@ -4015,17 +4009,13 @@ int cfg_parse_log_forward(const char *file, int linenum, char **args, int kwm) cur_arg += 1 + kw->skip; } if (*args[cur_arg] != 0) { - char *kws = NULL; - - if (!kws_dumped) { - kws_dumped = 1; - bind_dump_kws(&kws); - indent_msg(&kws, 4); - } - ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.%s%s\n", - file, linenum, args[cur_arg], cursection, - kws ? " Registered keywords :" : "", kws ? kws: ""); - free(kws); + const char *best = bind_find_best_kw(args[cur_arg]); + if (best) + ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section; did you mean '%s' maybe ?\n", + file, linenum, args[cur_arg], cursection, best); + else + ha_alert("parsing [%s:%d] : unknown keyword '%s' in '%s' section.\n", + file, linenum, args[cur_arg], cursection); err_code |= ERR_ALERT | ERR_FATAL; goto out; }