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