mail-stats-fill.c revision 6ef83bcdc4e40d6b387857e5f7d58cd86c71ec50
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce/* Copyright (c) 2011-2017 Dovecot authors, see the included COPYING file */
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce#include "lib.h"
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce#include "time-util.h"
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce#include "stats-plugin.h"
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce#include "mail-stats.h"
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce#include <sys/resource.h>
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce#define PROC_IO_PATH "/proc/self/io"
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorcestatic bool proc_io_disabled = FALSE;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorcestatic int proc_io_fd = -1;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorcestatic int
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorceprocess_io_buffer_parse(const char *buf, struct mail_stats *stats)
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce{
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce const char *const *tmp;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce tmp = t_strsplit(buf, "\n");
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce for (; *tmp != NULL; tmp++) {
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce if (strncmp(*tmp, "rchar: ", 7) == 0) {
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce if (str_to_uint64(*tmp + 7, &stats->read_bytes) < 0)
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce return -1;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce } else if (strncmp(*tmp, "wchar: ", 7) == 0) {
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce if (str_to_uint64(*tmp + 7, &stats->write_bytes) < 0)
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce return -1;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce } else if (strncmp(*tmp, "syscr: ", 7) == 0) {
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce if (str_to_uint32(*tmp + 7, &stats->read_count) < 0)
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce return -1;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce } else if (strncmp(*tmp, "syscw: ", 7) == 0) {
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce if (str_to_uint32(*tmp + 7, &stats->write_count) < 0)
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce return -1;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce }
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce }
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce return 0;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce}
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorcestatic int process_io_open(void)
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce{
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce uid_t uid;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce if (proc_io_fd != -1)
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce return proc_io_fd;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce if (proc_io_disabled)
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce return -1;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce proc_io_fd = open(PROC_IO_PATH, O_RDONLY);
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce if (proc_io_fd == -1 && errno == EACCES) {
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce /* kludge: if we're running with permissions temporarily
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce dropped, get them temporarily back so we can open
4ebab24f65b54720a6672898b76185462015ababPavel Březina /proc/self/io. */
75d66aea7accc842e68c88f085f9053112b20eccPavel Březina uid = geteuid();
c1058e96679c7ed1372825bf5226ce7d28a8e6ffPavel Březina if (seteuid(0) == 0) {
dee7a89098b698e756f63e4041734d7322ad8b1ePavel Březina proc_io_fd = open(PROC_IO_PATH, O_RDONLY);
ab967283b710dfa05d11ee5b30c7ac916486ceecSimo Sorce if (seteuid(uid) < 0) {
c6872e79e8496fd075e20aec0343ade99cca725cSimo Sorce /* oops, this is bad */
c6872e79e8496fd075e20aec0343ade99cca725cSimo Sorce i_fatal("stats: seteuid(%s) failed", dec2str(uid));
c6872e79e8496fd075e20aec0343ade99cca725cSimo Sorce }
7c69221077c780e62f6c536e78675f2dc1c131bcMichal Zidek }
22a21e910fd216ec1468fe769dcc29f1621a52a4Ondrej Kos errno = EACCES;
ab967283b710dfa05d11ee5b30c7ac916486ceecSimo Sorce }
ab967283b710dfa05d11ee5b30c7ac916486ceecSimo Sorce if (proc_io_fd == -1) {
ab967283b710dfa05d11ee5b30c7ac916486ceecSimo Sorce /* ignore access errors too, certain security options can
ab967283b710dfa05d11ee5b30c7ac916486ceecSimo Sorce prevent root access to this file when not owned by root */
233a3c6c48972b177e60d6ef4cecfacd3cf31659Simo Sorce if (errno != ENOENT && errno != EACCES)
c6872e79e8496fd075e20aec0343ade99cca725cSimo Sorce i_error("open(%s) failed: %m", PROC_IO_PATH);
c6872e79e8496fd075e20aec0343ade99cca725cSimo Sorce proc_io_disabled = TRUE;
233a3c6c48972b177e60d6ef4cecfacd3cf31659Simo Sorce return -1;
233a3c6c48972b177e60d6ef4cecfacd3cf31659Simo Sorce }
c9b0071bfcb8eb8c71e40248de46d23aceecc0f3Pavel Reichl return proc_io_fd;
c9b0071bfcb8eb8c71e40248de46d23aceecc0f3Pavel Reichl}
c9b0071bfcb8eb8c71e40248de46d23aceecc0f3Pavel Reichl
dfd71fc92db940b2892cc996911cec03d7b6c52bSimo Sorcestatic void process_read_io_stats(struct mail_stats *stats)
f9961e5f82e0ef474d6492371bfdf9e74e208a99Pavel Březina{
f9961e5f82e0ef474d6492371bfdf9e74e208a99Pavel Březina char buf[1024];
e5f455afbc2d149527bfd08f4e89903a3a8da17aPavel Březina int fd, ret;
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek if ((fd = process_io_open()) == -1)
9cb46bc62f22e0104f1b41a423b014c281ef5fc2Jakub Hrozek return;
7caf7ed4f2eae1ec1c0717b4ee6ce78bdacd5926Jakub Hrozek
dcc6877aa2e2dd63a9dc9c411a9c58feaeb36b9aStephen Gallagher ret = pread(fd, buf, sizeof(buf), 0);
bc30ce9b7d588a17e58012e699986f0d6898b791Pavel Březina if (ret <= 0) {
b5ee224324b0158641d9b110f81d2bc6eddddc13Pavel Reichl if (ret == -1)
2a96981a0ac781d01e5bba473409ed2bdf4cd4e0Jakub Hrozek i_error("read(%s) failed: %m", PROC_IO_PATH);
e81deec535d11912b87954c81a1edd768c1386c9Jakub Hrozek else
4dd38025efda88f123eac672f87d3cda12f050c8Jakub Hrozek i_error("read(%s) returned EOF", PROC_IO_PATH);
4dd38025efda88f123eac672f87d3cda12f050c8Jakub Hrozek } else if (ret == sizeof(buf)) {
0161a3c5637a0c0092bf54c436bb3d6508d7df26Jakub Hrozek /* just shouldn't happen.. */
0161a3c5637a0c0092bf54c436bb3d6508d7df26Jakub Hrozek i_error("%s is larger than expected", PROC_IO_PATH);
10a28f461c25d788ff4dcffefa881e7aa724a25dPavel Březina proc_io_disabled = TRUE;
60cab26b12df9a2153823972cde0c38ca86e01b9Yassir Elley } else {
1319e71fd1680ca4864afe0b1aca2b8c8e4a1ee4Stef Walter buf[ret] = '\0';
0c1d65998907930678da2d091789446f2c344d5dJakub Hrozek T_BEGIN {
a2ea3f5d9ef9f17efbb61e942c2bc6cff7d1ebf2Jakub Hrozek if (process_io_buffer_parse(buf, stats) < 0) {
f3a25949de81f80c136bb073e4a8f504b080c20cJakub Hrozek i_error("Invalid input in file %s",
8394eddba54b5d3e3fda868145e3751247bdbdb2Michal Zidek PROC_IO_PATH);
5a5c5cdeb92f4012fc75fd717bfea06598f68f12Pavel Reichl proc_io_disabled = TRUE;
804df4040eb142f82a44c019c7a55b5ce524583cMichal Zidek }
1243e093fd31c5660adf1bb3dd477d6935a755beJakub Hrozek } T_END;
1243e093fd31c5660adf1bb3dd477d6935a755beJakub Hrozek }
82a958e6592c4a4078e45b7197bbe4751b70f511Pavel Reichl}
979e8d8d6ed444007eeff6be5269e8dc5d2bdf68Pavel Reichl
05d935cc9d04f03522d0bb44598d22d99b085926Jakub Hrozekstatic void
64ea4127f463798410a2c20e0261c6b15f60257fJakub Hrozekuser_trans_stats_get(struct stats_user *suser, struct mail_stats *dest_r)
64ea4127f463798410a2c20e0261c6b15f60257fJakub Hrozek{
a8d887323f83984679a7d9b827a70146656bb7b2Sumit Bose struct stats_transaction_context *strans;
b42bf6c0c01db08208fb81d8295a2909d307284aPavel Reichl
9118a539a5d59f669f551114f880fe91d6bb8741Jakub Hrozek mail_stats_add_transaction(dest_r, &suser->finished_transaction_stats);
b5825c74b6bf7a99ae2172392dbecb51179013a6Jakub Hrozek for (strans = suser->transactions; strans != NULL; strans = strans->next)
19e44537c28f6d5f011cd7ac885c74c1e892605fSimo Sorce mail_stats_add_transaction(dest_r, &strans->trans->stats);
5f7cd30c865046a7ea69944f7e07c85b4c43465aSumit Bose}
c30b7a1931211fdcae0564551a7625cc4f6dee9fJakub Hrozek
e732d23f3ec986a463d757781a334040e03d1f59Jakub Hrozekvoid mail_stats_fill(struct stats_user *suser, struct mail_stats *stats_r)
e732d23f3ec986a463d757781a334040e03d1f59Jakub Hrozek{
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce static bool getrusage_broken = FALSE;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce static struct rusage prev_usage;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce struct rusage usage;
0c16d2eefbc6ac8331078a4cdcecfee817a71bc6Simo Sorce
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce i_zero(stats_r);
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce /* cputime */
7650ded4ffa87fcf7ce5adf00920fecf89cffcf5Michal Zidek if (getrusage(RUSAGE_SELF, &usage) < 0) {
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce if (!getrusage_broken) {
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce i_error("getrusage() failed: %m");
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce getrusage_broken = TRUE;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce }
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce usage = prev_usage;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce } else if (timeval_diff_usecs(&usage.ru_stime, &prev_usage.ru_stime) < 0) {
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce /* This seems to be a Linux bug. */
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce usage.ru_stime = prev_usage.ru_stime;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce }
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce prev_usage = usage;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce stats_r->user_cpu = usage.ru_utime;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce stats_r->sys_cpu = usage.ru_stime;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce stats_r->min_faults = usage.ru_minflt;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce stats_r->maj_faults = usage.ru_majflt;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce stats_r->vol_cs = usage.ru_nvcsw;
8bcabb97d988d1602882a1f036aac2eaf5e09234Simo Sorce 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;
(void)gettimeofday(&stats_r->clock_time, NULL);
process_read_io_stats(stats_r);
user_trans_stats_get(suser, stats_r);
}
void mail_stats_fill_global_deinit(void)
{
i_close_fd(&proc_io_fd);
}