MEDIUM: sink: add global statement to create a new ring (sink buffer)

This patch adds the new global statement:
ring <name> [desc <desc>] [format <format>] [size <size>] [maxlen <length>]
  Creates a named ring buffer which could be used on log line for instance.

  <desc> is an optionnal description string of the ring. It will appear on
         CLI. By default, <name> is reused to fill this field.

  <format> is the log format used when generating syslog messages. It may be
           one of the following :

    iso       A message containing only the ISO date, followed by the text.
              The PID, process name and system name are omitted. This is
              designed to be used with a local log server.

    raw       A message containing only the text. The level, PID, date, time,
              process name and system name are omitted. This is designed to be
              used in containers or during development, where the severity only
              depends on the file descriptor used (stdout/stderr). This is
              the default.

    rfc3164   The RFC3164 syslog message format. This is the default.
              (https://tools.ietf.org/html/rfc3164)

    rfc5424   The RFC5424 syslog message format.
              (https://tools.ietf.org/html/rfc5424)

    short     A message containing only a level between angle brackets such as
              '<3>', followed by the text. The PID, date, time, process name
              and system name are omitted. This is designed to be used with a
              local log server. This format is compatible with what the systemd
              logger consumes.

    timed     A message containing only a level between angle brackets such as
              '<3>', followed by ISO date and by the text. The PID, process
              name and system name are omitted. This is designed to be
              used with a local log server.

  <length> is the maximum length of event message stored into the ring,
           including formatted header. If the event message is longer
           than <length>, it would be truncated to this length.

  <name> is the ring identifier, which follows the same naming convention as
         proxies and servers.

  <size> is the optionnal size in bytes. Default value is set to BUFSIZE.

Note: Historically sink's name and desc were refs on const strings. But with new
configurable rings a dynamic allocation is needed.
diff --git a/doc/configuration.txt b/doc/configuration.txt
index e434a47..6e86a0a 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -614,6 +614,7 @@
    - pidfile
    - presetenv
    - resetenv
+   - ring
    - uid
    - ulimit-n
    - user
@@ -1193,6 +1194,51 @@
   next line in the configuration file sees the new environment. See also
   "setenv", "presetenv", and "unsetenv".
 
+ring <name> [desc <desc>] [format <format>] [size <size>] [maxlen <length>]
+  Creates a named ring buffer which could be used on log line for instance.
+
+  <desc> is an optionnal description string of the ring. It will appear on
+         CLI. By default, <name> is reused to fill this field.
+
+  <format> is the log format used when generating syslog messages. It may be
+           one of the following :
+
+    iso       A message containing only the ISO date, followed by the text.
+              The PID, process name and system name are omitted. This is
+              designed to be used with a local log server.
+
+    raw       A message containing only the text. The level, PID, date, time,
+              process name and system name are omitted. This is designed to be
+              used in containers or during development, where the severity only
+              depends on the file descriptor used (stdout/stderr). This is
+              the default.
+
+    rfc3164   The RFC3164 syslog message format. This is the default.
+              (https://tools.ietf.org/html/rfc3164)
+
+    rfc5424   The RFC5424 syslog message format.
+              (https://tools.ietf.org/html/rfc5424)
+
+    short     A message containing only a level between angle brackets such as
+              '<3>', followed by the text. The PID, date, time, process name
+              and system name are omitted. This is designed to be used with a
+              local log server. This format is compatible with what the systemd
+              logger consumes.
+
+    timed     A message containing only a level between angle brackets such as
+              '<3>', followed by ISO date and by the text. The PID, process
+              name and system name are omitted. This is designed to be
+              used with a local log server.
+
+  <length> is the maximum length of an event message stored into the ring,
+           including formatted header. If an event message is longer than
+           <length>, it will be truncated to this length.
+
+  <name> is the ring identifier, which follows the same naming convention as
+         proxies and servers.
+
+  <size> is the optionnal size in bytes. Default value is set to BUFSIZE.
+
 stats bind-process [ all | odd | even | <process_num>[-[process_num>]] ] ...
   Limits the stats socket to a certain set of processes numbers. By default the
   stats socket is bound to all processes, causing a warning to be emitted when
diff --git a/include/common/cfgparse.h b/include/common/cfgparse.h
index 1a28816..286099b 100644
--- a/include/common/cfgparse.h
+++ b/include/common/cfgparse.h
@@ -28,6 +28,7 @@
 #include <common/mini-clist.h>
 
 #include <proto/log.h>
+#include <proto/sink.h>
 #include <proto/proxy.h>
 
 /* configuration sections */
diff --git a/include/proto/sink.h b/include/proto/sink.h
index d30f30a..c312185 100644
--- a/include/proto/sink.h
+++ b/include/proto/sink.h
@@ -78,6 +78,9 @@
 	return sent;
 }
 
+
+int parse_sinkbuf(char **args, char **err);
+
 #endif /* _PROTO_SINK_H */
 
 /*
diff --git a/include/types/sink.h b/include/types/sink.h
index 28b0379..ef11096 100644
--- a/include/types/sink.h
+++ b/include/types/sink.h
@@ -52,8 +52,8 @@
 /* describes the configuration and current state of an event sink */
 struct sink {
 	struct list sink_list;     // position in the sink list
-	const char *name;          // sink name
-	const char *desc;          // sink description
+	char *name;                // sink name
+	char *desc;                // sink description
 	enum sink_fmt fmt;         // format expected by the sink
 	enum sink_type type;       // type of storage
 	uint32_t maxlen;           // max message length (truncated above)
diff --git a/src/cfgparse-global.c b/src/cfgparse-global.c
index 2b32497..d971c11 100644
--- a/src/cfgparse-global.c
+++ b/src/cfgparse-global.c
@@ -905,6 +905,13 @@
 		free(global.log_send_hostname);
 		global.log_send_hostname = strdup(name);
 	}
+	else if (!strcmp(args[0], "ring")) {
+		if (!parse_sinkbuf(args, &errmsg)) {
+			ha_alert("parsing [%s:%d] : %s : %s\n", file, linenum, args[0], errmsg);
+			err_code |= ERR_ALERT | ERR_FATAL;
+			goto out;
+		}
+	}
 	else if (!strcmp(args[0], "server-state-base")) { /* path base where HAProxy can find server state files */
 		if (global.server_state_base != NULL) {
 			ha_alert("parsing [%s:%d] : '%s' already specified. Continuing.\n", file, linenum, args[0]);
diff --git a/src/sink.c b/src/sink.c
index 15595cd..34bc97c 100644
--- a/src/sink.c
+++ b/src/sink.c
@@ -58,8 +58,8 @@
 	if (!sink)
 		goto end;
 
-	sink->name = name;
-	sink->desc = desc;
+	sink->name = strdup(name);
+	sink->desc = strdup(desc);
 	sink->fmt  = fmt;
 	sink->type = SINK_TYPE_NEW;
 	sink->maxlen = BUFSIZE;
@@ -123,6 +123,8 @@
 	sink->ctx.ring = ring_new(size);
 	if (!sink->ctx.ring) {
 		LIST_DEL(&sink->sink_list);
+		free(sink->name);
+		free(sink->desc);
 		free(sink);
 		goto fail;
 	}
@@ -326,10 +328,130 @@
 		if (sink->type == SINK_TYPE_BUFFER)
 			ring_free(sink->ctx.ring);
 		LIST_DEL(&sink->sink_list);
+		free(sink->name);
+		free(sink->desc);
 		free(sink);
 	}
 }
 
+/*
+ * Parse "ring" keyword and create corresponding sink buffer.
+ *
+ * The function returns 1 in success case, otherwise, it returns 0 and err is
+ * filled.
+ */
+
+int parse_sinkbuf(char **args, char **err)
+{
+	int myidx = 2;
+	size_t size = BUFSIZE;
+	size_t maxlen = size;
+	int format = SINK_FMT_RAW;
+	struct sink *sink = NULL;
+	char *desc, *name = args[1];
+	const char *inv;
+
+	if (!*name) {
+		memprintf(err, "missing sink name");
+		goto error;
+	}
+
+	inv = invalid_char(name);
+	if (inv) {
+		memprintf(err, "invalid sink name '%s' (character '%c' is not permitted)", name, *inv);
+		goto error;
+	}
+
+	desc = name;
+
+	while (*args[myidx]) {
+		if (!strcmp(args[myidx], "size")) {
+			myidx++;
+			size = atol(args[myidx]);
+			if (!size) {
+				memprintf(err, "invalid size '%s' for new sink buffer for ring '%s'", args[myidx], name);
+				goto error;
+			}
+		}
+		else if (!strcmp(args[myidx], "maxlen")) {
+			myidx++;
+			maxlen = atol(args[myidx]);
+			if (!maxlen) {
+				memprintf(err, "invalid size '%s' for new sink buffer for ring '%s'", args[myidx], name);
+				goto error;
+			}
+		}
+		else if (!strcmp(args[myidx], "maxlen")) {
+			myidx++;
+			maxlen = atol(args[myidx]);
+			if (!maxlen) {
+				memprintf(err, "invalid size '%s' for new sink buffer for ring '%s'", args[myidx], name);
+				goto error;
+			}
+		}
+		else if (!strcmp(args[myidx], "desc")) {
+			myidx++;
+			desc = args[myidx];
+		}
+		else if (!strcmp(args[myidx],"format")) {
+			myidx++;
+			if (!strcmp(args[myidx], "raw")) {
+				format = SINK_FMT_RAW;
+			}
+			else if (!strcmp(args[myidx], "short")) {
+				format = SINK_FMT_SHORT;
+			}
+			else if (!strcmp(args[myidx], "iso")) {
+				format = SINK_FMT_ISO;
+			}
+			else if (!strcmp(args[myidx], "timed")) {
+				format = SINK_FMT_TIMED;
+			}
+			else if (!strcmp(args[myidx], "rfc3164")) {
+				format = SINK_FMT_RFC3164;
+			}
+			else if (!strcmp(args[myidx], "rfc5424")) {
+				format = SINK_FMT_RFC5424;
+			}
+			else {
+				memprintf(err, "unknown format '%s' for new sink buffer for ring '%s'", args[myidx], name);
+				goto error;
+			}
+		}
+		else {
+			memprintf(err, "unknown parameter '%s' for new sink buffer for ring '%s'", args[myidx], name);
+			goto error;
+		}
+		myidx++;
+	}
+
+	if (maxlen > size) {
+		memprintf(err, "maxlen '%lu' exceeds size of the new sink buffer for ring '%s'", maxlen, name);
+		goto error;
+	}
+
+	sink = sink_new_buf(name, desc, format, size);
+	if (!sink || sink->type != SINK_TYPE_BUFFER) {
+		memprintf(err, "failed to create a new sink buffer for ring '%s'", name);
+		goto error;
+	}
+
+	sink->maxlen = maxlen;
+
+	return 1;
+error:
+	if (sink) {
+		if (sink->type == SINK_TYPE_BUFFER)
+			ring_free(sink->ctx.ring);
+		LIST_DEL(&sink->sink_list);
+		free(sink->name);
+		free(sink->desc);
+		free(sink);
+	}
+	return 0;
+
+}
+
 INITCALL0(STG_REGISTER, sink_init);
 REGISTER_POST_DEINIT(sink_deinit);