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