static void stats_user_deinit(struct mail_user *user) { struct stats_user *suser = STATS_USER_CONTEXT(user); struct stats_connection *stats_conn = suser->stats_conn; i_assert(stats_user_count > 0); stats_user_count--; if (stats_global_user != NULL) { /* we were updating the session lazily. do one final update. */ i_assert(stats_global_user == user); stats_add_session(user); stats_global_user = NULL; } io_loop_context_remove_callbacks(suser->ioloop_ctx, stats_io_activate, stats_io_deactivate, user); /* send final stats before disconnection */ session_stats_refresh(user); mail_stats_connection_disconnect(stats_conn, user); if (suser->to_stats_timeout != NULL) timeout_remove(&suser->to_stats_timeout); suser->module_ctx.super.deinit(user); stats_connection_unref(&stats_conn); }
void stats_connection_connect(struct stats_connection *conn, struct mail_user *user) { struct stats_user *suser = STATS_USER_CONTEXT(user); string_t *str = t_str_new(128); str_append(str, "CONNECT\t"); /* required fields */ str_append(str, guid_128_to_string(suser->session_guid)); str_append_c(str, '\t'); str_append_tabescaped(str, user->username); str_append_c(str, '\t'); str_append_tabescaped(str, user->service); str_printfa(str, "\t%s", my_pid); /* optional fields */ if (user->local_ip != NULL) { str_append(str, "\tlip="); str_append(str, net_ip2addr(user->local_ip)); } if (user->remote_ip != NULL) { str_append(str, "\trip="); str_append(str, net_ip2addr(user->remote_ip)); } str_append_c(str, '\n'); stats_connection_send(conn, str); }
static void stats_transaction_rollback(struct mailbox_transaction_context *ctx) { struct stats_transaction_context *strans = STATS_CONTEXT(ctx); struct stats_mailbox *sbox = STATS_CONTEXT(ctx->box); struct stats_user *suser = STATS_USER_CONTEXT(ctx->box->storage->user); stats_transaction_free(suser, strans); sbox->module_ctx.super.transaction_rollback(ctx); }
static void stats_user_stats_fill(struct mail_user *user, struct stats *stats) { struct stats_user *suser = STATS_USER_CONTEXT(user); struct mail_stats *mail_stats; mail_stats = stats_fill_ptr(stats, mail_stats_item); mail_stats_fill(suser, mail_stats); suser->module_ctx.super.stats_fill(user, stats); }
static void stats_add_session(struct mail_user *user) { struct stats_user *suser = STATS_USER_CONTEXT(user); struct mail_stats new_stats; mail_stats_get(suser, &new_stats); mail_stats_add_diff(&suser->session_stats, &suser->pre_io_stats, &new_stats); suser->pre_io_stats = new_stats; }
void stats_connection_disconnect(struct stats_connection *conn, struct mail_user *user) { struct stats_user *suser = STATS_USER_CONTEXT(user); string_t *str = t_str_new(128); str_append(str, "DISCONNECT\t"); str_append(str, guid_128_to_string(suser->session_guid)); str_append_c(str, '\n'); stats_connection_send(conn, str); }
static int stats_transaction_commit(struct mailbox_transaction_context *ctx, struct mail_transaction_commit_changes *changes_r) { struct stats_transaction_context *strans = STATS_CONTEXT(ctx); struct stats_mailbox *sbox = STATS_CONTEXT(ctx->box); struct stats_user *suser = STATS_USER_CONTEXT(ctx->box->storage->user); stats_transaction_free(suser, strans); return sbox->module_ctx.super.transaction_commit(ctx, changes_r); }
void stats_connection_send_session(struct stats_connection *conn, struct mail_user *user, const struct mail_stats *stats) { struct stats_user *suser = STATS_USER_CONTEXT(user); string_t *str = t_str_new(128); str_append(str, "UPDATE-SESSION\t"); str_append(str, guid_128_to_string(suser->session_guid)); mail_stats_export(str, stats); str_append_c(str, '\n'); stats_connection_send(conn, str); }
static void stats_io_activate(void *context) { struct mail_user *user = context; struct stats_user *suser = STATS_USER_CONTEXT(user); if (stats_user_count == 1) { /* the first user sets the global user. the second user sets it to NULL. when we get back to one user we'll need to set the global user again somewhere. do it here. */ stats_global_user = user; } else { i_assert(stats_global_user == NULL); mail_stats_get(suser, &suser->pre_io_stats); } }
static void stats_io_activate(struct mail_user *user) { struct stats_user *suser = STATS_USER_CONTEXT(user); struct mail_stats *mail_stats; if (stats_user_count == 1) { /* the first user sets the global user. the second user sets it to NULL. when we get back to one user we'll need to set the global user again somewhere. do it here. */ stats_global_user = user; /* skip time spent waiting in ioloop */ mail_stats = stats_fill_ptr(suser->pre_io_stats, mail_stats_item); mail_stats->clock_time = ioloop_timeval; } else { i_assert(stats_global_user == NULL); mail_user_stats_fill(user, suser->pre_io_stats); } }
static void stats_io_deactivate(struct mail_user *user) { struct stats_user *suser = STATS_USER_CONTEXT(user); unsigned int last_update_secs; if (stats_global_user == NULL) stats_add_session(user); last_update_secs = time(NULL) - suser->last_session_update; if (last_update_secs >= suser->refresh_secs) { if (stats_global_user != NULL) stats_add_session(user); session_stats_refresh(user); } else if (suser->to_stats_timeout == NULL) { suser->to_stats_timeout = timeout_add(suser->refresh_secs*1000, session_stats_refresh_timeout, user); } }
static struct mailbox_transaction_context * stats_transaction_begin(struct mailbox *box, enum mailbox_transaction_flags flags) { struct stats_user *suser = STATS_USER_CONTEXT(box->storage->user); struct stats_mailbox *sbox = STATS_CONTEXT(box); struct mailbox_transaction_context *trans; struct stats_transaction_context *strans; trans = sbox->module_ctx.super.transaction_begin(box, flags); trans->stats_track = TRUE; strans = i_new(struct stats_transaction_context, 1); strans->trans = trans; DLLIST_PREPEND(&suser->transactions, strans); MODULE_CONTEXT_SET(trans, stats_storage_module, strans); return trans; }
static void stats_add_session(struct mail_user *user) { struct stats_user *suser = STATS_USER_CONTEXT(user); struct stats *new_stats, *diff_stats; const char *error; new_stats = stats_alloc(pool_datastack_create()); diff_stats = stats_alloc(pool_datastack_create()); mail_user_stats_fill(user, new_stats); /* we'll count new_stats-pre_io_stats and add the changes to session_stats. the new_stats can't be directly copied to session_stats because there are some fields that don't start from zero, like clock_time. (actually with stats_global_user code we're requiring that clock_time is the only such field..) */ if (!stats_diff(suser->pre_io_stats, new_stats, diff_stats, &error)) i_error("stats: session stats shrank: %s", error); stats_add(suser->session_stats, diff_stats); /* copying is only needed if stats_global_user=NULL */ stats_copy(suser->pre_io_stats, new_stats); }
static void stats_mailbox_allocated(struct mailbox *box) { struct mailbox_vfuncs *v = box->vlast; struct stats_mailbox *sbox; struct stats_user *suser = STATS_USER_CONTEXT(box->storage->user); if (suser == NULL) return; stats_register_notify_callbacks(box->storage); sbox = p_new(box->pool, struct stats_mailbox, 1); sbox->module_ctx.super = *v; box->vlast = &sbox->module_ctx.super; v->transaction_begin = stats_transaction_begin; v->transaction_commit = stats_transaction_commit; v->transaction_rollback = stats_transaction_rollback; v->search_next_nonblock = stats_search_next_nonblock; MODULE_CONTEXT_SET(box, stats_storage_module, sbox); }
static void session_stats_refresh(struct mail_user *user) { struct stats_user *suser = STATS_USER_CONTEXT(user); unsigned int to_next_secs; time_t now = time(NULL); bool changed; if (session_stats_need_send(suser, now, &changed, &to_next_secs)) { suser->session_sent_duplicate = !changed; suser->last_session_update = now; stats_copy(suser->last_sent_session_stats, suser->session_stats); mail_stats_connection_send_session(suser->stats_conn, user, suser->session_stats); } if (suser->to_stats_timeout != NULL) timeout_remove(&suser->to_stats_timeout); suser->to_stats_timeout = timeout_add(to_next_secs*1000, session_stats_refresh_timeout, user); }
static void stats_command_pre(struct client_command_context *cmd) { struct stats_user *suser = STATS_USER_CONTEXT(cmd->client->user); struct stats_client_command *scmd; static unsigned int stats_cmd_id_counter = 0; if (suser == NULL || !suser->track_commands) return; if (strcasecmp(cmd->name, "IDLE") == 0) { /* IDLE can run forever and waste stats process's memory while waiting for it to timeout. don't send them. */ return; } scmd = IMAP_STATS_IMAP_CONTEXT(cmd); if (scmd == NULL) { scmd = p_new(cmd->pool, struct stats_client_command, 1); scmd->id = ++stats_cmd_id_counter; scmd->stats = stats_alloc(cmd->pool); scmd->pre_stats = stats_alloc(cmd->pool); MODULE_CONTEXT_SET(cmd, imap_stats_imap_module, scmd); }
static bool stats_search_next_nonblock(struct mail_search_context *ctx, struct mail **mail_r, bool *tryagain_r) { struct stats_mailbox *sbox = STATS_CONTEXT(ctx->transaction->box); struct mail_user *user = ctx->transaction->box->storage->user; struct stats_user *suser = STATS_USER_CONTEXT(user); bool ret; ret = sbox->module_ctx.super. search_next_nonblock(ctx, mail_r, tryagain_r); if (!ret && !*tryagain_r) { /* end of search */ return FALSE; } if (*tryagain_r || ++suser->refresh_check_counter % REFRESH_CHECK_INTERVAL == 0) { /* a) retrying, so this is a long running search. b) we've returned enough matches */ if (time(NULL) != suser->last_session_update) session_stats_refresh(user); } return ret; }