1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143
|
#include <fcntl.h>
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <unistd.h>
#include <vector>
#include "client.h"
#include "log.h"
#include "serverpool.h"
#include "stats.h"
#include "util.h"
using namespace std;
extern ServerPool *servers;
StatsThread::StatsThread(const string &stats_file, int stats_interval)
: stats_file(stats_file),
stats_interval(stats_interval)
{
}
void StatsThread::do_work()
{
while (!should_stop()) {
int fd;
char *filename;
FILE *fp;
timespec now;
vector<ClientStats> client_stats;
vector<HLSZombie> hls_zombies;
unordered_map<string, HLSZombie> remaining_hls_zombies;
if (clock_gettime(CLOCK_MONOTONIC_COARSE, &now) == -1) {
log_perror("clock_gettime(CLOCK_MONOTONIC_COARSE)");
goto sleep;
}
// Open a new, temporary file.
filename = strdup((stats_file + ".new.XXXXXX").c_str());
fd = mkostemp(filename, O_WRONLY | O_CLOEXEC);
if (fd == -1) {
log_perror(filename);
free(filename);
goto sleep;
}
fp = fdopen(fd, "w");
if (fp == nullptr) {
log_perror("fdopen");
safe_close(fd);
if (unlink(filename) == -1) {
log_perror(filename);
}
free(filename);
goto sleep;
}
// Get all the HLS zombies and combine them into one map (we resolve conflicts
// by having an arbitrary element win; in practice, that means the lowest
// server ID).
for (HLSZombie &zombie : servers->get_hls_zombies()) {
const string remote_addr = zombie.remote_addr;
remaining_hls_zombies[move(remote_addr)] = move(zombie);
}
// Remove all zombies whose ID match an already ongoing request.
// (Normally, this is cleared out already when it starts,
// but the request could happen on a different server from the zombie,
// or the zombie could be deserialized.)
for (const ClientStats &stats : servers->get_client_stats()) {
if (stats.url != "-") {
remaining_hls_zombies.erase(stats.hls_zombie_key);
}
}
for (const ClientStats &stats : servers->get_client_stats()) {
string url = stats.url;
if (url == "-") {
// No download going on currently; could it be waiting for more HLS fragments?
auto it = remaining_hls_zombies.find(stats.remote_addr);
if (it != remaining_hls_zombies.end()) {
url = it->second.url;
remaining_hls_zombies.erase(it);
}
}
fprintf(fp, "%s %d %d %s %d %llu %llu %llu \"%s\" \"%s\"\n",
stats.remote_addr.c_str(),
stats.sock,
0, // Used to be fwmark.
url.c_str(),
int(now.tv_sec - stats.connect_time.tv_sec), // Rather coarse.
(long long unsigned)(stats.bytes_sent),
(long long unsigned)(stats.bytes_lost),
(long long unsigned)(stats.num_loss_events),
stats.referer.c_str(),
stats.user_agent.c_str());
}
for (const auto &url_and_zombie : remaining_hls_zombies) {
const HLSZombie &zombie = url_and_zombie.second;
fprintf(fp, "%s %d %d %s %d %llu %llu %llu \"%s\" \"%s\"\n",
zombie.remote_addr.c_str(),
0, // Fake socket. (The Munin script doesn't like negative numbers.)
0, // Used to be fwmark.
zombie.url.c_str(),
0,
0ULL,
0ULL,
0ULL,
zombie.referer.c_str(),
zombie.user_agent.c_str());
}
if (fclose(fp) == EOF) {
log_perror("fclose");
if (unlink(filename) == -1) {
log_perror(filename);
}
free(filename);
goto sleep;
}
if (rename(filename, stats_file.c_str()) == -1) {
log_perror("rename");
if (unlink(filename) == -1) {
log_perror(filename);
}
}
free(filename);
sleep:
// Wait until we are asked to quit, stats_interval timeout,
// or a spurious signal. (The latter will cause us to write stats
// too often, but that's okay.)
timespec timeout_ts;
timeout_ts.tv_sec = stats_interval;
timeout_ts.tv_nsec = 0;
wait_for_wakeup(&timeout_ts);
}
}
|