8e4443cf14d4f2ce478c702a1bbb0127fa8f461b
[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
17 using namespace std;
18
19 extern ServerPool *servers;
20
21 StatsThread::StatsThread(const std::string &stats_file, int stats_interval)
22         : stats_file(stats_file),
23           stats_interval(stats_interval)
24 {
25 }
26
27 void StatsThread::do_work()
28 {
29         while (!should_stop) {
30                 int fd;
31                 FILE *fp;
32                 time_t now;
33                 vector<ClientStats> client_stats;
34
35                 // Open a new, temporary file.
36                 char *filename = strdup((stats_file + ".new.XXXXXX").c_str());
37                 fd = mkostemp(filename, O_WRONLY);
38                 if (fd == -1) {
39                         log_perror(filename);
40                         free(filename);
41                         goto sleep;
42                 }
43
44                 fp = fdopen(fd, "w");
45                 if (fp == NULL) {
46                         log_perror("fdopen");
47                         close(fd);
48                         unlink(filename);
49                         free(filename);
50                         goto sleep;
51                 }
52
53                 now = time(NULL);
54                 client_stats = servers->get_client_stats();
55                 for (size_t i = 0; i < client_stats.size(); ++i) {
56                         fprintf(fp, "%s %d %d %s %d %llu %llu %llu\n",
57                                 client_stats[i].remote_addr.c_str(),
58                                 client_stats[i].sock,
59                                 client_stats[i].fwmark,
60                                 client_stats[i].stream_id.c_str(),
61                                 int(now - client_stats[i].connect_time),
62                                 (long long unsigned)(client_stats[i].bytes_sent),
63                                 (long long unsigned)(client_stats[i].bytes_lost),
64                                 (long long unsigned)(client_stats[i].num_loss_events));
65                 }
66                 if (fclose(fp) == EOF) {
67                         log_perror("fclose");
68                         unlink(filename);
69                         free(filename);
70                         goto sleep;
71                 }
72                 
73                 if (rename(filename, stats_file.c_str()) == -1) {
74                         log_perror("rename");
75                         unlink(filename);
76                 }
77                 free(filename);
78
79 sleep:
80                 // Wait until the stop_fd pipe is closed, stats_interval timeout,
81                 // or a spurious signal. (The latter will cause us to write stats
82                 // too often, but that's okay.)
83                 pollfd pfd;
84                 pfd.fd = stop_fd_read;
85                 pfd.events = POLLIN | POLLRDHUP;
86
87                 int nfds = poll(&pfd, 1, stats_interval * 1000);
88                 if (nfds == 0 || (nfds == -1 && errno == EINTR)) {
89                         continue;
90                 }
91                 if (nfds == 1) {
92                         // Should stop.
93                         break;
94                 }
95                 if (nfds == -1) {
96                         log_perror("poll");
97                         usleep(100000);
98                         continue;
99                 }
100         }
101 }