/
niceSOFT
/
dovecot
Обзор
Документация
Войти
/
niceSOFT
/
dovecot
Код
Задачи
Вики
Пакеты
0
Релизы
0
Аналитика
Безопасность
main
src/stats/client-reader.c
296 строк
8 KB
Michael M Slusarz
COPYING: Simplify and standardize copyright handling
03 июл 2026, 02:25
03 июл 2026, 02:25
2a5809c
Код
Авторство
О чём код?
/* Copyright (c) Dovecot authors, see top-level COPYING file */ #include "stats-common.h" #include "array.h" #include "str.h" #include "stats-dist.h" #include "strescape.h" #include "connection.h" #include "ostream.h" #include "settings.h" #include "settings-parser.h" #include "master-service.h" #include "stats-metrics.h" #include "stats-settings.h" #include "event-exporter.h" #include "client-reader.h" #include "client-writer.h" #include "event-exporter.h" struct reader_client { struct connection conn; }; static struct connection_list *reader_clients = NULL; void client_reader_create(int fd) { struct reader_client *client; client = i_new(struct reader_client, 1); connection_init_server(reader_clients, &client->conn, "stats-reader", fd, fd); } static void reader_client_destroy(struct connection *conn) { connection_deinit(conn); i_free(conn); master_service_client_connection_destroyed(master_service); } static void reader_client_dump_stats(string_t *str, struct stats_dist *stats, const char *const *fields) { for (unsigned int i = 0; fields[i] != NULL; i++) { const char *field = fields[i]; str_append_c(str, '\t'); if (strcmp(field, "count") == 0) str_printfa(str, "%u", stats_dist_get_count(stats)); else if (strcmp(field, "sum") == 0) str_printfa(str, "%"PRIu64, stats_dist_get_sum(stats)); else if (strcmp(field, "min") == 0) str_printfa(str, "%"PRIu64, stats_dist_get_min(stats)); else if (strcmp(field, "max") == 0) str_printfa(str, "%"PRIu64, stats_dist_get_max(stats)); else if (strcmp(field, "avg") == 0) str_printfa(str, "%.02f", stats_dist_get_avg(stats)); else if (strcmp(field, "median") == 0) str_printfa(str, "%"PRIu64, stats_dist_get_median(stats)); else if (strcmp(field, "variance") == 0) str_printfa(str, "%.02f", stats_dist_get_variance(stats)); else if (field[0] == '%') { str_printfa(str, "%"PRIu64, stats_dist_get_percentile(stats, strtod(field+1, NULL)/100.0)); } else { /* return unknown fields as empty */ } } } static void reader_client_dump_metric(string_t *str, const struct metric *metric, const char *const *fields) { reader_client_dump_stats(str, metric->duration_stats, fields); for (unsigned int i = 0; i < metric->fields_count; i++) { str_append_c(str, '\t'); str_append_tabescaped(str, metric->fields[i].field_key); reader_client_dump_stats(str, metric->fields[i].stats, fields); } str_append_c(str, '\n'); } static void reader_client_append_sub_name(string_t *str, const char *sub_name) { for (; *sub_name != '\0'; sub_name++) { switch (*sub_name) { case '\t': case '\n': case '\r': case ' ': str_append_c(str, '_'); break; default: str_append_c(str, *sub_name); } } } static void reader_client_dump_sub_metrics(struct ostream *output, const struct metric *metric, const char *sub_name, const char *const *fields) { size_t root_pos, name_pos; struct metric *const *sub_metrics; if (!array_is_created(&metric->sub_metrics)) return; string_t *str = t_str_new(128); reader_client_append_sub_name(str, sub_name); str_append_c(str, '_'); root_pos = str->used; array_foreach(&metric->sub_metrics, sub_metrics) { str_truncate(str, root_pos); reader_client_append_sub_name(str, (*sub_metrics)->sub_name); name_pos = str->used; reader_client_dump_metric(str, *sub_metrics, fields); o_stream_nsend(output, str_data(str), str_len(str)); str_truncate(str, name_pos); reader_client_dump_sub_metrics(output, *sub_metrics, str_c(str), fields); } } static int reader_client_input_dump(struct reader_client *client, const char *const *args) { struct stats_metrics_iter *iter; const struct metric *metric; o_stream_cork(client->conn.output); iter = stats_metrics_iterate_init(stats_metrics); while ((metric = stats_metrics_iterate(iter)) != NULL) T_BEGIN { string_t *str = t_str_new(128); str_append_tabescaped(str, metric->name); reader_client_dump_metric(str, metric, args); o_stream_nsend(client->conn.output, str_data(str), str_len(str)); reader_client_dump_sub_metrics(client->conn.output, metric, metric->name, args); } T_END; o_stream_nsend(client->conn.output, "\n", 1); stats_metrics_iterate_deinit(&iter); o_stream_uncork(client->conn.output); return 1; } static int reader_client_input_dump_reset(struct reader_client *client, const char *const *args) { (void)reader_client_input_dump(client, args); stats_metrics_reset(stats_metrics); return 1; } static int reader_client_input_metrics_add(struct reader_client *client, const char *const *args) { ARRAY_TYPE(stats_metric_settings_group_by) group_by; const char *error; if (str_array_length(args) < 7) { e_error(client->conn.event, "METRICS-ADD: Not enough parameters"); return -1; } pool_t pool = pool_alloconly_create("dynamic stats metrics", 128); struct stats_metric_settings *set = settings_defaults_dup(pool, &stats_metric_setting_parser_info); set->name = p_strdup(pool, args[0]); set->description = p_strdup(pool, args[1]); set->filter = p_strdup(pool, args[4]); set->exporter = p_strdup(pool, args[5]); if (!parse_legacy_metric_group_by(pool, args[3], &group_by, &error)) { e_error(client->conn.event, "METRICS-ADD: Invalid metric_group_by: %s", error); pool_unref(&pool); return -1; } p_array_init(&set->fields, pool, 4); if (settings_parse_boollist_string(args[2], pool, &set->fields, &error) < 0) { e_error(client->conn.event, "METRICS-ADD: Invalid metric_fields: %s", error); pool_unref(&pool); return -1; } settings_boollist_finish(&set->fields, FALSE); p_array_init(&set->exporter_include, pool, 1); if (settings_parse_boollist_string(args[6], pool, &set->exporter_include, &error) < 0) { e_error(client->conn.event, "METRICS-ADD: metric_exporter_include parsing error: %s", error); pool_unref(&pool); return -1; } settings_boollist_finish(&set->exporter_include, FALSE); if (!stats_metric_setting_parser_info.check_func(set, pool, &error)) { e_error(client->conn.event, "METRICS-ADD: %s", error); pool_unref(&pool); return -1; } o_stream_cork(client->conn.output); if (stats_metrics_add_dynamic(stats_metrics, set, &group_by, &error)) { client_writer_update_connections(); o_stream_nsend(client->conn.output, "+", 1); } else { o_stream_nsend(client->conn.output, "-", 1); o_stream_nsend_str(client->conn.output, "METRICS-ADD: "); o_stream_nsend_str(client->conn.output, error); } settings_free(set); o_stream_nsend(client->conn.output, "\n", 1); o_stream_uncork(client->conn.output); return 1; } static int reader_client_input_metrics_remove(struct reader_client *client, const char *const *args) { if (str_array_length(args) < 1) { e_error(client->conn.event, "METRICS-REMOVE: Not enough parameters"); return -1; } if (stats_metrics_remove_dynamic(stats_metrics, args[0])) { client_writer_update_connections(); o_stream_nsend(client->conn.output, "+\n", 2); } else { o_stream_nsend_str(client->conn.output, t_strdup_printf("-metrics '%s' not found\n", args[0])); } return 1; } static int reader_client_input_args(struct connection *conn, const char *const *args) { struct reader_client *client = (struct reader_client *)conn; const char *cmd = args[0]; if (cmd == NULL) { e_error(conn->event, "Client sent empty line"); return 1; } args++; if (strcmp(cmd, "DUMP") == 0) return reader_client_input_dump(client, args); else if (strcmp(cmd, "METRICS-ADD") == 0) return reader_client_input_metrics_add(client, args); else if (strcmp(cmd, "METRICS-REMOVE") == 0) return reader_client_input_metrics_remove(client, args); else if (strcmp(cmd, "DUMP-RESET") == 0) return reader_client_input_dump_reset(client, args); else if (strcmp(cmd, "REOPEN") == 0) { event_exporters_reopen(); o_stream_nsend(client->conn.output, "+\n", 2); } return 1; } static struct connection_settings client_set = { .service_name_in = "stats-reader-client", .service_name_out = "stats-reader-server", .major_version = 2, .minor_version = 0, .input_max_size = 1024, .output_max_size = SIZE_MAX, .client = FALSE, }; static const struct connection_vfuncs client_vfuncs = { .destroy = reader_client_destroy, .input_args = reader_client_input_args, }; void client_readers_init(void) { reader_clients = connection_list_init(&client_set, &client_vfuncs); } void client_readers_deinit(void) { connection_list_deinit(&reader_clients); }