1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145
|
/* darkstat 3
* copyright (c) 2007-2014 Emil Mikulic.
*
* daylog.c: daily usage log
*
* You may use, modify and redistribute this file under the terms of the
* GNU General Public License version 2. (see COPYING.GPL)
*/
#define _GNU_SOURCE 1 /* for O_NOFOLLOW on Linux */
#include "cdefs.h"
#include "err.h"
#include "daylog.h"
#include "str.h"
#include "now.h"
#include <assert.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
static const char *daylog_fn = NULL;
static time_t today_real, tomorrow_real;
static uint64_t bytes_in, bytes_out, pkts_in, pkts_out;
#define DAYLOG_DATE_LEN 26 /* strlen("1900-01-01 00:00:00 +1234") + 1 */
static char datebuf[DAYLOG_DATE_LEN];
static char *fmt_date(time_t when) {
if (strftime(datebuf,
DAYLOG_DATE_LEN,
"%Y-%m-%d %H:%M:%S %z",
localtime(&when)) == 0)
errx(1, "strftime() failed in fmt_date()");
return datebuf;
}
/* Given some time today, find the first second of tomorrow. */
static time_t tomorrow(time_t t_before) {
time_t t_after;
struct tm tm, *lt;
lt = localtime(&t_before);
memcpy(&tm, lt, sizeof(tm));
tm.tm_sec = 0;
tm.tm_min = 0;
tm.tm_hour = 0;
tm.tm_mday = lt->tm_mday + 1; /* tomorrow */
t_after = mktime(&tm);
assert(t_after > t_before);
return t_after;
}
/* Warns on error. */
static void daylog_write(const char *format, ...) _printflike_(1, 2);
static void daylog_write(const char *format, ...) {
int fd;
ssize_t wr;
va_list va;
struct str *buf;
assert(daylog_fn != NULL);
fd = open(daylog_fn, O_WRONLY | O_APPEND | O_CREAT | O_NOFOLLOW, 0600);
if (fd == -1) {
warn("daylog_write: couldn't open '%s' for append", daylog_fn);
return;
}
buf = str_make();
va_start(va, format);
str_vappendf(buf, format, va);
va_end(va);
wr = str_write(buf, fd);
if (wr == -1)
warn("daylog_write: couldn't write to '%s'", daylog_fn);
else if (wr != (ssize_t)str_len(buf))
warnx("daylog_write: truncated write to '%s': wrote %d of %d bytes",
daylog_fn,
(int)wr,
(int)str_len(buf));
close(fd);
str_free(buf);
}
static void daylog_emit(void) {
daylog_write("%s|%qu|%qu|%qu|%qu|%qu\n",
fmt_date(today_real),
(qu)today_real,
(qu)bytes_in,
(qu)bytes_out,
(qu)pkts_in,
(qu)pkts_out);
}
void daylog_init(const char *filename) {
daylog_fn = filename;
today_real = now_real();
tomorrow_real = tomorrow(today_real);
verbosef("today is %llu, tomorrow is %llu",
(llu)today_real,
(llu)tomorrow_real);
bytes_in = bytes_out = pkts_in = pkts_out = 0;
daylog_write("# logging started at %s (%qu)\n",
fmt_date(today_real), (qu)today_real);
}
void daylog_free(void) {
today_real = now_real();
daylog_emit(); /* Emit what's currently accumulated before we exit. */
daylog_write("# logging stopped at %s (%qu)\n",
fmt_date(today_real), (qu)today_real);
}
void daylog_acct(uint64_t amount, enum graph_dir dir) {
if (daylog_fn == NULL)
return; /* daylogging disabled */
/* Check if we need to update the log. */
if (now_real() >= tomorrow_real) {
daylog_emit();
today_real = now_real();
tomorrow_real = tomorrow(today_real);
bytes_in = bytes_out = pkts_in = pkts_out = 0;
verbosef("updated daylog, tomorrow = %llu", (llu)tomorrow_real);
}
/* Accounting. */
if (dir == GRAPH_IN) {
bytes_in += amount;
pkts_in++;
} else {
assert(dir == GRAPH_OUT);
bytes_out += amount;
pkts_out++;
}
}
/* vim:set ts=3 sw=3 tw=78 et: */
|