From 49c2b45c1d141ac63c4efae1541b5c8e2f61e520 Mon Sep 17 00:00:00 2001 From: Willy Tarreau Date: Fri, 12 Mar 2021 09:58:04 +0100 Subject: [PATCH] MINOR: cfgparse/server: try to fix spelling mistakes on server lines Let's apply the fuzzy match to server keywords so that we can avoid dumping the huge list of supported keywords each time there is a spelling mistake, and suggest proper spelling instead: $ printf "listen f\nserver s 0 sendpx-v2\n" | ./haproxy -c -f /dev/stdin [NOTICE] 070/095718 (24152) : haproxy version is 2.4-dev11-caa6e3-25 [NOTICE] 070/095718 (24152) : path to executable is ./haproxy [ALERT] 070/095718 (24152) : parsing [/dev/stdin:2] : 'server s' unknown keyword 'sendpx-v2'; did you mean 'send-proxy-v2' maybe ? [ALERT] 070/095718 (24152) : Error(s) found in configuration file : /dev/stdin [ALERT] 070/095718 (24152) : Fatal errors found in configuration. --- src/server.c | 78 +++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 65 insertions(+), 13 deletions(-) diff --git a/src/server.c b/src/server.c index 8eb659d335..8b2dd4f257 100644 --- a/src/server.c +++ b/src/server.c @@ -50,6 +50,19 @@ static void srv_update_status(struct server *s); static int srv_apply_lastaddr(struct server *srv, int *err_code); static void srv_cleanup_connections(struct server *srv); +/* some keywords that are still being parsed using strcmp() and are not + * registered anywhere. They are used as suggestions for mistyped words. + */ +static const char *common_kw_list[] = { + "init-addr", "resolvers", "resolve-opts", "resolve-prefer", "ipv4", + "ipv6", "resolve-net", "weight", "log-proto", "legacy", "octet-count", + "minconn", "maxconn", "maxqueue", "slowstart", "on-error", "fastinter", + "fail-check", "sudden-death", "mark-down", "on-marked-down", + "shutdown-sessions", "on-marked-up", "shutdown-backup-sessions", + "error-limit", "usesrc", + NULL /* must be last */ +}; + /* List head of all known server keywords */ static struct srv_kw_list srv_keywords = { .list = LIST_HEAD_INIT(srv_keywords.list) @@ -287,6 +300,50 @@ void srv_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. An optional array of extra + * words to compare may be passed in , but it must then be terminated + * by a NULL entry. If unused it may be NULL. + */ +static const char *srv_find_best_kw(const char *word) +{ + uint8_t word_sig[1024]; + uint8_t list_sig[1024]; + const struct srv_kw_list *kwl; + const char *best_ptr = NULL; + int dist, best_dist = INT_MAX; + const char **extra; + int index; + + make_word_fingerprint(word_sig, word); + list_for_each_entry(kwl, &srv_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; + } + } + } + + for (extra = common_kw_list; *extra; extra++) { + make_word_fingerprint(list_sig, *extra); + dist = word_fingerprint_distance(word_sig, list_sig); + if (dist < best_dist) { + best_dist = dist; + best_ptr = *extra; + } + extra++; + } + + if (best_dist > 2 * strlen(word) || (best_ptr && best_dist > 2 * strlen(best_ptr))) + best_ptr = NULL; + + return best_ptr; +} + /* Parse the "backup" server keyword */ static int srv_parse_backup(char **args, int *cur_arg, struct proxy *curproxy, struct server *newsrv, char **err) @@ -2394,9 +2451,8 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr goto out; } else { - static int srv_dumped; struct srv_kw *kw; - char *err; + const char *best; kw = srv_find_kw(args[cur_arg]); if (kw) { @@ -2439,17 +2495,13 @@ int parse_server(const char *file, int linenum, char **args, struct proxy *curpr continue; } - err = NULL; - if (!srv_dumped) { - srv_dump_kws(&err); - indent_msg(&err, 4); - srv_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 = srv_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; -- 2.39.5