summaryrefslogtreecommitdiffstats
path: root/src/old-stats/mail-command.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/old-stats/mail-command.c')
-rw-r--r--src/old-stats/mail-command.c247
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);
+ }
+}