Expose real tracker state to web UI
This commit is contained in:
parent
6be239943a
commit
6ec5b19a73
4 changed files with 249 additions and 20 deletions
|
|
@ -256,6 +256,28 @@ static json_t *torrent_json(torrent_task *task) {
|
|||
}
|
||||
json_object_set_new(result, "peer_list", peer_list);
|
||||
}
|
||||
json_t *trackers = json_array();
|
||||
if (trackers) {
|
||||
uint32_t tracker_count = task->stats.tracker_count;
|
||||
if (tracker_count > NAUT_SWARM_MAX_TRACKER_STATS)
|
||||
tracker_count = NAUT_SWARM_MAX_TRACKER_STATS;
|
||||
for (uint32_t i = 0; i < tracker_count; i++) {
|
||||
const naut_swarm_tracker_stats *tracker =
|
||||
&task->stats.tracker_stats[i];
|
||||
json_t *item = json_pack(
|
||||
"{s:s,s:i,s:s,s:i,s:i,s:i,s:i,s:s}",
|
||||
"url", tracker->url,
|
||||
"tier", tracker->tier,
|
||||
"status", tracker->status,
|
||||
"seeds", tracker->seeds,
|
||||
"peers", tracker->peers,
|
||||
"leeches", tracker->leeches,
|
||||
"downloaded", tracker->downloaded,
|
||||
"message", tracker->message);
|
||||
if (item) json_array_append_new(trackers, item);
|
||||
}
|
||||
json_object_set_new(result, "trackers", trackers);
|
||||
}
|
||||
json_object_set_new(result, "elapsed_seconds",
|
||||
json_real(task->stats.elapsed_seconds));
|
||||
json_object_set_new(result, "pending_moves",
|
||||
|
|
|
|||
|
|
@ -41,6 +41,10 @@
|
|||
#define EXT_RESERVED 0x0000000000100000ULL
|
||||
#define DEFAULT_TARGET_PEERS 80
|
||||
#define MAX_TARGET_PEERS 512
|
||||
#define TRACKER_DEFAULT_INTERVAL 1800.0
|
||||
#define TRACKER_MIN_INTERVAL 60.0
|
||||
#define TRACKER_FAILURE_RETRY_INTERVAL 300.0
|
||||
#define DHT_REFRESH_INTERVAL 300.0
|
||||
|
||||
typedef struct {
|
||||
uint32_t piece, begin, length;
|
||||
|
|
@ -85,6 +89,13 @@ static uint32_t target_peer_count(void) {
|
|||
return (uint32_t)NAUT_MIN(value, MAX_TARGET_PEERS);
|
||||
}
|
||||
|
||||
static double tracker_delay_seconds(int32_t interval) {
|
||||
if (interval <= 0) return TRACKER_DEFAULT_INTERVAL;
|
||||
if (interval < (int32_t)TRACKER_MIN_INTERVAL)
|
||||
return TRACKER_MIN_INTERVAL;
|
||||
return (double)interval;
|
||||
}
|
||||
|
||||
static void random_bytes(uint8_t *output, size_t length) {
|
||||
int fd = open("/dev/urandom", O_RDONLY);
|
||||
size_t offset = 0;
|
||||
|
|
@ -225,12 +236,63 @@ static void snapshot_peer_stats(naut_swarm_stats *stats,
|
|||
}
|
||||
}
|
||||
|
||||
static uint32_t init_tracker_stats(char *const *trackers,
|
||||
const uint32_t *tracker_tiers,
|
||||
size_t num_trackers,
|
||||
naut_swarm_tracker_stats *stats,
|
||||
uint32_t capacity) {
|
||||
uint32_t count = 0;
|
||||
if (!stats) return 0;
|
||||
for (size_t i = 0; i < num_trackers && count < capacity; i++) {
|
||||
naut_swarm_tracker_stats *out = &stats[count++];
|
||||
memset(out, 0, sizeof(*out));
|
||||
snprintf(out->url, sizeof out->url, "%s", trackers[i]);
|
||||
out->tier = tracker_tiers ? (int32_t)tracker_tiers[i] : (int32_t)i;
|
||||
snprintf(out->status, sizeof out->status, "not contacted");
|
||||
out->seeds = -1;
|
||||
out->peers = -1;
|
||||
out->leeches = -1;
|
||||
out->downloaded = -1;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
static naut_swarm_tracker_stats *tracker_stat_for(
|
||||
naut_swarm_tracker_stats *stats, uint32_t count, const char *url) {
|
||||
if (!stats || !url) return NULL;
|
||||
for (uint32_t i = 0; i < count; i++)
|
||||
if (strcmp(stats[i].url, url) == 0) return &stats[i];
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void tracker_set_status(naut_swarm_tracker_stats *tracker,
|
||||
const char *status, const char *message) {
|
||||
if (!tracker) return;
|
||||
snprintf(tracker->status, sizeof tracker->status, "%s",
|
||||
status ? status : "");
|
||||
snprintf(tracker->message, sizeof tracker->message, "%s",
|
||||
message ? message : "");
|
||||
}
|
||||
|
||||
static void snapshot_tracker_stats(naut_swarm_stats *stats,
|
||||
const naut_swarm_tracker_stats *trackers,
|
||||
uint32_t tracker_count) {
|
||||
if (!stats || !trackers) return;
|
||||
if (tracker_count > NAUT_SWARM_MAX_TRACKER_STATS)
|
||||
tracker_count = NAUT_SWARM_MAX_TRACKER_STATS;
|
||||
stats->tracker_count = tracker_count;
|
||||
for (uint32_t i = 0; i < tracker_count; i++)
|
||||
stats->tracker_stats[i] = trackers[i];
|
||||
}
|
||||
|
||||
static void report_progress(const naut_swarm_config *config,
|
||||
const naut_download *download,
|
||||
const naut_metainfo *metainfo,
|
||||
uint32_t peers_total, uint32_t peers_connecting,
|
||||
uint32_t peers_active, uint32_t peers_failed,
|
||||
peer_t *peers, int npeers,
|
||||
const naut_swarm_tracker_stats *trackers,
|
||||
uint32_t tracker_count,
|
||||
double started_at) {
|
||||
if (!config->on_progress) return;
|
||||
naut_swarm_stats stats = {
|
||||
|
|
@ -245,6 +307,7 @@ static void report_progress(const naut_swarm_config *config,
|
|||
.elapsed_seconds = now() - started_at,
|
||||
};
|
||||
snapshot_peer_stats(&stats, peers, npeers, metainfo->num_pieces);
|
||||
snapshot_tracker_stats(&stats, trackers, tracker_count);
|
||||
if (download) {
|
||||
stats.piece_state_count = (uint32_t)naut_download_piece_states(
|
||||
download, stats.piece_states, NAUT_SWARM_MAX_PIECE_STATS);
|
||||
|
|
@ -340,16 +403,24 @@ static bool discover_trackers(const uint8_t info_hash[20], uint64_t total_length
|
|||
char *const *trackers, size_t num_trackers,
|
||||
const uint32_t *tracker_tiers,
|
||||
const uint8_t peerid[20],
|
||||
endpoint_t **eps, size_t *neps, size_t *cap) {
|
||||
uint64_t downloaded, uint64_t left,
|
||||
naut_tracker_event event,
|
||||
endpoint_t **eps, size_t *neps, size_t *cap,
|
||||
int32_t *announce_interval,
|
||||
naut_swarm_tracker_stats *tracker_stats,
|
||||
uint32_t tracker_count) {
|
||||
naut_announce_req req;
|
||||
memset(&req, 0, sizeof req);
|
||||
memcpy(req.info_hash, info_hash, sizeof req.info_hash);
|
||||
memcpy(req.peer_id, peerid, sizeof req.peer_id);
|
||||
req.port = 6881;
|
||||
req.downloaded = downloaded;
|
||||
req.left = total_length;
|
||||
req.event = NAUT_TEV_STARTED;
|
||||
if (left <= total_length) req.left = left;
|
||||
req.event = event;
|
||||
req.numwant = 100;
|
||||
memcpy(&req.key, peerid + 8, sizeof req.key);
|
||||
if (announce_interval) *announce_interval = 0;
|
||||
|
||||
size_t tier_start = 0;
|
||||
while (tier_start < num_trackers) {
|
||||
|
|
@ -370,6 +441,10 @@ static bool discover_trackers(const uint8_t info_hash[20], uint64_t total_length
|
|||
size_t i = tier_start + (first + n) % tier_count;
|
||||
const char *tracker = trackers[i];
|
||||
naut_tracker_response response;
|
||||
memset(&response, 0, sizeof response);
|
||||
response.seeders = response.leechers = -1;
|
||||
naut_swarm_tracker_stats *tracker_stat =
|
||||
tracker_stat_for(tracker_stats, tracker_count, tracker);
|
||||
naut_err e = NAUT_ERR_INVAL;
|
||||
if (strncmp(tracker, "http://", 7) == 0) {
|
||||
char url[4096];
|
||||
|
|
@ -382,14 +457,30 @@ static bool discover_trackers(const uint8_t info_hash[20], uint64_t total_length
|
|||
if (parse_udp_tracker(tracker, host, sizeof host, &port))
|
||||
e = naut_tracker_announce_udp(host, port, &req, &response);
|
||||
} else {
|
||||
tracker_set_status(tracker_stat, "unsupported",
|
||||
"unsupported tracker scheme");
|
||||
NAUT_WARN("tracker scheme unsupported: %s", tracker);
|
||||
continue;
|
||||
}
|
||||
if (e != NAUT_OK) {
|
||||
tracker_set_status(tracker_stat, "error",
|
||||
response.failure
|
||||
? response.failure : "announce failed");
|
||||
NAUT_WARN("tracker announce failed: %s", tracker);
|
||||
naut_tracker_response_free(&response);
|
||||
continue;
|
||||
}
|
||||
tier_succeeded = true;
|
||||
if (announce_interval && response.interval > 0)
|
||||
*announce_interval = response.interval;
|
||||
if (tracker_stat) {
|
||||
tracker_set_status(tracker_stat, "working", "");
|
||||
tracker_stat->seeds = response.seeders;
|
||||
tracker_stat->leeches = response.leechers;
|
||||
tracker_stat->peers = response.num_peers > INT32_MAX
|
||||
? INT32_MAX : (int32_t)response.num_peers;
|
||||
tracker_stat->downloaded = -1;
|
||||
}
|
||||
NAUT_INFO("tracker %s returned %zu peers",
|
||||
tracker, response.num_peers);
|
||||
for (size_t p = 0; p < response.num_peers; p++) {
|
||||
|
|
@ -934,6 +1025,9 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
|||
endpoint_t *endpoints = NULL;
|
||||
size_t neps = 0, epcap = 0;
|
||||
uint32_t target_peers = target_peer_count();
|
||||
int32_t tracker_interval = 0;
|
||||
naut_swarm_tracker_stats tracker_stats[NAUT_SWARM_MAX_TRACKER_STATS];
|
||||
uint32_t tracker_count = 0;
|
||||
if (config->num_peers > 0) {
|
||||
for (size_t i = 0; i < config->num_peers; i++) {
|
||||
naut_peer_addr addr;
|
||||
|
|
@ -958,10 +1052,15 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
|||
from_magnet ? NULL : mi.tracker_tiers;
|
||||
size_t num_trackers =
|
||||
from_magnet ? magnet.num_trackers : mi.num_trackers;
|
||||
tracker_count = init_tracker_stats(trackers, tracker_tiers,
|
||||
num_trackers, tracker_stats,
|
||||
NAUT_SWARM_MAX_TRACKER_STATS);
|
||||
uint64_t total = from_magnet ? 0 : (uint64_t)mi.total_length;
|
||||
if (!discover_trackers(hash, total, trackers, num_trackers,
|
||||
tracker_tiers, peerid, &endpoints, &neps,
|
||||
&epcap) ||
|
||||
tracker_tiers, peerid, 0, total,
|
||||
NAUT_TEV_STARTED, &endpoints, &neps, &epcap,
|
||||
&tracker_interval, tracker_stats,
|
||||
tracker_count) ||
|
||||
(neps < target_peers &&
|
||||
!discover_dht(hash, &endpoints, &neps, &epcap))) {
|
||||
NAUT_ERROR("out of memory collecting discovered peers");
|
||||
|
|
@ -1088,6 +1187,11 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
|||
for (int i = 0; i < npeers; i++) peers[i].fd = -1;
|
||||
|
||||
double t0 = now();
|
||||
double next_tracker_announce =
|
||||
t0 + (tracker_interval > 0
|
||||
? tracker_delay_seconds(tracker_interval)
|
||||
: TRACKER_FAILURE_RETRY_INTERVAL);
|
||||
double next_dht_lookup = t0 + DHT_REFRESH_INTERVAL;
|
||||
naut_err run_error = NAUT_OK;
|
||||
bool cancelled = false;
|
||||
uint32_t connecting = 0;
|
||||
|
|
@ -1096,7 +1200,7 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
|||
struct pollfd *connect_fds = pfd;
|
||||
for (int i = 0; i < npeers; i++) connect_fds[i].fd = -1;
|
||||
report_progress(config, d, &mi, (uint32_t)npeers, 0, 0, 0,
|
||||
peers, npeers, t0);
|
||||
peers, npeers, tracker_stats, tracker_count, t0);
|
||||
for (int i = 0; i < npeers; i++) {
|
||||
bool connected = false;
|
||||
int fd = connect_start(&endpoints[i], &connected);
|
||||
|
|
@ -1130,7 +1234,8 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
|||
while (connecting > 0 && now() < connect_deadline &&
|
||||
!stop_requested(config)) {
|
||||
report_progress(config, d, &mi, (uint32_t)npeers, connecting,
|
||||
active, failed, peers, npeers, t0);
|
||||
active, failed, peers, npeers,
|
||||
tracker_stats, tracker_count, t0);
|
||||
int ready = poll(connect_fds, (nfds_t)neps, 100);
|
||||
if (ready < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
|
|
@ -1181,13 +1286,53 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
|||
|
||||
emit_event(config, NAUT_EVENT_TORRENT_ADDED, 0, NULL, NULL);
|
||||
report_progress(config, d, &mi, (uint32_t)npeers, 0, active, failed,
|
||||
peers, npeers, t0);
|
||||
peers, npeers, tracker_stats, tracker_count, t0);
|
||||
while (!naut_download_complete(d) && run_error == NAUT_OK) {
|
||||
service_control(config, st);
|
||||
if (stop_requested(config)) {
|
||||
cancelled = true;
|
||||
break;
|
||||
}
|
||||
if (config->num_peers == 0 && npending == 0 &&
|
||||
peer_count_active(peers, npeers, true) < target_peers) {
|
||||
double t = now();
|
||||
if (mi.num_trackers > 0 && t >= next_tracker_announce) {
|
||||
uint64_t downloaded = naut_download_bytes_done(d);
|
||||
uint64_t left = (uint64_t)mi.total_length > downloaded
|
||||
? (uint64_t)mi.total_length - downloaded : 0;
|
||||
int32_t interval = 0;
|
||||
size_t before = npending;
|
||||
if (!discover_trackers(mi.infohash_v1,
|
||||
(uint64_t)mi.total_length,
|
||||
mi.trackers, mi.num_trackers,
|
||||
mi.tracker_tiers, peerid,
|
||||
downloaded, left, NAUT_TEV_NONE,
|
||||
&pending, &npending, &pending_cap,
|
||||
&interval, tracker_stats,
|
||||
tracker_count)) {
|
||||
run_error = NAUT_ERR_NOMEM;
|
||||
break;
|
||||
}
|
||||
next_tracker_announce = t + (interval > 0
|
||||
? tracker_delay_seconds(interval)
|
||||
: TRACKER_FAILURE_RETRY_INTERVAL);
|
||||
if (npending > before)
|
||||
NAUT_INFO("tracker refresh queued %zu peers",
|
||||
npending - before);
|
||||
}
|
||||
if (npending == 0 && t >= next_dht_lookup) {
|
||||
size_t before = npending;
|
||||
if (!discover_dht(mi.infohash_v1, &pending, &npending,
|
||||
&pending_cap)) {
|
||||
run_error = NAUT_ERR_NOMEM;
|
||||
break;
|
||||
}
|
||||
next_dht_lookup = t + DHT_REFRESH_INTERVAL;
|
||||
if (npending > before)
|
||||
NAUT_INFO("DHT refresh queued %zu peers",
|
||||
npending - before);
|
||||
}
|
||||
}
|
||||
if (!connect_pending_peers(config, d, &mi, peerid, &peers, &npeers,
|
||||
&peer_capacity, &pfd, &idx_map,
|
||||
pending, &npending, target_peers, &failed)) {
|
||||
|
|
@ -1226,7 +1371,7 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
|||
(uint32_t)connecting_peers,
|
||||
(uint32_t)live_peers,
|
||||
failed,
|
||||
peers, npeers, t0);
|
||||
peers, npeers, tracker_stats, tracker_count, t0);
|
||||
int r = poll(pfd, nf, 200);
|
||||
if (r < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
|
|
@ -1304,7 +1449,8 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
|||
naut_download_pieces_done(d), mi.num_pieces, dt, mb/dt,
|
||||
naut_download_in_endgame(d) ? " (passed through endgame)" : "");
|
||||
report_progress(config, d, &mi, (uint32_t)npeers, 0, 0,
|
||||
(uint32_t)npeers, peers, npeers, t0);
|
||||
(uint32_t)npeers, peers, npeers,
|
||||
tracker_stats, tracker_count, t0);
|
||||
emit_event(config, NAUT_EVENT_TORRENT_FINISHED, 0, NULL, NULL);
|
||||
while (config->keep_alive && !stop_requested(config)) {
|
||||
service_control(config, st);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue