~james-page/ubuntu/raring/dovecot/autopkgtest

« back to all changes in this revision

Viewing changes to src/plugins/stats/stats-plugin.c

  • Committer: Package Import Robot
  • Author(s): James Page
  • Date: 2012-06-11 11:11:54 UTC
  • mfrom: (1.15.2) (4.1.27 sid)
  • Revision ID: package-import@ubuntu.com-20120611111154-678cwbdj6ktgsv1h
Tags: 1:2.1.7-1ubuntu1
* Merge from Debian unstable, remaining changes:
  + Add mail-stack-delivery package:
    - Update d/rules
    - d/control: convert existing dovecot-postfix package to a dummy
      package and add new mail-stack-delivery package.
    - Update maintainer scripts.
    - Rename d/dovecot-postfix.* to debian/mail-stack-delivery.*
    - d/mail-stack-delivery.preinst: Move previously installed backups and
      config files to a new package namespace.
    - d/mail-stack-delivery.prerm: Added to handle downgrades.
  + Use Snakeoil SSL certificates by default:
    - d/control: Depend on ssl-cert.
    - d/dovecot-core.postinst: Relax grep for SSL_* a bit.
  + Add autopkgtest to debian/tests/*.
  + Add ufw integration:
    - d/dovecot-core.ufw.profile: new ufw profile.
    - d/rules: install profile in dovecot-core.
    - d/control: dovecot-core - suggest ufw.
  + d/{control,rules}: enable PIE hardening.
  + d/dovecot-core.dirs: Added usr/share/doc/dovecot-core
  + Add apport hook:
    - d/rules, d/source_dovecot.py
  + Add upstart job:
    - d/rules, d/dovecot-core.dovecot.upstart, d/control,
      d/dovecot-core.dirs, dovecot-imapd.{postrm, postinst, prerm},
      d/dovecot-pop3d.{postinst, postrm, prerm}.
      d/mail-stack-deliver.postinst: Convert init script to upstart.
  + d/control: Added Pre-Depends: dpkg (>= 1.15.6) to dovecot-dbg to support
    xz compression in Ubuntu.
  + d/control: Demote dovecot-common Recommends: to Suggests: to prevent
    install of extra packages on upgrade.
  + d/patches/dovecot-drac.patch: Updated with version for dovecot >= 2.0.0.
  + d/control: Drop B-D on systemd.
* Dropped changes:
  + d/patches/fix-racey-restart.patch: part of 2.1.x, no longer required.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* Copyright (c) 2011-2012 Dovecot authors, see the included COPYING file */
 
2
 
 
3
#include "lib.h"
 
4
#include "ioloop.h"
 
5
#include "llist.h"
 
6
#include "str.h"
 
7
#include "time-util.h"
 
8
#include "settings-parser.h"
 
9
#include "stats-connection.h"
 
10
#include "stats-plugin.h"
 
11
 
 
12
#include <sys/time.h>
 
13
#include <sys/resource.h>
 
14
 
 
15
#define STATS_CONTEXT(obj) \
 
16
        MODULE_CONTEXT(obj, stats_storage_module)
 
17
 
 
18
/* If session isn't refreshed every 15 minutes, it's dropped.
 
19
   Must be smaller than MAIL_SESSION_IDLE_TIMEOUT_MSECS in stats server */
 
20
#define SESSION_STATS_FORCE_REFRESH_SECS (5*60)
 
21
#define REFRESH_CHECK_INTERVAL 100
 
22
#define MAIL_STATS_SOCKET_NAME "stats-mail"
 
23
#define PROC_IO_PATH "/proc/self/io"
 
24
 
 
25
#define USECS_PER_SEC 1000000
 
26
 
 
27
struct stats_transaction_context {
 
28
        union mailbox_transaction_module_context module_ctx;
 
29
 
 
30
        struct stats_transaction_context *prev, *next;
 
31
        struct mailbox_transaction_context *trans;
 
32
 
 
33
        struct mailbox_transaction_stats prev_stats;
 
34
};
 
