bcb4e51a409d94ae670de96afb8483a4f7855294Stephan Bosch/* Copyright (c) 2011-2018 Dovecot authors, see the included COPYING file */
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen#include "lib.h"
fdcb22a688c4676face8db865736b217d9c07d19Timo Sirainen#include "time-util.h"
892f02d4ab8f764f86015009aaf7437349398286Timo Sirainen#include "restrict-access.h"
892f02d4ab8f764f86015009aaf7437349398286Timo Sirainen#include "stats-plugin.h"
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen#include "mail-stats.h"
4a7e04d325db0c03f575f98f045246fceb0de279Timo Sirainen
e156adefc1260d31a145df2f5e9b3c82050d4163Timo Sirainen#include <sys/resource.h>
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen
6d3374b2113005515da30bca67e063d3fbac6fdaTimo Sirainen#define PROC_IO_PATH "/proc/self/io"
d2e0d8ee40bff599057a68c5b3e03604e9a22cccTimo Sirainen
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainenstatic bool proc_io_disabled = FALSE;
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainenstatic int proc_io_fd = -1;
892f02d4ab8f764f86015009aaf7437349398286Timo Sirainen
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainenstatic int
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainenprocess_io_buffer_parse(const char *buf, struct mail_stats *stats)
933a8cc7033e8a9bb7a1c7630fbc786b914f1510Timo Sirainen{
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen const char *const *tmp;
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen
d5960ce1c0adda5c9e259bc429123ebc29c60baeTimo Sirainen tmp = t_strsplit(buf, "\n");
d5960ce1c0adda5c9e259bc429123ebc29c60baeTimo Sirainen for (; *tmp != NULL; tmp++) {
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen if (strncmp(*tmp, "rchar: ", 7) == 0) {
62461eb609e1d852e027cf4e07d30d51288678a2Aki Tuomi if (str_to_uint64(*tmp + 7, &stats->read_bytes) < 0)
6ca8d19dd94eb79ba65d2c5b686c37225ab33adcTimo Sirainen return -1;
6ca8d19dd94eb79ba65d2c5b686c37225ab33adcTimo Sirainen } else if (strncmp(*tmp, "wchar: ", 7) == 0) {
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen if (str_to_uint64(*tmp + 7, &stats->write_bytes) < 0)
22bcfbefeb32d8b787a3449139cdba550c4cf1b8Timo Sirainen return -1;
22bcfbefeb32d8b787a3449139cdba550c4cf1b8Timo Sirainen } else if (strncmp(*tmp, "syscr: ", 7) == 0) {
22bcfbefeb32d8b787a3449139cdba550c4cf1b8Timo Sirainen if (str_to_uint32(*tmp + 7, &stats->read_count) < 0)
22bcfbefeb32d8b787a3449139cdba550c4cf1b8Timo Sirainen return -1;
ee19b4573879d83353c2e32e5ac2f5480dec55eaTimo Sirainen } else if (strncmp(*tmp, "syscw: ", 7) == 0) {
ee19b4573879d83353c2e32e5ac2f5480dec55eaTimo Sirainen if (str_to_uint32(*tmp + 7, &stats->write_count) < 0)
ee19b4573879d83353c2e32e5ac2f5480dec55eaTimo Sirainen return -1;
bbebb157d18f7b98b323260bdff96d0defa4d2efTimo Sirainen }
bbebb157d18f7b98b323260bdff96d0defa4d2efTimo Sirainen }
bbebb157d18f7b98b323260bdff96d0defa4d2efTimo Sirainen return 0;
bbebb157d18f7b98b323260bdff96d0defa4d2efTimo Sirainen}
ee19b4573879d83353c2e32e5ac2f5480dec55eaTimo Sirainen
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainenstatic int process_io_open(void)
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen{
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen uid_t uid;
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen if (proc_io_fd != -1)
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen return proc_io_fd;
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen if (proc_io_disabled)
e75ea33b31009cd6f6917fb7fdab737a2112d775Timo Sirainen return -1;
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen
cfbe8dbf68fd1c2a98444cdfd0145e002965dd50Timo Sirainen bool dumpable = restrict_access_get_dumpable();
433a716f8a27cd7701de7c187e3882042535a424Timo Sirainen if (!dumpable)
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen restrict_access_set_dumpable(TRUE);
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen proc_io_fd = open(PROC_IO_PATH, O_RDONLY);
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen if (proc_io_fd == -1 && errno == EACCES) {
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen /* kludge: if we're running with permissions temporarily
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen dropped, get them temporarily back so we can open
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen /proc/self/io. */
8fb31d963d484e2b9e1def6756afcc22c18c0310Timo Sirainen uid = geteuid();
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen if (seteuid(0) == 0) {
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen proc_io_fd = open(PROC_IO_PATH, O_RDONLY);
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen if (seteuid(uid) < 0) {
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen /* oops, this is bad */
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen i_fatal("stats: seteuid(%s) failed", dec2str(uid));
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen }
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen }
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen errno = EACCES;
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen }
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen if (!dumpable)
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen restrict_access_set_dumpable(FALSE);
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen if (proc_io_fd == -1) {
6ca8d19dd94eb79ba65d2c5b686c37225ab33adcTimo Sirainen /* ignore access errors too, certain security options can
73a3d209062ede6bda2db328808e36390faf0a72Timo Sirainen prevent root access to this file when not owned by root */
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen if (errno != ENOENT && errno != EACCES)
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen i_error("open(%s) failed: %m", PROC_IO_PATH);
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen proc_io_disabled = TRUE;
0dffa25d211be541ee3c953b23566a1a990789dfTimo Sirainen return -1;
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen }
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen return proc_io_fd;
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen}
f0d94b1f11229d1fd0535ac684c74960baf49b1eTimo Sirainen
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainenstatic void process_read_io_stats(struct mail_stats *stats)
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen{
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen char buf[1024];
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen int fd, ret;
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen if ((fd = process_io_open()) == -1)
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen return;
f0d94b1f11229d1fd0535ac684c74960baf49b1eTimo Sirainen
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen ret = pread(fd, buf, sizeof(buf), 0);
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen if (ret <= 0) {
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen if (ret == -1)
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen i_error("read(%s) failed: %m", PROC_IO_PATH);
13c6532dc104d23061e6901783ceb1ff8872c206Timo Sirainen else
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen i_error("read(%s) returned EOF", PROC_IO_PATH);
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen } else if (ret == sizeof(buf)) {
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen /* just shouldn't happen.. */
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen i_error("%s is larger than expected", PROC_IO_PATH);
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen proc_io_disabled = TRUE;
dd45ee57c3e6b19ff67b4643f52b153535cabe0cTimo Sirainen } else {
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen buf[ret] = '\0';
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen T_BEGIN {
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen if (process_io_buffer_parse(buf, stats) < 0) {
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen i_error("Invalid input in file %s",
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen PROC_IO_PATH);
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen proc_io_disabled = TRUE;
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen }
4307c886579381dbb1897ea1388ae6978c96f560Timo Sirainen } T_END;
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen }
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen}
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainenstatic void
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainenuser_trans_stats_get(struct stats_user *suser, struct mail_stats *dest_r)
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen{
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen struct stats_transaction_context *strans;
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen mail_stats_add_transaction(dest_r, &suser->finished_transaction_stats);
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen for (strans = suser->transactions; strans != NULL; strans = strans->next)
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen mail_stats_add_transaction(dest_r, &strans->trans->stats);
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen}
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainenvoid mail_stats_fill(struct stats_user *suser, struct mail_stats *stats_r)
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen{
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen static bool getrusage_broken = FALSE;
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen static struct rusage prev_usage;
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen struct rusage usage;
9261dbf0675204898c6557591c7aa376e23a52b2Timo Sirainen
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen i_zero(stats_r);
dd45ee57c3e6b19ff67b4643f52b153535cabe0cTimo Sirainen /* cputime */
dd45ee57c3e6b19ff67b4643f52b153535cabe0cTimo Sirainen if (getrusage(RUSAGE_SELF, &usage) < 0) {
dd45ee57c3e6b19ff67b4643f52b153535cabe0cTimo Sirainen if (!getrusage_broken) {
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen i_error("getrusage() failed: %m");
48f78a48f2e1cf299026544444666471ae16ad97Timo Sirainen getrusage_broken = TRUE;
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen }
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen usage = prev_usage;
8fb31d963d484e2b9e1def6756afcc22c18c0310Timo Sirainen } else if (timeval_diff_usecs(&usage.ru_stime, &prev_usage.ru_stime) < 0) {
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen /* This seems to be a Linux bug. */
8fb31d963d484e2b9e1def6756afcc22c18c0310Timo Sirainen usage.ru_stime = prev_usage.ru_stime;
8fb31d963d484e2b9e1def6756afcc22c18c0310Timo Sirainen }
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen prev_usage = usage;
8fb31d963d484e2b9e1def6756afcc22c18c0310Timo Sirainen
0fcd79110ad9a7a799d986cf7e64af453369f2c2Timo Sirainen stats_r->user_cpu = usage.ru_utime;
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen stats_r->sys_cpu = usage.ru_stime;
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen stats_r->min_faults = usage.ru_minflt;
8fb31d963d484e2b9e1def6756afcc22c18c0310Timo Sirainen stats_r->maj_faults = usage.ru_majflt;
8fb31d963d484e2b9e1def6756afcc22c18c0310Timo Sirainen stats_r->vol_cs = usage.ru_nvcsw;
8fb31d963d484e2b9e1def6756afcc22c18c0310Timo Sirainen stats_r->invol_cs = usage.ru_nivcsw;
8fb31d963d484e2b9e1def6756afcc22c18c0310Timo Sirainen stats_r->disk_input = (unsigned long long)usage.ru_inblock * 512ULL;
8fb31d963d484e2b9e1def6756afcc22c18c0310Timo Sirainen stats_r->disk_output = (unsigned long long)usage.ru_oublock * 512ULL;
8fb31d963d484e2b9e1def6756afcc22c18c0310Timo Sirainen (void)gettimeofday(&stats_r->clock_time, NULL);
8fb31d963d484e2b9e1def6756afcc22c18c0310Timo Sirainen process_read_io_stats(stats_r);
0fcd79110ad9a7a799d986cf7e64af453369f2c2Timo Sirainen user_trans_stats_get(suser, stats_r);
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen}
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainenvoid mail_stats_fill_global_deinit(void)
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen{
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen i_close_fd(&proc_io_fd);
8fb31d963d484e2b9e1def6756afcc22c18c0310Timo Sirainen}
8ecbb74bc4c7f6f6145da3525941d1d0e20e67f1Timo Sirainen