MINOR: cli: implement experimental-mode

Experimental mode is similar to expert-mode. It can be used to access to
features still in development.
diff --git a/doc/management.txt b/doc/management.txt
index ca5d858..5f88a5d 100644
--- a/doc/management.txt
+++ b/doc/management.txt
@@ -1677,11 +1677,21 @@
   This command is restricted and can only be issued on sockets configured for
   level "admin".
 
+experimental-mode [on|off]
+  Without options, this indicates whether the experimental mode is enabled or
+  disabled on the current connection. When passed "on", it turns the
+  experimental mode on for the current CLI connection only. With "off" it turns
+  it off.
+
+  The experimental mode is used to access to extra features still in
+  development. These features are currently not stable and should be used with
+  care. They may be subject to breaking changes accross versions.
+
 expert-mode [on|off]
-  Without options, this indicates whether the expert mode is enabled or
-  disabled on the current connection. When passed "on", it turns the expert
-  mode on for the current CLI connection only. With "off" it turns it off. The
-  expert mode enables displaying of expert commands that can be extremely
+  This command is similar to experimental-mode but is used to toggle the
+  expert mode.
+
+  The expert mode enables displaying of expert commands that can be extremely
   dangerous for the process and which may occasionally help developers collect
   important information about complex bugs. Any misuse of these features will
   likely lead to a process crash. Do not use this option without being invited
@@ -1984,11 +1994,13 @@
 
 operator
   Decrease the CLI level of the current CLI session to operator. It can't be
-  increased. It also drops expert mode. See also "show cli level".
+  increased. It also drops expert and experimental mode. See also "show cli
+  level".
 
 user
   Decrease the CLI level of the current CLI session to user. It can't be
-  increased. It also drops expert mode. See also "show cli level".
+  increased. It also drops expert and experimental mode. See also "show cli
+  level".
 
 show activity
   Reports some counters about internal events that will help developers and
diff --git a/include/haproxy/cli-t.h b/include/haproxy/cli-t.h
index 450781a..3d52f27 100644
--- a/include/haproxy/cli-t.h
+++ b/include/haproxy/cli-t.h
@@ -35,6 +35,7 @@
 #define ACCESS_MASTER       0x0008  /* works with the master (and every other processes) */
 #define ACCESS_MASTER_ONLY  0x0010  /* only works with the master */
 #define ACCESS_EXPERT       0x0020  /* access to dangerous commands reserved to experts */
+#define ACCESS_EXPERIMENTAL 0x0040
 
 /* flags for appctx->st1 */
 #define APPCTX_CLI_ST1_PROMPT  (1 << 0)