35
 
 
36
struct stats_mailbox {
 
37
        union mailbox_module_context module_ctx;
 
38
};
 
39
 
 
40
const char *stats_plugin_version = DOVECOT_VERSION;
 
41
 
 
42
struct stats_user_module stats_user_module =
 
43
        MODULE_CONTEXT_INIT(&mail_user_module_register);
 
44
static MODULE_CONTEXT_DEFINE_INIT(stats_storage_module,
 
45
                                  &mail_storage_module_register);
 
46
 
 
47
static bool proc_io_disabled = FALSE;
 
48
static int proc_io_fd = -1;
 
49
 
 
50
static struct stats_connection *global_stats_conn = NULL;
 
51
static struct mail_user *stats_global_user = NULL;
 
52
static unsigned int stats_user_count = 0;
 
53
 
 
54
static void session_stats_refresh_timeout(struct mail_user *user);
 
55
 
 
56
static void trans_stats_dec(struct mailbox_transaction_stats *dest,
 
57
                            const struct mailbox_transaction_stats *src)
 
58
{
 
59
        dest->open_lookup_count -= src->open_lookup_count;
 
60
        dest->stat_lookup_count -= src->stat_lookup_count;
 
61
        dest->fstat_lookup_count -= src->fstat_lookup_count;
 
62
        dest->files_read_count -= src->files_read_count;
 
63
        dest->files_read_bytes -= src->files_read_bytes;
 
64
        dest->cache_hit_count -= src->cache_hit_count;
 
65
}
 
66
 
 
67
static void trans_stats_add(struct mailbox_transaction_stats *dest,
 
68
                            const struct mailbox_transaction_stats *src)
 
69
{
 
70
        dest->open_lookup_count += src->open_lookup_count;
 
71
        dest->stat_lookup_count += src->stat_lookup_count;
 
72
        dest->fstat_lookup_count += src->fstat_lookup_count;
 
73
        dest->files_read_count += src->files_read_count;
 
74
        dest->files_read_bytes += src->files_read_bytes;
 
75
        dest->cache_hit_count += src->cache_hit_count;
 
76
}
 
77
 
 
78
static void user_trans_stats_get(struct stats_user *suser,
 
79
                                 struct mailbox_transaction_stats *dest_r)
 
80
{
 
81
        struct stats_transaction_context *strans;
 
82
 
 
83
        memset(dest_r, 0, sizeof(*dest_r));
 
84
        strans = suser->transactions;
 
85
        for (; strans != NULL; strans = strans->next)
 
86
                trans_stats_add(dest_r, &strans->trans->stats);
 
87
}
 
88
 
 
89
static int
 
90
process_io_buffer_parse(const char *buf, struct mail_stats *stats)
 
91
{
 
92
        const char *const *tmp;
 
93
 
 
94
        tmp = t_strsplit(buf, "\n");
 
95
        for (; *tmp != NULL; tmp++) {
 
96
                if (strncmp(*tmp, "rchar: ", 7) == 0) {
 
97
                        if (str_to_uint64(*tmp + 7, &stats->read_bytes) < 0)
 
98
                                return -1;
 
99
                } else if (strncmp(*tmp, "wchar: ", 7) == 0) {
 
100
                        if (str_to_uint64(*tmp + 7, &stats->write_bytes) < 0)
 
101
                                return -1;
 
102
                } else if (strncmp(*tmp, "syscr: ", 7) == 0) {
 
103
                        if (str_to_uint32(*tmp + 7, &stats->read_count) < 0)
 
104
                                return -1;
 
105
                } else if (strncmp(*tmp, "syscw: ", 7) == 0) {
 
106
                        if (str_to_uint32(*tmp + 7, &stats->write_count) < 0)
 
107
                                return -1;
 
108
                }
 
109
        }
 
110
        return 0;
 
111
}
 
