BUG/MINOR: lua: warn when registering action, conv, sf, cli or applet multiple times

Lua allows registering multiple sample-fetches, converters, action, cli,
applet/services with the same name. This is absolutely useless since only
the first registration will be used. This patch sends a warning if the case
is encountered.

This pach could be backported until 1.8, with the 3 associated patches:
 - MINOR: actions: Export actions lookup functions
 - MINOR: actions: add a function returning a service pointer from its name
 - MINOR: cli: add a function to look up a CLI service description

(cherry picked from commit f67442efdb509d5d15f530a536b13f29fa7f48b7)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 04cbfd7cc0138575caf53e9710a49f1ed0a2ebbd)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit a9bca65df7674d2091ad753bad4e689d76a4a9db)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
(cherry picked from commit 59e8fadb39ad9832b5e7b7db035ca67c2b2b0ea6)
Signed-off-by: Christopher Faulet <cfaulet@haproxy.com>
diff --git a/src/hlua.c b/src/hlua.c
index a72e6d0..693ea31 100644
--- a/src/hlua.c
+++ b/src/hlua.c
@@ -6688,6 +6688,8 @@
 	int ref;
 	int len;
 	struct hlua_function *fcn;
+	struct sample_conv *sc;
+	struct buffer *trash;
 
 	MAY_LJMP(check_args(L, 2, "register_converters"));
 
@@ -6697,6 +6699,15 @@
 	/* Second argument : lua function. */
 	ref = MAY_LJMP(hlua_checkfunction(L, 2));
 
+	/* Check if the converter is already registered */
+	trash = get_trash_chunk();
+	chunk_printf(trash, "lua.%s", name);
+	sc = find_sample_conv(trash->area, trash->data);
+	if (sc != NULL) {
+		ha_warning("Trying to register converter 'lua.%s' more than once. "
+		           "This will become a hard error in version 2.5.\n", name);
+	}
+
 	/* Allocate and fill the sample fetch keyword struct. */
 	sck = calloc(1, sizeof(*sck) + sizeof(struct sample_conv) * 2);
 	if (!sck)
@@ -6745,6 +6756,8 @@
 	int len;
 	struct sample_fetch_kw_list *sfk;
 	struct hlua_function *fcn;
+	struct sample_fetch *sf;
+	struct buffer *trash;
 
 	MAY_LJMP(check_args(L, 2, "register_fetches"));
 
@@ -6754,6 +6767,15 @@
 	/* Second argument : lua function. */
 	ref = MAY_LJMP(hlua_checkfunction(L, 2));
 
+	/* Check if the sample-fetch is already registered */
+	trash = get_trash_chunk();
+	chunk_printf(trash, "lua.%s", name);
+	sf = find_sample_fetch(trash->area, trash->data);
+	if (sf != NULL) {
+		ha_warning("Trying to register sample-fetch 'lua.%s' more than once. "
+		           "This will become a hard error in version 2.5.\n", name);
+	}
+
 	/* Allocate and fill the sample fetch keyword struct. */
 	sfk = calloc(1, sizeof(*sfk) + sizeof(struct sample_fetch) * 2);
 	if (!sfk)
@@ -7746,6 +7768,8 @@
 	int len;
 	struct hlua_function *fcn;
 	int nargs;
+	struct buffer *trash;
+	struct action_kw *akw;
 
 	/* Initialise the number of expected arguments at 0. */
 	nargs = 0;
@@ -7773,6 +7797,25 @@
 		if (lua_type(L, -1) != LUA_TSTRING)
 			WILL_LJMP(luaL_error(L, "register_action: second argument must be a table of strings"));
 
+		/* Check if action exists */
+		trash = get_trash_chunk();
+		chunk_printf(trash, "lua.%s", name);
+		if (strcmp(lua_tostring(L, -1), "tcp-req") == 0) {
+			akw = tcp_req_cont_action(trash->area);
+		} else if (strcmp(lua_tostring(L, -1), "tcp-res") == 0) {
+			akw = tcp_res_cont_action(trash->area);
+		} else if (strcmp(lua_tostring(L, -1), "http-req") == 0) {
+			akw = action_http_req_custom(trash->area);
+		} else if (strcmp(lua_tostring(L, -1), "http-res") == 0) {
+			akw = action_http_res_custom(trash->area);
+		} else {
+			akw = NULL;
+		}
+		if (akw != NULL) {
+			ha_warning("Trying to register action 'lua.%s' more than once. "
+			           "This will become a hard error in version 2.5.\n", name);
+		}
+
 		/* Check required environment. Only accepted "http" or "tcp". */
 		/* Allocate and fill the sample fetch keyword struct. */
 		akl = calloc(1, sizeof(*akl) + sizeof(struct action_kw) * 2);
@@ -7872,6 +7915,8 @@
 	int ref;
 	int len;
 	struct hlua_function *fcn;
+	struct buffer *trash;
+	struct action_kw *akw;
 
 	MAY_LJMP(check_args(L, 3, "register_service"));
 
@@ -7884,6 +7929,15 @@
 	/* Third argument : lua function. */
 	ref = MAY_LJMP(hlua_checkfunction(L, 3));
 
+	/* Check for service already registered */
+	trash = get_trash_chunk();
+	chunk_printf(trash, "lua.%s", name);
+	akw = service_find(trash->area);
+	if (akw != NULL) {
+		ha_warning("Trying to register service 'lua.%s' more than once. "
+		           "This will become a hard error in version 2.5.\n", name);
+	}
+
 	/* Allocate and fill the sample fetch keyword struct. */
 	akl = calloc(1, sizeof(*akl) + sizeof(struct action_kw) * 2);
 	if (!akl)
@@ -8116,6 +8170,9 @@
 	struct hlua_function *fcn;
 	int index;
 	int i;
+	struct buffer *trash;
+	const char *kw[5];
+	struct cli_kw *cli_kw;
 
 	MAY_LJMP(check_args(L, 3, "register_cli"));
 
@@ -8129,6 +8186,30 @@
 	/* Third and fourth argument : lua function. */
 	ref_io = MAY_LJMP(hlua_checkfunction(L, 3));
 
+	/* Check for CLI service already registered */
+	trash = get_trash_chunk();
+	index = 0;
+	lua_pushnil(L);
+	memset(kw, 0, sizeof(kw));
+	while (lua_next(L, 1) != 0) {
+		if (index >= CLI_PREFIX_KW_NB)
+			WILL_LJMP(luaL_argerror(L, 1, "1st argument must be a table with a maximum of 5 entries"));
+		if (lua_type(L, -1) != LUA_TSTRING)
+			WILL_LJMP(luaL_argerror(L, 1, "1st argument must be a table filled with strings"));
+		kw[index] = lua_tostring(L, -1);
+		if (index == 0)
+			chunk_printf(trash, "%s", kw[index]);
+		else
+			chunk_appendf(trash, " %s", kw[index]);
+		index++;
+		lua_pop(L, 1);
+	}
+	cli_kw = cli_find_kw_exact((char **)kw);
+	if (cli_kw != NULL) {
+		ha_warning("Trying to register CLI keyword 'lua.%s' more than once. "
+		           "This will become a hard error in version 2.5.\n", trash->area);
+	}
+
 	/* Allocate and fill the sample fetch keyword struct. */
 	cli_kws = calloc(1, sizeof(*cli_kws) + sizeof(struct cli_kw) * 2);
 	if (!cli_kws)