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