diff options
Diffstat (limited to 'src/old-stats/mail-command.c')
-rw-r--r-- | src/old-stats/mail-command.c | 247 |
1 files changed, 247 insertions, 0 deletions
diff --git a/src/old-stats/mail-command.c b/src/old-stats/mail-command.c new file mode 100644 index 0000000..98fdc9d --- /dev/null +++ b/src/old-stats/mail-command.c @@ -0,0 +1,247 @@ +/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */ + +#include "lib.h" +#include "buffer.h" +#include "base64.h" +#include "ioloop.h" +#include "llist.h" +#include "global-memory.h" +#include "stats-settings.h" +#include "mail-stats.h" +#include "mail-session.h" +#include "mail-command.h" + +#define MAIL_COMMAND_TIMEOUT_SECS (60*15) + +/* commands are sorted by their last_update timestamp, oldest first */ +struct mail_command *stable_mail_commands_head; +struct mail_command *stable_mail_commands_tail; + +static size_t mail_command_memsize(const struct mail_command *cmd) +{ + return sizeof(*cmd) + strlen(cmd->name) + 1 + strlen(cmd->args) + 1; +} + +static struct mail_command * +mail_command_find(struct mail_session *session, unsigned int id) +{ + struct mail_command *cmd; + + i_assert(id != 0); + + if (id > session->highest_cmd_id) { + /* fast path for new commands */ + return NULL; + } + for (cmd = session->commands; cmd != NULL; cmd = cmd->session_next) { + if (cmd->id == id) + return cmd; + } + /* expired */ + return NULL; +} + +static struct mail_command * +mail_command_add(struct mail_session *session, const char *name, + const char *args) +{ + struct mail_command *cmd; + + cmd = i_malloc(MALLOC_ADD(sizeof(struct mail_command), stats_alloc_size())); + cmd->stats = (void *)(cmd + 1); + cmd->refcount = 1; /* unrefed at "done" */ + cmd->session = session; + cmd->name = i_strdup(name); + cmd->args = i_strdup(args); + cmd->last_update = ioloop_timeval; + + DLLIST2_APPEND_FULL(&stable_mail_commands_head, + &stable_mail_commands_tail, cmd, + stable_prev, stable_next); + DLLIST_PREPEND_FULL(&session->commands, cmd, + session_prev, session_next); + mail_session_ref(cmd->session); + global_memory_alloc(mail_command_memsize(cmd)); + return cmd; +} + +static void mail_command_free(struct mail_command *cmd) +{ + i_assert(cmd->refcount == 0); + + global_memory_free(mail_command_memsize(cmd)); + + DLLIST2_REMOVE_FULL(&stable_mail_commands_head, + &stable_mail_commands_tail, cmd, + stable_prev, stable_next); + DLLIST_REMOVE_FULL(&cmd->session->commands, cmd, + session_prev, session_next); + mail_session_unref(&cmd->session); + i_free(cmd->name); + i_free(cmd->args); + i_free(cmd); +} + +void mail_command_ref(struct mail_command *cmd) +{ + cmd->refcount++; +} + +void mail_command_unref(struct mail_command **_cmd) +{ + struct mail_command *cmd = *_cmd; + + i_assert(cmd->refcount > 0); + cmd->refcount--; + + *_cmd = NULL; +} + +int mail_command_update_parse(const char *const *args, const char **error_r) +{ + struct mail_session *session; + struct mail_command *cmd; + struct stats *new_stats, *diff_stats; + buffer_t *buf; + const char *error; + unsigned int i, cmd_id; + bool done = FALSE, continued = FALSE; + + /* <session guid> <cmd id> [d] <name> <args> <stats> + <session guid> <cmd id> c[d] <stats> */ + if (str_array_length(args) < 3) { + *error_r = "UPDATE-CMD: Too few parameters"; + return -1; + } + if (mail_session_get(args[0], &session, error_r) < 0) + return -1; + + if (str_to_uint(args[1], &cmd_id) < 0 || cmd_id == 0) { + *error_r = "UPDATE-CMD: Invalid command id"; + return -1; + } + for (i = 0; args[2][i] != '\0'; i++) { + switch (args[2][i]) { + case 'd': + done = TRUE; + break; + case 'c': + continued = TRUE; + break; + default: + *error_r = "UPDATE-CMD: Invalid flags parameter"; + return -1; + } + } + + cmd = mail_command_find(session, cmd_id); + if (!continued) { + /* new command */ + if (cmd != NULL) { + *error_r = "UPDATE-CMD: Duplicate new command id"; + return -1; + } + if (str_array_length(args) < 5) { + *error_r = "UPDATE-CMD: Too few parameters"; + return -1; + } + cmd = mail_command_add(session, args[3], args[4]); + cmd->id = cmd_id; + + session->highest_cmd_id = + I_MAX(session->highest_cmd_id, cmd_id); + session->num_cmds++; + session->user->num_cmds++; + session->user->domain->num_cmds++; + if (session->ip != NULL) + session->ip->num_cmds++; + mail_global_stats.num_cmds++; + args += 5; + } else { + if (cmd == NULL) { + /* already expired command, ignore */ + i_warning("UPDATE-CMD: Already expired"); + return 0; + } + args += 3; + cmd->last_update = ioloop_timeval; + } + buf = t_buffer_create(256); + if (args[0] == NULL || + base64_decode(args[0], strlen(args[0]), NULL, buf) < 0) { + *error_r = t_strdup_printf("UPDATE-CMD: Invalid base64 input"); + return -1; + } + + new_stats = stats_alloc(pool_datastack_create()); + diff_stats = stats_alloc(pool_datastack_create()); + + if (!stats_import(buf->data, buf->used, cmd->stats, new_stats, &error)) { + *error_r = t_strdup_printf("UPDATE-CMD: %s", error); + return -1; + } + + if (!stats_diff(cmd->stats, new_stats, diff_stats, &error)) { + *error_r = t_strdup_printf("UPDATE-CMD: stats shrank: %s", error); + return -1; + } + stats_add(cmd->stats, diff_stats); + + if (done) { + cmd->id = 0; + mail_command_unref(&cmd); + } + mail_session_refresh(session, NULL); + return 0; +} + +static bool mail_command_is_timed_out(struct mail_command *cmd) +{ + /* some commands like IDLE can run forever */ + return ioloop_time - cmd->last_update.tv_sec > + MAIL_COMMAND_TIMEOUT_SECS; +} + +void mail_commands_free_memory(void) +{ + unsigned int diff; + + while (stable_mail_commands_head != NULL) { + struct mail_command *cmd = stable_mail_commands_head; + + if (cmd->refcount == 0) + i_assert(cmd->id == 0); + else if (cmd->refcount == 1 && + (cmd->session->disconnected || + mail_command_is_timed_out(cmd))) { + /* session was probably lost */ + mail_command_unref(&cmd); + } else { + break; + } + mail_command_free(stable_mail_commands_head); + + if (global_used_memory < stats_settings->memory_limit || + stable_mail_commands_head == NULL) + break; + + diff = ioloop_time - stable_mail_commands_head->last_update.tv_sec; + if (diff < stats_settings->command_min_time) + break; + } +} + +void mail_commands_init(void) +{ +} + +void mail_commands_deinit(void) +{ + while (stable_mail_commands_head != NULL) { + struct mail_command *cmd = stable_mail_commands_head; + + if (cmd->id != 0) + mail_command_unref(&cmd); + mail_command_free(stable_mail_commands_head); + } +} |