]> git.sesse.net Git - cubemap/blob - stats.cpp
Flush after writing pending TLS data.
[cubemap] / stats.cpp
1 #include <fcntl.h>
2 #include <stddef.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <string.h>
6 #include <time.h>
7 #include <unistd.h>
8 #include <vector>
9
10 #include "client.h"
11 #include "log.h"
12 #include "serverpool.h"
13 #include "stats.h"
14 #include "util.h"
15
16 using namespace std;
17
18 extern ServerPool *servers;
19
20 StatsThread::StatsThread(const string &stats_file, int stats_interval)
21         : stats_file(stats_file),
22           stats_interval(stats_interval)
23 {
24 }
25
26 void StatsThread::do_work()
27 {
28         while (!should_stop()) {
29                 int fd;
30                 char *filename;
31                 FILE *fp;
32                 timespec now;
33                 vector<ClientStats> client_stats;
34
35                 if (clock_gettime(CLOCK_MONOTONIC_COARSE, &now) == -1) {
36                         log_perror("clock_gettime(CLOCK_MONOTONIC_COARSE)");
37                         goto sleep;
38                 }
39
40                 // Open a new, temporary file.
41                 filename = strdup((stats_file + ".new.XXXXXX").c_str());
42                 fd = mkostemp(filename, O_WRONLY);
43                 if (fd == -1) {
44                         log_perror(filename);
45                         free(filename);
46                         goto sleep;
47                 }
48
49                 fp = fdopen(fd, "w");
50                 if (fp == nullptr) {
51                         log_perror("fdopen");
52                         safe_close(fd);
53                         if (unlink(filename) == -1) {
54                                 log_perror(filename);
55                         }
56                         free(filename);
57                         goto sleep;
58                 }
59
60                 client_stats = servers->get_client_stats();
61                 for (size_t i = 0; i < client_stats.size(); ++i) {
62                         fprintf(fp, "%s %d %d %s %d %llu %llu %llu \"%s\" \"%s\"\n",
63                                 client_stats[i].remote_addr.c_str(),
64                                 client_stats[i].sock,
65                                 0,  // Used to be fwmark.
66                                 client_stats[i].url.c_str(),
67                                 int(now.tv_sec - client_stats[i].connect_time.tv_sec),  // Rather coarse.
68                                 (long long unsigned)(client_stats[i].bytes_sent),
69                                 (long long unsigned)(client_stats[i].bytes_lost),
70                                 (long long unsigned)(client_stats[i].num_loss_events),
71                                 client_stats[i].referer.c_str(),
72                                 client_stats[i].user_agent.c_str());
73                 }
74                 if (fclose(fp) == EOF) {
75                         log_perror("fclose");
76                         if (unlink(filename) == -1) {
77                                 log_perror(filename);
78                         }
79                         free(filename);
80                         goto sleep;
81                 }
82                 
83                 if (rename(filename, stats_file.c_str()) == -1) {
84                         log_perror("rename");
85                         if (unlink(filename) == -1) {
86                                 log_perror(filename);
87                         }
88                 }
89                 free(filename);
90
91 sleep:
92                 // Wait until we are asked to quit, stats_interval timeout,
93                 // or a spurious signal. (The latter will cause us to write stats
94                 // too often, but that's okay.)
95                 timespec timeout_ts;
96                 timeout_ts.tv_sec = stats_interval;
97                 timeout_ts.tv_nsec = 0;
98                 wait_for_wakeup(&timeout_ts);
99         }
100 }