stats-client.c revision bcb4e51a409d94ae670de96afb8483a4f7855294
02c335c23bf5fa225a467c19f2c063fb0dc7b8c3Timo Sirainen/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "lib.h"
0536ccb51d41e3078c3a9fa33e509fb4b2420f95Timo Sirainen#include "str.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "strescape.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "ostream.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "time-util.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "lib-event-private.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#include "event-filter.h"
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen#include "connection.h"
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen#include "stats-client.h"
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen#define STATS_CLIENT_RECONNECT_INTERVAL_MSECS (10*1000)
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainenstruct stats_client {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen struct connection conn;
a10ed8c47534b4c6b6bf2711ccfe577e720a47b4Timo Sirainen struct event_filter *filter;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen struct ioloop *ioloop;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen struct timeout *to_reconnect;
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainen bool handshaked;
42dbeeb3462895b03e7633dbc59e8e191199734bTimo Sirainen bool handshake_received_at_least_once;
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen bool silent_notfound_errors;
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen};
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainenstatic struct connection_list *stats_clients;
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainenstatic void stats_client_connect(struct stats_client *client);
e815af0640b38444b31eadfaa1673bcb422e1573Timo Sirainen
3785910c303507db5f629684e6dde2cc7f83668eTimo Sirainenstatic int
1a266561b099269bef75eee1a3742e61130ef780Timo Sirainenclient_handshake_filter(const char *const *args, struct event_filter **filter_r,
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen const char **error_r)
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen{
dd2df6a67f10792ce31a3666197c0b6885893a3aTimo Sirainen if (strcmp(args[0], "FILTER") != 0) {
14175321ddb88619015866978c05a27786ca4814Timo Sirainen *error_r = "Expected FILTER";
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen return -1;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen }
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen *filter_r = event_filter_create();
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (!event_filter_import_unescaped(*filter_r, args+1, error_r)) {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen event_filter_unref(filter_r);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen return -1;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen }
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen return 0;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen}
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainenstatic int
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainenstats_client_handshake(struct stats_client *client, const char *const *args)
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen{
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen struct event_filter *filter;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen const char *error;
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (client_handshake_filter(args, &filter, &error) < 0) {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen i_error("stats: Received invalid handshake: %s (input: %s)",
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen error, t_strarray_join(args, "\t"));
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen return -1;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen }
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen client->handshaked = TRUE;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen client->handshake_received_at_least_once = TRUE;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (client->ioloop != NULL)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen io_loop_stop(client->ioloop);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (client->filter != NULL) {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen /* Filter is already set. It becomes a bit complicated to
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen change it. Since it's most likely exactly the same filter
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen anyway, just keep the old one. */
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen event_filter_unref(&filter);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen return 1;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen }
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen client->filter = filter;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (event_get_global_debug_send_filter() != NULL) {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen /* merge into the global debug send filter */
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen event_filter_merge(event_get_global_debug_send_filter(),
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen client->filter);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen } else {
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen /* no global filter yet - use this */
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen event_set_global_debug_send_filter(client->filter);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen }
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen return 1;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen}
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainenstatic int
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainenstats_client_input_args(struct connection *conn, const char *const *args)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen{
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen struct stats_client *client = (struct stats_client *)conn;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen if (!client->handshaked)
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen return stats_client_handshake(client, args);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen i_error("stats: Received unexpected input: %s",
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen t_strarray_join(args, "\t"));
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen return 0;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen}
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainenstatic void stats_client_reconnect(struct stats_client *client)
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen{
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen timeout_remove(&client->to_reconnect);
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen stats_client_connect(client);
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen}
d4fe93a9c242d745e0cf2e6cc58d5caf265de2a0Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainenstatic void stats_client_destroy(struct connection *conn)
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen{
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen struct stats_client *client = (struct stats_client *)conn;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen struct event *event;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen unsigned int reconnect_msecs = STATS_CLIENT_RECONNECT_INTERVAL_MSECS;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen /* after reconnection the IDs need to be re-sent */
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen for (event = events_get_head(); event != NULL; event = event->next)
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen event->id_sent_to_stats = FALSE;
8ad2759cf4073e3bf4fcb9222a86e2153ed31875Timo Sirainen
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen client->handshaked = FALSE;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen connection_disconnect(conn);
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen if (client->ioloop != NULL) {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen /* waiting for stats handshake to finish */
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen io_loop_stop(client->ioloop);
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen } else if (conn->connect_finished.tv_sec != 0) {
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen int msecs_since_connected =
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen timeval_diff_msecs(&ioloop_timeval,
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen &conn->connect_finished);
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen if (msecs_since_connected >= STATS_CLIENT_RECONNECT_INTERVAL_MSECS) {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen /* reconnect immdiately */
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen reconnect_msecs = 0;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen } else {
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen /* wait for reconnect interval since we last
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen were connected. */
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen reconnect_msecs = STATS_CLIENT_RECONNECT_INTERVAL_MSECS -
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen msecs_since_connected;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen }
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen if (client->to_reconnect == NULL) {
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen client->to_reconnect =
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen timeout_add(reconnect_msecs,
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen stats_client_reconnect, client);
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen }
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen}
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainenstatic const struct connection_settings stats_client_set = {
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen .service_name_in = "stats-server",
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen .service_name_out = "stats-client",
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen .major_version = 2,
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen .minor_version = 0,
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen .input_max_size = (size_t)-1,
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen .output_max_size = (size_t)-1,
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen .client = TRUE
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen};
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainenstatic const struct connection_vfuncs stats_client_vfuncs = {
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen .destroy = stats_client_destroy,
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen .input_args = stats_client_input_args,
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen};
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainenstatic struct event *stats_event_get_parent(struct event *event)
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen{
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen struct event *parent = event->parent;
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen unsigned int count;
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen if (parent == NULL || parent->id_sent_to_stats)
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen return parent;
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen /* avoid sending unnecessary events that don't add anything */
4d6c99647852146fec4f2663767b17593ef73a47Timo Sirainen (void)event_get_fields(parent, &count);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen if (count > 0)
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen return parent;
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen (void)event_get_categories(parent, &count);
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen if (count > 0)
a0261d4fe4dfd422a051e474482e88785c04ee41Timo Sirainen return parent;
1de2b5a16a455e018d8cbf72ee114d4b5d557a48Timo Sirainen return stats_event_get_parent(parent);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen}
5478609e31a7665ee108ded988a309673f221aa1Timo Sirainen
553667c748977991590854426255e1c34a615f24Timo Sirainenstatic void stats_event_write(struct event *event, string_t *str, bool begin)
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen{
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen struct event *parent_event =
553667c748977991590854426255e1c34a615f24Timo Sirainen begin ? event->parent : stats_event_get_parent(event);
c09f9f95db314e7482c95e502e1c56ed6c555797Timo Sirainen
5478609e31a7665ee108ded988a309673f221aa1Timo Sirainen /* FIXME: we could use create-timestamp of the events to figure out
5478609e31a7665ee108ded988a309673f221aa1Timo Sirainen whether to use BEGIN or to just merge the categories and fields
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen to the same EVENT. If the parent's timestamp is the same as ours,
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen don't bother using BEGIN for parent. */
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen if (parent_event != NULL) {
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen if (!parent_event->id_sent_to_stats)
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen stats_event_write(parent_event, str, TRUE);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen if (begin) {
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen str_printfa(str, "BEGIN\t%"PRIu64"\t", event->id);
597dba3488c648ffb375ee4a552bd52ac4346979Timo Sirainen event->id_sent_to_stats = TRUE;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen event->call_free = TRUE;
a33b41b1dc19c692b1283049ec4de492fdadeb9aTimo Sirainen } else {
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen str_append(str, "EVENT\t");
da2aa032ccfa8e7e4a4380ef738014549f4d2c2dTimo Sirainen }
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen str_printfa(str, "%"PRIu64"\t",
bf3fb941cc1deb06786449b89c77d9a56a07c251Timo Sirainen parent_event == NULL ? 0 : parent_event->id);
9511a40d933181045343110c8101b75887062aaeTimo Sirainen event_export(event, str);
9511a40d933181045343110c8101b75887062aaeTimo Sirainen str_append_c(str, '\n');
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen}
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainenstatic void
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainenstats_client_send_event(struct stats_client *client, struct event *event)
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen{
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen if (!client->handshaked || !event_filter_match(client->filter, event))
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen return;
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen string_t *str = t_str_new(256);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen stats_event_write(event, str, FALSE);
5fe5ea74285e2fc0fbf7568c53f251aa894650fbTimo Sirainen o_stream_nsend(client->conn.output, str_data(str), str_len(str));
}
static void
stats_client_free_event(struct stats_client *client, struct event *event)
{
if (!event->id_sent_to_stats)
return;
o_stream_nsend_str(client->conn.output,
t_strdup_printf("END\t%"PRIu64"\n", event->id));
}
static bool
stats_event_callback(struct event *event, enum event_callback_type type,
struct failure_context *ctx ATTR_UNUSED,
const char *fmt ATTR_UNUSED, va_list args ATTR_UNUSED)
{
if (stats_clients->connections == NULL)
return TRUE;
struct stats_client *client =
(struct stats_client *)stats_clients->connections;
if (client->conn.output == NULL)
return TRUE;
switch (type) {
case EVENT_CALLBACK_TYPE_EVENT:
stats_client_send_event(client, event);
break;
case EVENT_CALLBACK_TYPE_FREE:
stats_client_free_event(client, event);
break;
}
return TRUE;
}
static void
stats_category_append(string_t *str, const struct event_category *category)
{
str_append(str, "CATEGORY\t");
str_append_tabescaped(str, category->name);
if (category->parent != NULL) {
str_append_c(str, '\t');
str_append_tabescaped(str, category->parent->name);
}
str_append_c(str, '\n');
}
static void stats_category_registered(struct event_category *category)
{
if (stats_clients->connections == NULL)
return;
struct stats_client *client =
(struct stats_client *)stats_clients->connections;
if (client->conn.output == NULL)
return;
string_t *str = t_str_new(64);
stats_category_append(str, category);
o_stream_nsend(client->conn.output, str_data(str), str_len(str));
}
static void stats_global_init(void)
{
stats_clients = connection_list_init(&stats_client_set,
&stats_client_vfuncs);
event_register_callback(stats_event_callback);
event_category_register_callback(stats_category_registered);
}
static void stats_global_deinit(void)
{
event_unregister_callback(stats_event_callback);
event_category_unregister_callback(stats_category_registered);
connection_list_deinit(&stats_clients);
}
static void stats_client_wait_handshake(struct stats_client *client)
{
struct ioloop *prev_ioloop = current_ioloop;
i_assert(client->to_reconnect == NULL);
client->ioloop = io_loop_create();
connection_switch_ioloop(&client->conn);
io_loop_run(client->ioloop);
io_loop_set_current(prev_ioloop);
connection_switch_ioloop(&client->conn);
if (client->to_reconnect != NULL)
client->to_reconnect = io_loop_move_timeout(&client->to_reconnect);
io_loop_set_current(client->ioloop);
io_loop_destroy(&client->ioloop);
}
static void stats_client_send_registered_categories(struct stats_client *client)
{
struct event_category *const *categories;
unsigned int i, count;
string_t *str = t_str_new(64);
categories = event_get_registered_categories(&count);
for (i = 0; i < count; i++)
stats_category_append(str, categories[i]);
o_stream_nsend(client->conn.output, str_data(str), str_len(str));
}
static void stats_client_connect(struct stats_client *client)
{
if (connection_client_connect(&client->conn) == 0) {
/* read the handshake so the global debug filter is updated */
stats_client_send_registered_categories(client);
if (!client->handshake_received_at_least_once)
stats_client_wait_handshake(client);
} else if (!client->silent_notfound_errors ||
(errno != ENOENT && errno != ECONNREFUSED)) {
i_error("net_connect_unix(%s) failed: %m", client->conn.name);
}
}
struct stats_client *
stats_client_init(const char *path, bool silent_notfound_errors)
{
struct stats_client *client;
if (stats_clients == NULL)
stats_global_init();
client = i_new(struct stats_client, 1);
client->silent_notfound_errors = silent_notfound_errors;
connection_init_client_unix(stats_clients, &client->conn, path);
stats_client_connect(client);
return client;
}
void stats_client_deinit(struct stats_client **_client)
{
struct stats_client *client = *_client;
*_client = NULL;
event_filter_unref(&client->filter);
connection_deinit(&client->conn);
timeout_remove(&client->to_reconnect);
i_free(client);
if (stats_clients->connections == NULL)
stats_global_deinit();
}