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