diff --git a/src/cli.c b/src/cli.c
index 9aede13..2e4da52 100644
--- a/src/cli.c
+++ b/src/cli.c
@@ -103,7 +103,7 @@
 	/* first, let's measure the longest match */
 	list_for_each_entry(kw_list, &cli_keywords.list, list) {
 		for (kw = &kw_list->kw[0]; kw->str_kw[0]; kw++) {
-			if (kw->level & ~appctx->cli_level & (ACCESS_MASTER_ONLY|ACCESS_EXPERT))
+			if (kw->level & ~appctx->cli_level & (ACCESS_MASTER_ONLY|ACCESS_EXPERT|ACCESS_EXPERIMENTAL))
 				continue;
 			if ((appctx->cli_level & ~kw->level & (ACCESS_MASTER_ONLY|ACCESS_MASTER)) ==
 			    (ACCESS_MASTER_ONLY|ACCESS_MASTER))
@@ -143,7 +143,7 @@
 	if (args && args[length] && *args[length]) {
 		list_for_each_entry(kw_list, &cli_keywords.list, list) {
 			for (kw = &kw_list->kw[0]; kw->str_kw[0]; kw++) {
-				if (kw->level & ~appctx->cli_level & (ACCESS_MASTER_ONLY|ACCESS_EXPERT))
+				if (kw->level & ~appctx->cli_level & (ACCESS_MASTER_ONLY|ACCESS_EXPERT|ACCESS_EXPERIMENTAL))
 					continue;
 				if ((appctx->cli_level & ~kw->level & (ACCESS_MASTER_ONLY|ACCESS_MASTER)) ==
 				    (ACCESS_MASTER_ONLY|ACCESS_MASTER))
@@ -226,9 +226,9 @@
 		for (kw = &kw_list->kw[0]; kw->str_kw[0]; kw++) {
 
 			/* in a worker or normal process, don't display master-only commands
-			 * nor expert mode commands if not in this mode.
+			 * nor expert/experimental mode commands if not in this mode.
 			 */
-			if (kw->level & ~appctx->cli_level & (ACCESS_MASTER_ONLY|ACCESS_EXPERT))
+			if (kw->level & ~appctx->cli_level & (ACCESS_MASTER_ONLY|ACCESS_EXPERT|ACCESS_EXPERIMENTAL))
 				continue;
 
 			/* in master don't display commands that have neither the master bit
@@ -730,6 +730,11 @@
 		return 0;
 	}
 
+	if (kw->level & ~appctx->cli_level & ACCESS_EXPERIMENTAL) {
+		cli_err(appctx, "This command is restricted to experimental mode only.\n");
+		return 0;
+	}
+
 	appctx->io_handler = kw->io_handler;
 	appctx->io_release = kw->io_release;
 
@@ -1677,29 +1682,45 @@
 		appctx->cli_level &= ~ACCESS_LVL_MASK;
 		appctx->cli_level |= ACCESS_LVL_USER;
 	}
-	appctx->cli_level &= ~ACCESS_EXPERT;
+	appctx->cli_level &= ~(ACCESS_EXPERT|ACCESS_EXPERIMENTAL);
 	return 1;
 }
 
 
-/* parse and set the CLI expert-mode dynamically */
-static int cli_parse_expert_mode(char **args, char *payload, struct appctx *appctx, void *private)
+/* parse and set the CLI expert/experimental-mode dynamically */
+static int cli_parse_expert_experimental_mode(char **args, char *payload, struct appctx *appctx, void *private)
 {
+	int level;
+	char *level_str;
+	char *output = NULL;
+
 	if (!cli_has_level(appctx, ACCESS_LVL_ADMIN))
 		return 1;
+
+	if (!strcmp(args[0], "expert-mode")) {
+		level = ACCESS_EXPERT;
+		level_str = "expert-mode";
+	}
+	else if (!strcmp(args[0], "experimental-mode")) {
+		level = ACCESS_EXPERIMENTAL;
+		level_str = "experimental-mode";
+	}
+	else {
+		return 1;
+	}
 
-	if (!*args[1])
-		return (appctx->cli_level & ACCESS_EXPERT)
-			? cli_msg(appctx, LOG_INFO, "expert-mode is ON\n")
-			: cli_msg(appctx, LOG_INFO, "expert-mode is OFF\n");
+	if (!*args[1]) {
+		memprintf(&output, "%s is %s\n", level_str,
+		          (appctx->cli_level & level) ? "ON" : "OFF");
+		return cli_dynmsg(appctx, LOG_INFO, output);
+	}
 
-	appctx->cli_level &= ~ACCESS_EXPERT;
+	appctx->cli_level &= ~level;
 	if (strcmp(args[1], "on") == 0)
-		appctx->cli_level |= ACCESS_EXPERT;
+		appctx->cli_level |= level;
 	return 1;
 }
 
-
 int cli_parse_default(char **args, char *payload, struct appctx *appctx, void *private)
 {
 	return 0;
@@ -2928,7 +2949,8 @@
 	{ { "operator", NULL },  "operator       : lower the level of the current CLI session to operator", cli_parse_set_lvl, NULL, NULL, NULL, ACCESS_MASTER},
 	{ { "user", NULL },      "user           : lower the level of the current CLI session to user", cli_parse_set_lvl, NULL, NULL, NULL, ACCESS_MASTER},
 	{ { "_getsocks", NULL }, NULL,  _getsocks, NULL },
-	{ { "expert-mode", NULL },  NULL,  cli_parse_expert_mode, NULL }, // not listed
+	{ { "expert-mode", NULL },  NULL,  cli_parse_expert_experimental_mode, NULL }, // not listed
+	{ { "experimental-mode", NULL },  NULL,  cli_parse_expert_experimental_mode, NULL }, // not listed
 	{{},}
 }};