summaryrefslogtreecommitdiffstats
path: root/src/plugins/old-stats/mail-stats-fill.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins/old-stats/mail-stats-fill.c')
-rw-r--r--src/plugins/old-stats/mail-stats-fill.c156
1 files changed, 156 insertions, 0 deletions
diff --git a/src/plugins/old-stats/mail-stats-fill.c b/src/plugins/old-stats/mail-stats-fill.c
new file mode 100644
index 0000000..10d8c39
--- /dev/null
+++ b/src/plugins/old-stats/mail-stats-fill.c
@@ -0,0 +1,156 @@
+/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */
+
+#include "lib.h"
+#include "time-util.h"
+#include "stats-plugin.h"
+#include "mail-stats.h"
+
+#include <sys/resource.h>
+
+#define PROC_IO_PATH "/proc/self/io"
+
+static bool proc_io_disabled = FALSE;
+static int proc_io_fd = -1;
+
+static int
+process_io_buffer_parse(const char *buf, struct mail_stats *stats)
+{
+ const char *const *tmp;
+
+ tmp = t_strsplit(buf, "\n");
+ for (; *tmp != NULL; tmp++) {
+ if (str_begins(*tmp, "rchar: ")) {
+ if (str_to_uint64(*tmp + 7, &stats->read_bytes) < 0)
+ return -1;
+ } else if (str_begins(*tmp, "wchar: ")) {
+ if (str_to_uint64(*tmp + 7, &stats->write_bytes) < 0)
+ return -1;
+ } else if (str_begins(*tmp, "syscr: ")) {
+ if (str_to_uint32(*tmp + 7, &stats->read_count) < 0)
+ return -1;
+ } else if (str_begins(*tmp, "syscw: ")) {
+ if (str_to_uint32(*tmp + 7, &stats->write_count) < 0)
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int process_io_open(void)
+{
+ uid_t uid;
+
+ if (proc_io_fd != -1)
+ return proc_io_fd;
+
+ if (proc_io_disabled)
+ return -1;
+
+ proc_io_fd = open(PROC_IO_PATH, O_RDONLY);
+ if (proc_io_fd == -1 && errno == EACCES) {
+ /* kludge: if we're running with permissions temporarily
+ dropped, get them temporarily back so we can open
+ /proc/self/io. */
+ uid = geteuid();
+ if (seteuid(0) == 0) {
+ proc_io_fd = open(PROC_IO_PATH, O_RDONLY);
+ if (seteuid(uid) < 0) {
+ /* oops, this is bad */
+ i_fatal("stats: seteuid(%s) failed", dec2str(uid));
+ }
+ }
+ errno = EACCES;
+ }
+ if (proc_io_fd == -1) {
+ /* ignore access errors too, certain security options can
+ prevent root access to this file when not owned by root */
+ if (errno != ENOENT && errno != EACCES)
+ i_error("open(%s) failed: %m", PROC_IO_PATH);
+ proc_io_disabled = TRUE;
+ return -1;
+ }
+ return proc_io_fd;
+}
+
+static void process_read_io_stats(struct mail_stats *stats)
+{
+ char buf[1024];
+ int fd, ret;
+
+ if ((fd = process_io_open()) == -1)
+ return;
+
+ ret = pread(fd, buf, sizeof(buf), 0);
+ if (ret <= 0) {
+ if (ret == -1)
+ i_error("read(%s) failed: %m", PROC_IO_PATH);
+ else
+ i_error("read(%s) returned EOF", PROC_IO_PATH);
+ } else if (ret == sizeof(buf)) {
+ /* just shouldn't happen.. */
+ i_error("%s is larger than expected", PROC_IO_PATH);
+ proc_io_disabled = TRUE;
+ } else {
+ buf[ret] = '\0';
+ T_BEGIN {
+ if (process_io_buffer_parse(buf, stats) < 0) {
+ i_error("Invalid input in file %s",
+ PROC_IO_PATH);
+ proc_io_disabled = TRUE;
+ }
+ } T_END;
+ }
+}
+
+static void
+user_trans_stats_get(struct stats_user *suser, struct mail_stats *dest_r)
+{
+ struct stats_transaction_context *strans;
+
+ mail_stats_add_transaction(dest_r, &suser->finished_transaction_stats);
+ for (strans = suser->transactions; strans != NULL; strans = strans->next)
+ mail_stats_add_transaction(dest_r, &strans->trans->stats);
+}
+
+void mail_stats_fill(struct stats_user *suser, struct mail_stats *stats_r)
+{
+ static bool getrusage_broken = FALSE;
+ static struct rusage prev_usage;
+ struct rusage usage;
+
+ i_zero(stats_r);
+ /* cputime */
+ if (getrusage(RUSAGE_SELF, &usage) < 0) {
+ if (!getrusage_broken) {
+ i_error("getrusage() failed: %m");
+ getrusage_broken = TRUE;
+ }
+ usage = prev_usage;
+ } else if (timeval_diff_usecs(&usage.ru_stime, &prev_usage.ru_stime) < 0) {
+ /* This seems to be a Linux bug. */
+ usage.ru_stime = prev_usage.ru_stime;
+ }
+ prev_usage = usage;
+
+ stats_r->user_cpu = usage.ru_utime;
+ stats_r->sys_cpu = usage.ru_stime;
+ stats_r->min_faults = usage.ru_minflt;
+ stats_r->maj_faults = usage.ru_majflt;
+ stats_r->vol_cs = usage.ru_nvcsw;
+ stats_r->invol_cs = usage.ru_nivcsw;
+ stats_r->disk_input = (unsigned long long)usage.ru_inblock * 512ULL;
+ stats_r->disk_output = (unsigned long long)usage.ru_oublock * 512ULL;
+ i_gettimeofday(&stats_r->clock_time);
+ process_read_io_stats(stats_r);
+ user_trans_stats_get(suser, stats_r);
+}
+
+void mail_stats_global_preinit(void)
+{
+ (void)process_io_open();
+}
+
+void mail_stats_fill_global_deinit(void)
+{
+ i_close_fd(&proc_io_fd);
+}