112
 
 
113
static int process_io_open(void)
 
114
{
 
115
        if (proc_io_fd == -1) {
 
116
                if (proc_io_disabled)
 
117
                        return -1;
 
118
                proc_io_fd = open(PROC_IO_PATH, O_RDONLY);
 
119
                if (proc_io_fd == -1) {
 
120
                        if (errno != ENOENT)
 
121
                                i_error("open(%s) failed: %m", PROC_IO_PATH);
 
122
                        proc_io_disabled = TRUE;
 
123
                        return -1;
 
124
                }
 
125
        }
 
126
        return proc_io_fd;
 
127
}
 
128
 
 
129
static void process_read_io_stats(struct mail_stats *stats)
 
130
{
 
131
        char buf[1024];
 
132
        int fd, ret;
 
133
 
 
134
        if ((fd = process_io_open()) == -1)
 
135
                return;
 
136
 
 
137
        ret = pread(fd, buf, sizeof(buf), 0);
 
138
        if (ret <= 0) {
 
139
                if (ret == -1)
 
140
                        i_error("read(%s) failed: %m", PROC_IO_PATH);
 
141
                else
 
142
                        i_error("read(%s) returned EOF", PROC_IO_PATH);
 
143
        } else if (ret == sizeof(buf)) {
 
144
                /* just shouldn't happen.. */
 
145
                i_error("%s is larger than expected", PROC_IO_PATH);
 
146
                proc_io_disabled = TRUE;
 
147
        } else {
 
148
                buf[ret] = '\0';
 
149
                T_BEGIN {
 
150
                        if (process_io_buffer_parse(buf, stats) < 0) {
 
151
                                i_error("Invalid input in file %s",
 
152
                                        PROC_IO_PATH);
 
153
                                proc_io_disabled = TRUE;
 
154
                        }
 
155
                } T_END;
 
156
        }
 
157
}
 
158
 
 
159
void mail_stats_get(struct stats_user *suser, struct mail_stats *stats_r)
 
160
{
 
161
        struct rusage usage;
 
162
 
 
163
        memset(stats_r, 0, sizeof(*stats_r));
 
164
        /* cputime */
 
165
        if (getrusage(RUSAGE_SELF, &usage) < 0)
 
166
                memset(&usage, 0, sizeof(usage));
 
167
        stats_r->user_cpu = usage.ru_utime;
 
168
        stats_r->sys_cpu = usage.ru_stime;
 
169
        stats_r->min_faults = usage.ru_minflt;
 
170
        stats_r->maj_faults = usage.ru_majflt;
 
171
        stats_r->vol_cs = usage.ru_nvcsw;
 
172
        stats_r->invol_cs = usage.ru_nivcsw;
 
173
        stats_r->disk_input = (unsigned long long)usage.ru_inblock * 512ULL;
 
174
        stats_r->disk_output = (unsigned long long)usage.ru_oublock * 512ULL;
 
175
        process_read_io_stats(stats_r);
 
176
        user_trans_stats_get(suser, &stats_r->trans_stats);
 
177
}
 
178
 
 
179
static void stats_io_activate(void *context)
 
180
{
 
181
        struct mail_user *user = context;
 
182
        struct stats_user *suser = STATS_USER_CONTEXT(user);
 
183
 
 
184
        if (stats_user_count == 1) {
 
185
                /* the first user sets the global user. the second user sets
 
186
                   it to NULL. when we get back to one user we'll need to set
 
187
                   the global user again somewhere. do it here. */
 
188
                stats_global_user = user;
 
189
        } else {
 
190
                i_assert(stats_global_user == NULL);
 
191
 
 
192
                mail_stats_get(suser, &suser->pre_io_stats);
 
193
        }
 
194
}
 
195
 
 
196
static void timeval_add_diff(struct timeval *dest,
 
197
                             const struct timeval *newsrc,
 
198
                             const struct timeval *oldsrc)
 
