bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2017-2018 Dovecot authors, see the included COPYING file */
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen#define STATS_CLIENT_RECONNECT_INTERVAL_MSECS (10*1000)
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainenstatic void stats_client_connect(struct stats_client *client);
583c5314e426542f5e0fb5bf8314c31519429ef7Timo Sirainenclient_handshake_filter(const char *const *args, struct event_filter **filter_r,
583c5314e426542f5e0fb5bf8314c31519429ef7Timo Sirainen const char **error_r)
583c5314e426542f5e0fb5bf8314c31519429ef7Timo Sirainen if (!event_filter_import_unescaped(*filter_r, args+1, error_r)) {
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainenstats_client_handshake(struct stats_client *client, const char *const *args)
583c5314e426542f5e0fb5bf8314c31519429ef7Timo Sirainen if (client_handshake_filter(args, &filter, &error) < 0) {
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen i_error("stats: Received invalid handshake: %s (input: %s)",
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen client->handshake_received_at_least_once = TRUE;
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 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 /* no global filter yet - use this */
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen event_set_global_debug_send_filter(client->filter);
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainenstats_client_input_args(struct connection *conn, const char *const *args)
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen struct stats_client *client = (struct stats_client *)conn;
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen i_error("stats: Received unexpected input: %s",
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainenstatic void stats_client_reconnect(struct stats_client *client)
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainenstatic void stats_client_destroy(struct connection *conn)
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen struct stats_client *client = (struct stats_client *)conn;
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen unsigned int reconnect_msecs = STATS_CLIENT_RECONNECT_INTERVAL_MSECS;
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 /* waiting for stats handshake to finish */
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen } else if (conn->connect_finished.tv_sec != 0) {
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen if (msecs_since_connected >= STATS_CLIENT_RECONNECT_INTERVAL_MSECS) {
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen /* reconnect immdiately */
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen /* wait for reconnect interval since we last
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen were connected. */
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen reconnect_msecs = STATS_CLIENT_RECONNECT_INTERVAL_MSECS -
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainenstatic const struct connection_settings stats_client_set = {
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainenstatic const struct connection_vfuncs stats_client_vfuncs = {
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainenstatic struct event *stats_event_get_parent(struct event *event)
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen if (parent == NULL || parent->id_sent_to_stats)
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen /* avoid sending unnecessary events that don't add anything */
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainenstatic void stats_event_write(struct event *event, string_t *str, bool begin)
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen begin ? event->parent : stats_event_get_parent(event);
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 str_printfa(str, "BEGIN\t%"PRIu64"\t", event->id);
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainenstats_client_send_event(struct stats_client *client, struct event *event)
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen if (!client->handshaked || !event_filter_match(client->filter, event))
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen o_stream_nsend(client->conn.output, str_data(str), str_len(str));
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainenstats_client_free_event(struct stats_client *client, struct event *event)
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen t_strdup_printf("END\t%"PRIu64"\n", event->id));
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainenstats_event_callback(struct event *event, enum event_callback_type type,
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen const char *fmt ATTR_UNUSED, va_list args ATTR_UNUSED)
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen (struct stats_client *)stats_clients->connections;
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainenstats_category_append(string_t *str, const struct event_category *category)
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen str_append_tabescaped(str, category->parent->name);
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainenstatic void stats_category_registered(struct event_category *category)
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen (struct stats_client *)stats_clients->connections;
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen o_stream_nsend(client->conn.output, str_data(str), str_len(str));
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen stats_clients = connection_list_init(&stats_client_set,
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen event_register_callback(stats_event_callback);
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen event_category_register_callback(stats_category_registered);
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen event_unregister_callback(stats_event_callback);
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen event_category_unregister_callback(stats_category_registered);
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainenstatic void stats_client_wait_handshake(struct stats_client *client)
e36634029ba3563713aaf639abaf85200f68d635Timo Sirainen client->to_reconnect = io_loop_move_timeout(&client->to_reconnect);
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainenstatic void stats_client_send_registered_categories(struct stats_client *client)
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen categories = event_get_registered_categories(&count);
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen for (i = 0; i < count; i++)
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen o_stream_nsend(client->conn.output, str_data(str), str_len(str));
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainenstatic void stats_client_connect(struct stats_client *client)
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)
5343822b9b61907ce16993da795d08998cb6fa14Timo Sirainen i_error("net_connect_unix(%s) failed: %m", client->conn.name);
5343822b9b61907ce16993da795d08998cb6fa14Timo Sirainenstats_client_init(const char *path, bool silent_notfound_errors)
5343822b9b61907ce16993da795d08998cb6fa14Timo Sirainen client->silent_notfound_errors = silent_notfound_errors;
b63e20ea9bc84f1aa90a551f217d01385e070b73Timo Sirainen connection_init_client_unix(stats_clients, &client->conn, path);