]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: sink: implement sink_find_early()
authorAurelien DARRAGON <adarragon@haproxy.com>
Thu, 10 Oct 2024 09:31:59 +0000 (11:31 +0200)
committerAurelien DARRAGON <adarragon@haproxy.com>
Thu, 10 Oct 2024 14:55:15 +0000 (16:55 +0200)
sink_find_early() is a convenient function that can be used instead of
sink_find() during parsing time in order to try to find a matching
sink even if the sink is not defined yet.

Indeed, if the sink is not defined, sink_find_early() will try to create
it and mark it as forward-declared. It will also save informations from
the caller to better identify it in case of errors.

If the sink happens to be found in the config, it will transition from
forward-declared type to its final type. Else, it means that the sink
was not found in the config, in this case, during postresolve, we raise
an error to indicate that the sink was not found in the configuration.

It should help solve postresolving issue with rings, because for now only
log targets implement proper ring postresolving.. but rings may be used
at different places in the code, such as debug() converter or in "traces"
section.

include/haproxy/sink-t.h
include/haproxy/sink.h
src/sink.c

index 990aa72d661ba5ae106a4f83e05db58c20f41720..f72a4e12a8d68744227387e62997d71b66794bcb 100644 (file)
  *   - ring buffer, readable from CLI
  */
 enum sink_type {
-       SINK_TYPE_NEW,      // not yet initialized
-       SINK_TYPE_FD,       // events sent to a file descriptor
-       SINK_TYPE_BUFFER,   // events sent to a ring buffer
+       SINK_TYPE_FORWARD_DECLARED, // was searched using sink_find_early(), is expected to exist by some component
+       SINK_TYPE_NEW,              // not yet initialized
+       SINK_TYPE_FD,               // events sent to a file descriptor
+       SINK_TYPE_BUFFER,           // events sent to a ring buffer
 };
 
 struct sink_forward_target {
@@ -50,7 +51,11 @@ struct sink_forward_target {
 struct sink {
        struct list sink_list;     // position in the sink list
        char *name;                // sink name
-       char *desc;                // sink description
+       char *desc;                /* sink description:
+                                   *   when forward-declared, holds info about where the
+                                   *   sink was first forward declared, else holds actual
+                                   *   sink description
+                                   */
        char *store;               // backing-store file when buffer
        enum log_fmt fmt;          // format expected by the sink
        enum sink_type type;       // type of storage
index bdc84475b8444f7c2938f3598bed7eefe80c12c4..ebf7c66d4dcd789c0979fe34951494a3460a3c32 100644 (file)
@@ -31,6 +31,7 @@ extern struct list sink_list;
 extern struct proxy *sink_proxies_list;
 
 struct sink *sink_find(const char *name);
+struct sink *sink_find_early(const char *name, const char *from, const char *file, int line);
 struct sink *sink_new_fd(const char *name, const char *desc, enum log_fmt, int fd);
 ssize_t __sink_write(struct sink *sink, struct log_header hdr, size_t maxlen,
                      const struct ist msg[], size_t nmsg);
index 711233793f7e4b76873f5dab5b2d7c92b28e2c30..0b951838838f8233aeabd6145a2b9121b71c4647 100644 (file)
@@ -46,7 +46,7 @@ struct proxy *sink_proxies_list;
 
 struct sink *cfg_sink;
 
-struct sink *sink_find(const char *name)
+static struct sink *_sink_find(const char *name)
 {
        struct sink *sink;
 
@@ -56,6 +56,63 @@ struct sink *sink_find(const char *name)
        return NULL;
 }
 
+/* returns sink if it really exists */
+struct sink *sink_find(const char *name)
+{
+       struct sink *sink;
+
+       sink = _sink_find(name);
+       if (sink && sink->type != SINK_TYPE_FORWARD_DECLARED)
+               return sink;
+       return NULL;
+}
+
+/* Similar to sink_find(), but intended to be used during config parsing:
+ * tries to resolve sink name, if it fails, creates the sink and marks
+ * it as forward-declared and hope that it will be defined later.
+ *
+ * The caller has to identify itself using <from>, <file> and <line> in
+ * order to report precise error messages in the event that the sink is
+ * never defined later (only the first misuse will be considered).
+ *
+ * It returns the sink on success and NULL on failure (memory error)
+ */
+struct sink *sink_find_early(const char *name, const char *from, const char *file, int line)
+{
+       struct sink *sink;
+
+       /* not expected to be used during runtime */
+       BUG_ON(!(global.mode & MODE_STARTING));
+
+       sink = _sink_find(name);
+       if (sink)
+               return sink;
+
+       /* not found, try to forward-declare it */
+       sink = calloc(1, sizeof(*sink));
+       if (!sink)
+               return NULL;
+
+       sink->name = strdup(name);
+       if (!sink->name)
+               goto err;
+
+       memprintf(&sink->desc, "parsing [%s:%d] : %s", file, line, from);
+       if (!sink->desc)
+               goto err;
+
+       sink->type = SINK_TYPE_FORWARD_DECLARED;
+       LIST_APPEND(&sink_list, &sink->sink_list);
+
+       return sink;
+
+ err:
+       ha_free(&sink->name);
+       ha_free(&sink->desc);
+       ha_free(&sink);
+       return NULL;
+}
+
 /* creates a new sink and adds it to the list, it's still generic and not fully
  * initialized. Returns NULL on allocation failure. If another one already
  * exists with the same name, it will be returned. The caller can detect it as
@@ -64,12 +121,19 @@ struct sink *sink_find(const char *name)
 static struct sink *__sink_new(const char *name, const char *desc, int fmt)
 {
        struct sink *sink;
+       uint8_t _new = 0;
 
-       sink = sink_find(name);
-       if (sink)
+       sink = _sink_find(name);
+       if (sink) {
+               if (sink->type == SINK_TYPE_FORWARD_DECLARED) {
+                       ha_free(&sink->desc); // free previous desc
+                       goto forward_declared;
+               }
                goto end;
+       }
 
        sink = calloc(1, sizeof(*sink));
+       _new = 1;
        if (!sink)
                goto end;
 
@@ -77,6 +141,7 @@ static struct sink *__sink_new(const char *name, const char *desc, int fmt)
        if (!sink->name)
                goto err;
 
+ forward_declared:
        sink->desc = strdup(desc);
        if (!sink->desc)
                goto err;
@@ -87,7 +152,8 @@ static struct sink *__sink_new(const char *name, const char *desc, int fmt)
        /* address will be filled by the caller if needed */
        sink->ctx.fd = -1;
        sink->ctx.dropped = 0;
-       LIST_APPEND(&sink_list, &sink->sink_list);
+       if (_new)
+               LIST_APPEND(&sink_list, &sink->sink_list);
  end:
        return sink;
 
@@ -1329,6 +1395,23 @@ static void sink_init()
        sink_new_buf("buf0",  "in-memory ring buffer", LOG_FORMAT_TIMED, 1048576);
 }
 
+static int sink_postcheck()
+{
+       struct sink *sink;
+
+       list_for_each_entry(sink, &sink_list, sink_list) {
+               if (sink->type == SINK_TYPE_FORWARD_DECLARED) {
+                       /* sink wasn't upgraded to actual sink despite being
+                        * forward-declared: it is an error (the sink doesn't
+                        * really exist)
+                        */
+                       ha_alert("%s: sink '%s' doesn't exist.\n", sink->desc, sink->name);
+                       return ERR_ALERT | ERR_FATAL;
+               }
+       }
+       return ERR_NONE;
+}
+
 static void sink_deinit()
 {
        struct sink *sink, *sb;
@@ -1338,6 +1421,7 @@ static void sink_deinit()
 }
 
 INITCALL0(STG_REGISTER, sink_init);
+REGISTER_POST_CHECK(sink_postcheck);
 REGISTER_POST_DEINIT(sink_deinit);
 
 static struct cli_kw_list cli_kws = {{ },{