199
{
 
200
        long long usecs;
 
201
 
 
202
        usecs = timeval_diff_usecs(newsrc, oldsrc);
 
203
        dest->tv_sec += usecs / USECS_PER_SEC;
 
204
        dest->tv_usec += usecs % USECS_PER_SEC;
 
205
        if (dest->tv_usec > USECS_PER_SEC) {
 
206
                dest->tv_usec -= USECS_PER_SEC;
 
207
                dest->tv_sec++;
 
208
        }
 
209
}
 
210
 
 
211
void mail_stats_add_diff(struct mail_stats *dest,
 
212
                         const struct mail_stats *old_stats,
 
213
                         const struct mail_stats *new_stats)
 
214
{
 
215
        dest->disk_input += new_stats->disk_input - old_stats->disk_input;
 
216
        dest->disk_output += new_stats->disk_output - old_stats->disk_output;
 
217
        dest->min_faults += new_stats->min_faults - old_stats->min_faults;
 
218
        dest->maj_faults += new_stats->maj_faults - old_stats->maj_faults;
 
219
        dest->vol_cs += new_stats->vol_cs - old_stats->vol_cs;
 
220
        dest->invol_cs += new_stats->invol_cs - old_stats->invol_cs;
 
221
        dest->read_count += new_stats->read_count - old_stats->read_count;
 
222
        dest->write_count += new_stats->write_count - old_stats->write_count;
 
223
        dest->read_bytes += new_stats->read_bytes - old_stats->read_bytes;
 
224
        dest->write_bytes += new_stats->write_bytes - old_stats->write_bytes;
 
225
 
 
226
        timeval_add_diff(&dest->user_cpu, &new_stats->user_cpu,
 
227
                         &old_stats->user_cpu);
 
228
        timeval_add_diff(&dest->sys_cpu, &new_stats->sys_cpu,
 
229
                         &old_stats->sys_cpu);
 
230
        trans_stats_dec(&dest->trans_stats, &old_stats->trans_stats);
 
231
        trans_stats_add(&dest->trans_stats, &new_stats->trans_stats);
 
232
}
 
233
 
 
234
void mail_stats_export(string_t *str, const struct mail_stats *stats)
 
235
{
 
236
        const struct mailbox_transaction_stats *tstats = &stats->trans_stats;
 
237
 
 
238
        str_printfa(str, "\tucpu=%ld.%ld", (long)stats->user_cpu.tv_sec,
 
239
                    (long)stats->user_cpu.tv_usec);
 
240
        str_printfa(str, "\tscpu=%ld.%ld", (long)stats->sys_cpu.tv_sec,
 
241
                    (long)stats->sys_cpu.tv_usec);
 
242
        str_printfa(str, "\tminflt=%u", stats->min_faults);
 
243
        str_printfa(str, "\tmajflt=%u", stats->maj_faults);
 
244
        str_printfa(str, "\tvolcs=%u", stats->vol_cs);
 
245
        str_printfa(str, "\tinvolcs=%u", stats->invol_cs);
 
246
        str_printfa(str, "\tdiskin=%llu",
 
247
                    (unsigned long long)stats->disk_input);
 
248
        str_printfa(str, "\tdiskout=%llu",
 
249
                    (unsigned long long)stats->disk_output);
 
250
        str_printfa(str, "\trchar=%llu",
 
251
                    (unsigned long long)stats->read_bytes);
 
252
        str_printfa(str, "\twchar=%llu",
 
253
                    (unsigned long long)stats->write_bytes);
 
254
        str_printfa(str, "\tsyscr=%u", stats->read_count);
 
255
        str_printfa(str, "\tsyscw=%u", stats->write_count);
 
256
        str_printfa(str, "\tmlpath=%lu",
 
257
                    tstats->open_lookup_count + tstats->stat_lookup_count);
 
258
        str_printfa(str, "\tmlattr=%lu",
 
259
                    tstats->fstat_lookup_count + tstats->stat_lookup_count);
 
260
        str_printfa(str, "\tmrcount=%lu", tstats->files_read_count);
 
261
        str_printfa(str, "\tmrbytes=%llu", tstats->files_read_bytes);
 
262
        str_printfa(str, "\tmcache=%lu", tstats->cache_hit_count);
 
263
}
 
