MEDIUM: sink: implement sink_find_early()

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.
This commit is contained in:
Aurelien DARRAGON 2024-10-10 11:31:59 +02:00
parent ba7c03c18e
commit 1bdf6e884a
3 changed files with 98 additions and 8 deletions

View File

@ -31,6 +31,7 @@
* - ring buffer, readable from CLI
*/
enum sink_type {
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
@ -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

View 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);

View 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,6 +152,7 @@ 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;
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 = {{ },{