Mercurial > dovecot > core-2.2
view src/stats/client-export.c @ 17130:add8c00fb3cc
Updated copyright notices to include year 2014.
author | Timo Sirainen <tss@iki.fi> |
---|---|
date | Tue, 04 Feb 2014 16:23:22 -0500 |
parents | d6b6d83efb4e |
children | f78d4b2bb6c3 |
line wrap: on
line source
/* Copyright (c) 2011-2014 Dovecot authors, see the included COPYING file */ #include "lib.h" #include "net.h" #include "ostream.h" #include "str.h" #include "strescape.h" #include "wildcard-match.h" #include "mail-stats.h" #include "mail-command.h" #include "mail-session.h" #include "mail-user.h" #include "mail-domain.h" #include "mail-ip.h" #include "client.h" #include "client-export.h" enum mail_export_level { MAIL_EXPORT_LEVEL_COMMAND, MAIL_EXPORT_LEVEL_SESSION, MAIL_EXPORT_LEVEL_USER, MAIL_EXPORT_LEVEL_DOMAIN, MAIL_EXPORT_LEVEL_IP }; static const char *mail_export_level_names[] = { "command", "session", "user", "domain", "ip" }; struct mail_export_filter { const char *user, *domain; struct ip_addr ip; unsigned int ip_bits; time_t since; bool connected; }; struct client_export_cmd { enum mail_export_level level; struct mail_export_filter filter; string_t *str; int (*export_iter)(struct client *client); bool header_sent; }; static int mail_export_level_parse(const char *str, enum mail_export_level *level_r) { unsigned int i; for (i = 0; i < N_ELEMENTS(mail_export_level_names); i++) { if (strcmp(mail_export_level_names[i], str) == 0) { *level_r = (enum mail_export_level)i; return 0; } } return -1; } static int mail_export_parse_filter(const char *const *args, pool_t pool, struct mail_export_filter *filter_r, const char **error_r) { unsigned long l; /* filters: user=<wildcard> | domain=<wildcard> ip=<ip>[/<mask>] since=<timestamp> connected */ memset(filter_r, 0, sizeof(*filter_r)); for (; *args != NULL; args++) { if (strncmp(*args, "user=", 5) == 0) filter_r->user = p_strdup(pool, *args + 5); else if (strncmp(*args, "domain=", 7) == 0) filter_r->domain = p_strdup(pool, *args + 7); else if (strncmp(*args, "ip=", 3) == 0) { if (net_parse_range(*args + 3, &filter_r->ip, &filter_r->ip_bits) < 0) { *error_r = "Invalid ip filter"; return -1; } } else if (strncmp(*args, "since=", 6) == 0) { if (str_to_ulong(*args + 6, &l) < 0) { *error_r = "Invalid since filter"; return -1; } filter_r->since = (time_t)l; } else if (strcmp(*args, "connected") == 0) { filter_r->connected = TRUE; } } return 0; } static void client_export_mail_stats(string_t *str, const struct mail_stats *stats) { #define MAIL_STATS_HEADER "\tuser_cpu\tsys_cpu\tclock_time" \ "\tmin_faults\tmaj_faults\tvol_cs\tinvol_cs" \ "\tdisk_input\tdisk_output" \ "\tread_count\tread_bytes\twrite_count\twrite_bytes" \ "\tmail_lookup_path\tmail_lookup_attr" \ "\tmail_read_count\tmail_read_bytes\tmail_cache_hits\n" str_printfa(str, "\t%ld.%06u", (long)stats->user_cpu.tv_sec, (unsigned int)stats->user_cpu.tv_usec); str_printfa(str, "\t%ld.%06u", (long)stats->sys_cpu.tv_sec, (unsigned int)stats->sys_cpu.tv_usec); str_printfa(str, "\t%ld.%06u", (long)stats->clock_time.tv_sec, (unsigned int)stats->clock_time.tv_usec); str_printfa(str, "\t%u\t%u", stats->min_faults, stats->maj_faults); str_printfa(str, "\t%u\t%u", stats->vol_cs, stats->invol_cs); str_printfa(str, "\t%llu\t%llu", (unsigned long long)stats->disk_input, (unsigned long long)stats->disk_output); str_printfa(str, "\t%u\t%llu\t%u\t%llu", stats->read_count, (unsigned long long)stats->read_bytes, stats->write_count, (unsigned long long)stats->write_bytes); str_printfa(str, "\t%u\t%u\t%u\t%llu\t%u", stats->mail_lookup_path, stats->mail_lookup_attr, stats->mail_read_count, (unsigned long long)stats->mail_read_bytes, stats->mail_cache_hits); } static bool mail_export_filter_match_session(const struct mail_export_filter *filter, const struct mail_session *session) { if (filter->connected && session->disconnected) return FALSE; if (filter->since > session->last_update.tv_sec) return FALSE; if (filter->user != NULL && !wildcard_match(session->user->name, filter->user)) return FALSE; if (filter->domain != NULL && !wildcard_match(session->user->domain->name, filter->domain)) return FALSE; if (filter->ip_bits > 0 && !net_is_in_network(&session->ip->ip, &filter->ip, filter->ip_bits)) return FALSE; return TRUE; } static bool mail_export_filter_match_user_common(const struct mail_export_filter *filter, const struct mail_user *user) { struct mail_session *s; bool connected = FALSE, ip_ok = FALSE; if (filter->user != NULL && !wildcard_match(user->name, filter->user)) return FALSE; if (filter->connected || filter->ip_bits > 0) { for (s = user->sessions; s != NULL; s = s->user_next) { if (!s->disconnected) connected = TRUE; if (filter->ip_bits > 0 && net_is_in_network(&s->ip->ip, &filter->ip, filter->ip_bits)) ip_ok = TRUE; } if (filter->connected && !connected) return FALSE; if (filter->ip_bits > 0 && !ip_ok) return FALSE; } return TRUE; } static bool mail_export_filter_match_user(const struct mail_export_filter *filter, const struct mail_user *user) { if (filter->since > user->last_update.tv_sec) return FALSE; if (filter->domain != NULL && !wildcard_match(user->domain->name, filter->domain)) return FALSE; return mail_export_filter_match_user_common(filter, user); } static bool mail_export_filter_match_domain(const struct mail_export_filter *filter, const struct mail_domain *domain) { struct mail_user *user; if (filter->since > domain->last_update.tv_sec) return FALSE; if (filter->domain != NULL && !wildcard_match(domain->name, filter->domain)) return FALSE; if (filter->user != NULL || filter->connected || filter->ip_bits > 0) { for (user = domain->users; user != NULL; user = user->domain_next) { if (mail_export_filter_match_user_common(filter, user)) break; } if (user == NULL) return FALSE; } return TRUE; } static bool mail_export_filter_match_ip(const struct mail_export_filter *filter, const struct mail_ip *ip) { struct mail_session *s; bool connected = FALSE, user_ok = FALSE, domain_ok = FALSE; if (filter->connected || filter->ip_bits > 0) { for (s = ip->sessions; s != NULL; s = s->ip_next) { if (!s->disconnected) connected = TRUE; if (filter->user != NULL && wildcard_match(s->user->name, filter->user)) user_ok = TRUE; if (filter->domain != NULL && wildcard_match(s->user->domain->name, filter->domain)) domain_ok = TRUE; } if (filter->connected && !connected) return FALSE; if (filter->user != NULL && !user_ok) return FALSE; if (filter->domain != NULL && !domain_ok) return FALSE; } if (filter->since > ip->last_update.tv_sec) return FALSE; if (filter->ip_bits > 0 && !net_is_in_network(&ip->ip, &filter->ip, filter->ip_bits)) return FALSE; return TRUE; } static void client_export_timeval(string_t *str, const struct timeval *tv) { str_printfa(str, "\t%ld.%06u", (long)tv->tv_sec, (unsigned int)tv->tv_usec); } static int client_export_iter_command(struct client *client) { struct client_export_cmd *cmd = client->cmd_export; struct mail_command *command = client->mail_cmd_iter; i_assert(cmd->level == MAIL_EXPORT_LEVEL_COMMAND); mail_command_unref(&client->mail_cmd_iter); if (!cmd->header_sent) { o_stream_nsend_str(client->output, "cmd\targs\tsession\tuser\tlast_update"MAIL_STATS_HEADER); cmd->header_sent = TRUE; } for (; command != NULL; command = command->stable_next) { if (client_is_busy(client)) break; if (!mail_export_filter_match_session(&cmd->filter, command->session)) continue; str_truncate(cmd->str, 0); str_append_tabescaped(cmd->str, command->name); str_append_c(cmd->str, '\t'); str_append_tabescaped(cmd->str, command->args); str_append_c(cmd->str, '\t'); T_BEGIN { str_append(cmd->str, guid_128_to_string(command->session->guid)); str_append_c(cmd->str, '\t'); str_append_tabescaped(cmd->str, command->session->user->name); } T_END; client_export_timeval(cmd->str, &command->last_update); client_export_mail_stats(cmd->str, &command->stats); str_append_c(cmd->str, '\n'); o_stream_nsend(client->output, str_data(cmd->str), str_len(cmd->str)); } if (command != NULL) { client->mail_cmd_iter = command; mail_command_ref(command); return 0; } return 1; } static int client_export_iter_session(struct client *client) { struct client_export_cmd *cmd = client->cmd_export; struct mail_session *session = client->mail_session_iter; i_assert(cmd->level == MAIL_EXPORT_LEVEL_SESSION); mail_session_unref(&client->mail_session_iter); if (!cmd->header_sent) { o_stream_nsend_str(client->output, "session\tuser\tip\tservice\tpid\tconnected" "\tlast_update\tnum_cmds" MAIL_STATS_HEADER); cmd->header_sent = TRUE; } for (; session != NULL; session = session->stable_next) { if (client_is_busy(client)) break; if (!mail_export_filter_match_session(&cmd->filter, session)) continue; str_truncate(cmd->str, 0); T_BEGIN { str_append(cmd->str, guid_128_to_string(session->guid)); str_append_c(cmd->str, '\t'); str_append_tabescaped(cmd->str, session->user->name); str_append_c(cmd->str, '\t'); if (session->ip != NULL) { str_append(cmd->str, net_ip2addr(&session->ip->ip)); } str_append_c(cmd->str, '\t'); str_append_tabescaped(cmd->str, session->service); } T_END; str_printfa(cmd->str, "\t%ld", (long)session->pid); str_printfa(cmd->str, "\t%d", !session->disconnected); client_export_timeval(cmd->str, &session->last_update); str_printfa(cmd->str, "\t%u", session->num_cmds); client_export_mail_stats(cmd->str, &session->stats); str_append_c(cmd->str, '\n'); o_stream_nsend(client->output, str_data(cmd->str), str_len(cmd->str)); } if (session != NULL) { client->mail_session_iter = session; mail_session_ref(session); return 0; } return 1; } static int client_export_iter_user(struct client *client) { struct client_export_cmd *cmd = client->cmd_export; struct mail_user *user = client->mail_user_iter; i_assert(cmd->level == MAIL_EXPORT_LEVEL_USER); mail_user_unref(&client->mail_user_iter); if (!cmd->header_sent) { o_stream_nsend_str(client->output, "user\treset_timestamp\tlast_update" "\tnum_logins\tnum_cmds"MAIL_STATS_HEADER); cmd->header_sent = TRUE; } for (; user != NULL; user = user->stable_next) { if (client_is_busy(client)) break; if (!mail_export_filter_match_user(&cmd->filter, user)) continue; str_truncate(cmd->str, 0); str_append_tabescaped(cmd->str, user->name); str_printfa(cmd->str, "\t%ld", (long)user->reset_timestamp); client_export_timeval(cmd->str, &user->last_update); str_printfa(cmd->str, "\t%u\t%u", user->num_logins, user->num_cmds); client_export_mail_stats(cmd->str, &user->stats); str_append_c(cmd->str, '\n'); o_stream_nsend(client->output, str_data(cmd->str), str_len(cmd->str)); } if (user != NULL) { client->mail_user_iter = user; mail_user_ref(user); return 0; } return 1; } static int client_export_iter_domain(struct client *client) { struct client_export_cmd *cmd = client->cmd_export; struct mail_domain *domain = client->mail_domain_iter; i_assert(cmd->level == MAIL_EXPORT_LEVEL_DOMAIN); mail_domain_unref(&client->mail_domain_iter); if (!cmd->header_sent) { o_stream_nsend_str(client->output, "domain\treset_timestamp\tlast_update" "\tnum_logins\tnum_cmds"MAIL_STATS_HEADER); cmd->header_sent = TRUE; } for (; domain != NULL; domain = domain->stable_next) { if (client_is_busy(client)) break; if (!mail_export_filter_match_domain(&cmd->filter, domain)) continue; str_truncate(cmd->str, 0); str_append_tabescaped(cmd->str, domain->name); str_printfa(cmd->str, "\t%ld", (long)domain->reset_timestamp); client_export_timeval(cmd->str, &domain->last_update); str_printfa(cmd->str, "\t%u\t%u", domain->num_logins, domain->num_cmds); client_export_mail_stats(cmd->str, &domain->stats); str_append_c(cmd->str, '\n'); o_stream_nsend(client->output, str_data(cmd->str), str_len(cmd->str)); } if (domain != NULL) { client->mail_domain_iter = domain; mail_domain_ref(domain); return 0; } return 1; } static int client_export_iter_ip(struct client *client) { struct client_export_cmd *cmd = client->cmd_export; struct mail_ip *ip = client->mail_ip_iter; i_assert(cmd->level == MAIL_EXPORT_LEVEL_IP); mail_ip_unref(&client->mail_ip_iter); if (!cmd->header_sent) { o_stream_nsend_str(client->output, "ip\treset_timestamp\tlast_update" "\tnum_logins\tnum_cmds"MAIL_STATS_HEADER); cmd->header_sent = TRUE; } for (; ip != NULL; ip = ip->stable_next) { if (client_is_busy(client)) break; if (!mail_export_filter_match_ip(&cmd->filter, ip)) continue; str_truncate(cmd->str, 0); T_BEGIN { str_append(cmd->str, net_ip2addr(&ip->ip)); } T_END; str_printfa(cmd->str, "\t%ld", (long)ip->reset_timestamp); client_export_timeval(cmd->str, &ip->last_update); str_printfa(cmd->str, "\t%u\t%u", ip->num_logins, ip->num_cmds); client_export_mail_stats(cmd->str, &ip->stats); str_append_c(cmd->str, '\n'); o_stream_nsend(client->output, str_data(cmd->str), str_len(cmd->str)); } if (ip != NULL) { client->mail_ip_iter = ip; mail_ip_ref(ip); return 0; } return 1; } static int client_export_more(struct client *client) { if (client->cmd_export->export_iter(client) == 0) return 0; o_stream_nsend_str(client->output, "\n"); return 1; } static bool client_export_iter_init(struct client *client) { struct client_export_cmd *cmd = client->cmd_export; if (cmd->filter.user != NULL && strchr(cmd->filter.user, '*') == NULL && (cmd->level == MAIL_EXPORT_LEVEL_USER || cmd->level == MAIL_EXPORT_LEVEL_SESSION)) { /* exact user */ struct mail_user *user = mail_user_lookup(cmd->filter.user); if (user == NULL) return FALSE; if (cmd->level == MAIL_EXPORT_LEVEL_SESSION) { client->mail_session_iter = user->sessions; if (client->mail_session_iter == NULL) return FALSE; mail_session_ref(client->mail_session_iter); cmd->export_iter = client_export_iter_session; } else { client->mail_user_iter = user; mail_user_ref(user); cmd->export_iter = client_export_iter_user; } return TRUE; } if (cmd->filter.ip_bits == IPADDR_BITS(&cmd->filter.ip) && (cmd->level == MAIL_EXPORT_LEVEL_IP || cmd->level == MAIL_EXPORT_LEVEL_SESSION)) { /* exact IP address */ struct mail_ip *ip = mail_ip_lookup(&cmd->filter.ip); if (ip == NULL) return FALSE; if (cmd->level == MAIL_EXPORT_LEVEL_SESSION) { client->mail_session_iter = ip->sessions; if (client->mail_session_iter == NULL) return FALSE; mail_session_ref(client->mail_session_iter); cmd->export_iter = client_export_iter_session; } else { client->mail_ip_iter = ip; mail_ip_ref(ip); cmd->export_iter = client_export_iter_ip; } return TRUE; } if (cmd->filter.domain != NULL && strchr(cmd->filter.domain, '*') == NULL && (cmd->level == MAIL_EXPORT_LEVEL_DOMAIN || cmd->level == MAIL_EXPORT_LEVEL_USER)) { /* exact domain */ struct mail_domain *domain = mail_domain_lookup(cmd->filter.domain); if (domain == NULL) return FALSE; if (cmd->level == MAIL_EXPORT_LEVEL_USER) { client->mail_user_iter = domain->users; mail_user_ref(client->mail_user_iter); cmd->export_iter = client_export_iter_user; } else { client->mail_domain_iter = domain; mail_domain_ref(domain); cmd->export_iter = client_export_iter_domain; } return TRUE; } switch (cmd->level) { case MAIL_EXPORT_LEVEL_COMMAND: client->mail_cmd_iter = stable_mail_commands_head; if (client->mail_cmd_iter == NULL) return FALSE; mail_command_ref(client->mail_cmd_iter); cmd->export_iter = client_export_iter_command; break; case MAIL_EXPORT_LEVEL_SESSION: client->mail_session_iter = stable_mail_sessions; if (client->mail_session_iter == NULL) return FALSE; mail_session_ref(client->mail_session_iter); cmd->export_iter = client_export_iter_session; break; case MAIL_EXPORT_LEVEL_USER: client->mail_user_iter = stable_mail_users; if (client->mail_user_iter == NULL) return FALSE; mail_user_ref(client->mail_user_iter); cmd->export_iter = client_export_iter_user; break; case MAIL_EXPORT_LEVEL_DOMAIN: client->mail_domain_iter = stable_mail_domains; if (client->mail_domain_iter == NULL) return FALSE; mail_domain_ref(client->mail_domain_iter); cmd->export_iter = client_export_iter_domain; break; case MAIL_EXPORT_LEVEL_IP: client->mail_ip_iter = stable_mail_ips; if (client->mail_ip_iter == NULL) return FALSE; mail_ip_ref(client->mail_ip_iter); cmd->export_iter = client_export_iter_ip; break; } i_assert(cmd->export_iter != NULL); return TRUE; } int client_export(struct client *client, const char *const *args, const char **error_r) { const char *level_str = args[0]; struct client_export_cmd *cmd; p_clear(client->cmd_pool); cmd = p_new(client->cmd_pool, struct client_export_cmd, 1); cmd->str = str_new(client->cmd_pool, 256); if (level_str == NULL) { *error_r = "Missing level parameter"; return -1; } if (mail_export_level_parse(level_str, &cmd->level) < 0) { *error_r = "Invalid level"; return -1; } if (mail_export_parse_filter(args + 1, client->cmd_pool, &cmd->filter, error_r) < 0) return -1; client->cmd_export = cmd; if (!client_export_iter_init(client)) { /* nothing to export */ o_stream_nsend_str(client->output, "\n"); return 1; } client->cmd_more = client_export_more; return client_export_more(client); }