264
 
 
265
static void stats_add_session(struct mail_user *user)
 
266
{
 
267
        struct stats_user *suser = STATS_USER_CONTEXT(user);
 
268
        struct mail_stats new_stats;
 
269
 
 
270
        mail_stats_get(suser, &new_stats);
 
271
        mail_stats_add_diff(&suser->session_stats, &suser->pre_io_stats,
 
272
                            &new_stats);
 
273
        suser->pre_io_stats = new_stats;
 
274
}
 
275
 
 
276
static bool session_has_changed(const struct mail_stats *prev,
 
277
                                const struct mail_stats *cur)
 
278
{
 
279
        if (cur->disk_input != prev->disk_input ||
 
280
            cur->disk_output != prev->disk_output ||
 
281
            memcmp(&cur->trans_stats, &prev->trans_stats,
 
282
                   sizeof(cur->trans_stats)) != 0)
 
283
                return TRUE;
 
284
 
 
285
        /* allow a tiny bit of changes that are caused by this
 
286
           timeout handling */
 
287
        if (timeval_diff_msecs(&cur->user_cpu, &prev->user_cpu) != 0)
 
288
                return TRUE;
 
289
        if (timeval_diff_msecs(&cur->sys_cpu, &prev->sys_cpu) != 0)
 
290
                return TRUE;
 
291
 
 
292
        if (cur->maj_faults > prev->maj_faults+10)
 
293
                return TRUE;
 
294
        if (cur->invol_cs > prev->invol_cs+10)
 
295
                return TRUE;
 
296
        /* don't check for read/write count/bytes changes, since they get
 
297
           changed by stats checking itself */
 
298
        return FALSE;
 
299
}
 
300
 
 
301
static bool
 
302
session_stats_need_send(struct stats_user *suser, time_t now,
 
303
                        bool *changed_r, unsigned int *to_next_secs_r)
 
304
{
 
305
        unsigned int diff;
 
306
 
 
307
        *to_next_secs_r = SESSION_STATS_FORCE_REFRESH_SECS;
 
308
 
 
309
        if (session_has_changed(&suser->last_sent_session_stats,
 
310
                                &suser->session_stats)) {
 
311
                *to_next_secs_r = suser->refresh_secs;
 
312
                *changed_r = TRUE;
 
313
                return TRUE;
 
314
        }
 
315
        *changed_r = FALSE;
 
316
 
 
317
        if (!suser->session_sent_duplicate) {
 
318
                if (suser->last_session_update != now) {
 
319
                        /* send one duplicate notification so stats reader
 
320
                           knows that this session is idle now */
 
321
                        return TRUE;
 
322
                }
 
323
                *to_next_secs_r = 1;
 
324
                return FALSE;
 
325
        }
 
326
 
 
327
        diff = now - suser->last_session_update;
 
328
        if (diff < SESSION_STATS_FORCE_REFRESH_SECS) {
 
329
                *to_next_secs_r = SESSION_STATS_FORCE_REFRESH_SECS - diff;
 
330
                return FALSE;
 
331
        }
 
332
        return TRUE;
 
333
}
 
334
 
 
335
static void session_stats_refresh(struct mail_user *user)
 
336
{
 
337
        struct stats_user *suser = STATS_USER_CONTEXT(user);
 
338
        unsigned int to_next_secs;
 
339
        time_t now = time(NULL);
 
340
        bool changed;
 
341
 
 
342
        if (session_stats_need_send(suser, now, &changed, &to_next_secs)) {
 
343
                suser->session_sent_duplicate = !changed;
 
344
                suser->last_session_update = now;
 
345
                suser->last_sent_session_stats = suser->session_stats;
 
346
                stats_connection_send_session(suser->stats_conn, user,
 
347
                                              &suser->session_stats);
 
348
        }
 
349
 
 
350
        if (suser->to_stats_timeout != NULL)
 
351
                timeout_remove(&suser->to_stats_timeout);
 
352
        suser->to_stats_timeout =
 
353
                timeout_add(to_next_secs*1000,
 
354
                            session_stats_refresh_timeout, user);
 
355
}
 
356
 
 
357
static struct mailbox_transaction_context *
 
358
stats_transaction_begin(struct mailbox *box,
 
359
                        enum mailbox_transaction_flags flags)
 
360
{
 
361
        struct stats_user *suser = STATS_USER_CONTEXT(box->storage->user);
 
362
        struct stats_mailbox *sbox = STATS_CONTEXT(box);
 
363
        struct mailbox_transaction_context *trans;
 
364
        struct stats_transaction_context *strans;
 
365
 
 
366
        trans = sbox->module_ctx.super.transaction_begin(box, flags);
 
367
        trans->stats_track = TRUE;
 
368
 
 
369
        strans = i_new(struct stats_transaction_context, 1);
 
370
        strans->trans = trans;
 
371
        DLLIST_PREPEND(&suser->transactions, strans);
 
372
 
 
373
        MODULE_CONTEXT_SET(trans, stats_storage_module, strans);
 
374
        return trans;
 
375
}
 
376
 
 
377
static void stats_transaction_free(struct stats_user *suser,
 
378
                                   struct stats_transaction_context *strans)
 
379
{
 
380
        DLLIST_REMOVE(&suser->transactions, strans);
 
381
 
 
382
        trans_stats_add(&suser->session_stats.trans_stats,
 
383
                        &strans->trans->stats);
 
384
}
 
385
 
 
386
static int
 
387
stats_transaction_commit(struct mailbox_transaction_context *ctx,
 
388
                         struct mail_transaction_commit_changes *changes_r)
 
389
{
 
390
        struct stats_transaction_context *strans = STATS_CONTEXT(ctx);
 
391
        struct stats_mailbox *sbox = STATS_CONTEXT(ctx->box);
 
392
        struct stats_user *suser = STATS_USER_CONTEXT(ctx->box->storage->user);
 
393
 
 
394
        stats_transaction_free(suser, strans);
 
395
        return sbox->module_ctx.super.transaction_commit(ctx, changes_r);
 
396
}
 
397
 
 
398
static void
 
399
stats_transaction_rollback(struct mailbox_transaction_context *ctx)
 
400
{
 
401
        struct stats_transaction_context *strans = STATS_CONTEXT(ctx);
 
402
        struct stats_mailbox *sbox = STATS_CONTEXT(ctx->box);
 
403
        struct stats_user *suser = STATS_USER_CONTEXT(ctx->box->storage->user);
 
404
 
 
405
        stats_transaction_free(suser, strans);
 
406
        sbox->module_ctx.super.transaction_rollback(ctx);
 
407
}
 
408
 
 
409
static bool stats_search_next_nonblock(struct mail_search_context *ctx,
 
410
                                       struct mail **mail_r, bool *tryagain_r)
 
411
{
 
412
        struct stats_mailbox *sbox = STATS_CONTEXT(ctx->transaction->box);
 
413
        struct mail_user *user = ctx->transaction->box->storage->user;
 
414
        struct stats_user *suser = STATS_USER_CONTEXT(user);
 
415
        bool ret;
 
416
 
 
417
        ret = sbox->module_ctx.super.
 
418
                search_next_nonblock(ctx, mail_r, tryagain_r);
 
419
        if (!ret && !*tryagain_r) {
 
420
                /* end of search */
 
421
                return FALSE;
 
422
        }
 
423
 
 
424
        if (*tryagain_r ||
 
425
            ++suser->refresh_check_counter % REFRESH_CHECK_INTERVAL == 0) {
 
426
                /* a) retrying, so this is a long running search.
 
427
                   b) we've returned enough matches */
 
428
                if (time(NULL) != suser->last_session_update)
 
429
                        session_stats_refresh(user);
 
430
        }
 
431
        return ret;
 
432
}
 
433
 
 
434
static void stats_mailbox_allocated(struct mailbox *box)
 
435
{
 
436
        struct mailbox_vfuncs *v = box->vlast;
 
437
        struct stats_mailbox *sbox;
 
438
        struct stats_user *suser = STATS_USER_CONTEXT(box->storage->user);
 
439
 
 
440
        if (suser == NULL)
 
441
                return;
 
442
 
 
443
        sbox = p_new(box->pool, struct stats_mailbox, 1);
 
444
        sbox->module_ctx.super = *v;
 
445
        box->vlast = &sbox->module_ctx.super;
 
446
 
 
447
        v->transaction_begin = stats_transaction_begin;
 
448
        v->transaction_commit = stats_transaction_commit;
 
449
        v->transaction_rollback = stats_transaction_rollback;
 
450
        v->search_next_nonblock = stats_search_next_nonblock;
 
451
        MODULE_CONTEXT_SET(box, stats_storage_module, sbox);
 
452
}
 
453
 
 
454
static void session_stats_refresh_timeout(struct mail_user *user)
 
455
{
 
456
        if (stats_global_user != NULL)
 
457
                stats_add_session(user);
 
458
        session_stats_refresh(user);
 
459
}
 
460
 
 
461
static void stats_io_deactivate(void *context)
 
462
{
 
463
        struct mail_user *user = context;
 
464
        struct stats_user *suser = STATS_USER_CONTEXT(user);
 
465
        unsigned int last_update_secs;
 
466
 
 
467
        if (stats_global_user == NULL)
 
468
                stats_add_session(user);
 
469
 
 
470
        last_update_secs = time(NULL) - suser->last_session_update;
 
471
        if (last_update_secs >= suser->refresh_secs) {
 
472
                if (stats_global_user != NULL)
 
473
                        stats_add_session(user);
 
474
                session_stats_refresh(user);
 
475
        } else if (suser->to_stats_timeout == NULL) {
 
476
                suser->to_stats_timeout =
 
477
                        timeout_add(suser->refresh_secs*1000,
 
478
                                    session_stats_refresh_timeout, user);
 
479
        }
 
480
}
 
481
 
 
482
static void stats_user_deinit(struct mail_user *user)
 
483
{
 
484
        struct stats_user *suser = STATS_USER_CONTEXT(user);
 
485
        struct stats_connection *stats_conn = suser->stats_conn;
 
486
 
 
487
        i_assert(stats_user_count > 0);
 
488
        if (--stats_user_count == 0) {
 
489
                /* we were updating the session lazily. do one final update. */
 
490
                i_assert(stats_global_user == user);
 
491
                stats_add_session(user);
 
492
                stats_global_user = NULL;
 
493
        } else {
 
494
                i_assert(stats_global_user == NULL);
 
495
        }
 
496
 
 
497
        io_loop_context_remove_callbacks(suser->ioloop_ctx,
 
498
                                         stats_io_activate,
 
499
                                         stats_io_deactivate, user);
 
500
        /* send final stats before disconnection */
 
501
        session_stats_refresh(user);
 
502
        stats_connection_disconnect(stats_conn, user);
 
503
 
 
504
        if (suser->to_stats_timeout != NULL)
 
505
                timeout_remove(&suser->to_stats_timeout);
 
506
        suser->module_ctx.super.deinit(user);
 
507
 
 
508
        stats_connection_unref(&stats_conn);
 
509
}
 
510
 
 
511
static void stats_user_created(struct mail_user *user)
 
512
{
 
513
        struct ioloop_context *ioloop_ctx =
 
514
                io_loop_get_current_context(current_ioloop);
 
515
        struct stats_user *suser;
 
516
        struct mail_user_vfuncs *v = user->vlast;
 
517
        const char *path, *str, *error;
 
518
        unsigned int refresh_secs;
 
519
 
 
520
        if (ioloop_ctx == NULL) {
 
521
                /* we're probably running some test program, or at least
 
522
                   mail-storage-service wasn't used to create this user.
 
523
                   disable stats tracking. */
 
524
                return;
 
525
        }
 
526
        if (user->autocreated) {
 
527
                /* lda / shared user. we're not tracking this one. */
 
528
                return;
 
529
        }
 
530
 
 
531
        /* get refresh time */
 
532
        str = mail_user_plugin_getenv(user, "stats_refresh");
 
533
        if (str == NULL)
 
534
                return;
 
535
        if (settings_get_time(str, &refresh_secs, &error) < 0) {
 
536
                i_error("stats: Invalid stats_refresh setting: %s", error);
 
537
                return;
 
538
        }
 
539
        if (refresh_secs == 0)
 
540
                return;
 
541
 
 
542
        if (global_stats_conn == NULL) {
 
543
                path = t_strconcat(user->set->base_dir,
 
544
                                   "/"MAIL_STATS_SOCKET_NAME, NULL);
 
545
                global_stats_conn = stats_connection_create(path);
 
546
        }
 
547
        stats_connection_ref(global_stats_conn);
 
548
 
 
549
        if (stats_user_count == 0) {
 
550
                /* first user connection */
 
551
                stats_global_user = user;
 
552
        } else if (stats_user_count == 1) {
 
553
                /* second user connection. we'll need to start doing
 
554
                   per-io callback tracking now. */
 
555
                stats_add_session(stats_global_user);
 
556
                stats_global_user = NULL;
 
557
        }
 
558
        stats_user_count++;
 
559
 
 
560
        suser = p_new(user->pool, struct stats_user, 1);
 
561
        suser->module_ctx.super = *v;
 
562
        user->vlast = &suser->module_ctx.super;
 
563
        v->deinit = stats_user_deinit;
 
564
 
 
565
        suser->refresh_secs = refresh_secs;
 
566
        str = mail_user_plugin_getenv(user, "stats_track_cmds");
 
567
        if (str != NULL && strcmp(str, "yes") == 0)
 
568
                suser->track_commands = TRUE;
 
569
 
 
570
        suser->stats_conn = global_stats_conn;
 
571
        guid_128_generate(suser->session_guid);
 
572
        suser->last_session_update = time(NULL);
 
573
 
 
574
        suser->ioloop_ctx = ioloop_ctx;
 
575
        io_loop_context_add_callbacks(ioloop_ctx,
 
576
                                      stats_io_activate,
 
577
                                      stats_io_deactivate, user);
 
578
 
 
579
        MODULE_CONTEXT_SET(user, stats_user_module, suser);
 
580
        stats_connection_connect(suser->stats_conn, user);
 
581
}
 
582
 
 
583
static struct mail_storage_hooks stats_mail_storage_hooks = {
 
584
        .mailbox_allocated = stats_mailbox_allocated,
 
585
        .mail_user_created = stats_user_created
 
586
};
 
587
 
 
588
void stats_plugin_init(struct module *module)
 
589
{
 
590
        mail_storage_hooks_add(module, &stats_mail_storage_hooks);
 
591
}
 
592
 
 
593
void stats_plugin_deinit(void)
 
594
{
 
595
        if (global_stats_conn != NULL)
 
596
                stats_connection_unref(&global_stats_conn);
 
597
        mail_storage_hooks_remove(&stats_mail_storage_hooks);
 
598
}