Expose peer and piece state to web UI
This commit is contained in:
parent
f4f4e86be4
commit
6ae72907b0
6 changed files with 213 additions and 11 deletions
|
|
@ -213,6 +213,16 @@ static json_t *torrent_json(torrent_task *task) {
|
|||
json_integer(task->stats.pieces_done));
|
||||
json_object_set_new(result, "total_pieces",
|
||||
json_integer(task->stats.total_pieces));
|
||||
json_t *piece_states = json_array();
|
||||
if (piece_states) {
|
||||
uint32_t state_count = task->stats.piece_state_count;
|
||||
if (state_count > NAUT_SWARM_MAX_PIECE_STATS)
|
||||
state_count = NAUT_SWARM_MAX_PIECE_STATS;
|
||||
for (uint32_t i = 0; i < state_count; i++)
|
||||
json_array_append_new(piece_states,
|
||||
json_integer(task->stats.piece_states[i]));
|
||||
json_object_set_new(result, "piece_states", piece_states);
|
||||
}
|
||||
json_object_set_new(result, "peers",
|
||||
json_integer(task->stats.peers_active));
|
||||
json_object_set_new(result, "peers_discovered",
|
||||
|
|
@ -221,6 +231,31 @@ static json_t *torrent_json(torrent_task *task) {
|
|||
json_integer(task->stats.peers_connecting));
|
||||
json_object_set_new(result, "peers_failed",
|
||||
json_integer(task->stats.peers_failed));
|
||||
json_t *peer_list = json_array();
|
||||
if (peer_list) {
|
||||
uint32_t peer_count = task->stats.peer_count;
|
||||
if (peer_count > NAUT_SWARM_MAX_PEER_STATS)
|
||||
peer_count = NAUT_SWARM_MAX_PEER_STATS;
|
||||
for (uint32_t i = 0; i < peer_count; i++) {
|
||||
const naut_swarm_peer_stats *peer =
|
||||
&task->stats.peer_stats[i];
|
||||
json_t *item = json_pack(
|
||||
"{s:s,s:i,s:s,s:s,s:s,s:f,s:f,s:I,s:I,s:I,s:I}",
|
||||
"ip", peer->ip,
|
||||
"port", (int)peer->port,
|
||||
"client", peer->client,
|
||||
"connection", peer->connection,
|
||||
"flags", peer->flags,
|
||||
"progress", peer->progress,
|
||||
"relevance", peer->relevance,
|
||||
"downloaded", (json_int_t)peer->downloaded,
|
||||
"uploaded", (json_int_t)peer->uploaded,
|
||||
"dlspeed", (json_int_t)peer->dlspeed,
|
||||
"upspeed", (json_int_t)peer->upspeed);
|
||||
if (item) json_array_append_new(peer_list, item);
|
||||
}
|
||||
json_object_set_new(result, "peer_list", peer_list);
|
||||
}
|
||||
json_object_set_new(result, "elapsed_seconds",
|
||||
json_real(task->stats.elapsed_seconds));
|
||||
json_object_set_new(result, "pending_moves",
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@
|
|||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <poll.h>
|
||||
#include <ctype.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
|
@ -51,12 +52,16 @@ typedef struct {
|
|||
|
||||
typedef struct {
|
||||
int fd;
|
||||
naut_peer_addr addr;
|
||||
char name[40];
|
||||
uint8_t peer_id[20];
|
||||
bool have_peer_id;
|
||||
uint8_t *rbuf; size_t rcap, rlen;
|
||||
bool hs_done, peer_choking;
|
||||
naut_bitfield have;
|
||||
req_t *inflight; size_t nflight, cflight;
|
||||
uint64_t blocks_received;
|
||||
uint64_t bytes_received;
|
||||
naut_ext_handshake extensions;
|
||||
uint64_t pex_received;
|
||||
naut_pipeline pipeline;
|
||||
|
|
@ -121,11 +126,77 @@ static void on_piece_complete(void *opaque, uint32_t index) {
|
|||
emit_event(config, NAUT_EVENT_PIECE_COMPLETE, index, NULL, NULL);
|
||||
}
|
||||
|
||||
static void peer_client_label(const peer_t *peer, char out[64]) {
|
||||
if (!peer || !peer->have_peer_id) {
|
||||
snprintf(out, 64, "Unknown");
|
||||
return;
|
||||
}
|
||||
if (peer->peer_id[0] == '-' && peer->peer_id[7] == '-') {
|
||||
char code[3] = {
|
||||
(char)peer->peer_id[1],
|
||||
(char)peer->peer_id[2],
|
||||
0,
|
||||
};
|
||||
char version[5];
|
||||
memcpy(version, peer->peer_id + 3, 4);
|
||||
version[4] = 0;
|
||||
for (size_t i = 0; i < sizeof version - 1; i++)
|
||||
if (!isprint((unsigned char)version[i])) version[i] = '?';
|
||||
snprintf(out, 64, "%s %s", code, version);
|
||||
return;
|
||||
}
|
||||
char id[21];
|
||||
memcpy(id, peer->peer_id, sizeof peer->peer_id);
|
||||
id[20] = 0;
|
||||
for (size_t i = 0; i < sizeof id - 1; i++)
|
||||
if (!isprint((unsigned char)id[i])) id[i] = '.';
|
||||
snprintf(out, 64, "%s", id);
|
||||
}
|
||||
|
||||
static void peer_flags(const peer_t *peer, char out[16]) {
|
||||
size_t n = 0;
|
||||
if (peer && !peer->peer_choking && n + 1 < 16) out[n++] = 'D';
|
||||
if (peer && (peer->extensions.ut_pex || peer->pex_received) && n + 1 < 16)
|
||||
out[n++] = 'X';
|
||||
out[n] = 0;
|
||||
}
|
||||
|
||||
static void snapshot_peer_stats(naut_swarm_stats *stats,
|
||||
const peer_t *peers, int npeers,
|
||||
uint32_t total_pieces) {
|
||||
if (!stats || !peers || npeers <= 0) return;
|
||||
for (int i = 0; i < npeers &&
|
||||
stats->peer_count < NAUT_SWARM_MAX_PEER_STATS; i++) {
|
||||
const peer_t *peer = &peers[i];
|
||||
if (peer->dead || !peer->hs_done) continue;
|
||||
naut_swarm_peer_stats *out =
|
||||
&stats->peer_stats[stats->peer_count++];
|
||||
snprintf(out->ip, sizeof out->ip, "%u.%u.%u.%u",
|
||||
peer->addr.ip[0], peer->addr.ip[1],
|
||||
peer->addr.ip[2], peer->addr.ip[3]);
|
||||
out->port = peer->addr.port;
|
||||
peer_client_label(peer, out->client);
|
||||
snprintf(out->connection, sizeof out->connection, "TCP");
|
||||
peer_flags(peer, out->flags);
|
||||
size_t have = peer->have.words ? naut_bitfield_count(&peer->have) : 0;
|
||||
double ratio = total_pieces
|
||||
? (double)have / (double)total_pieces : 0.0;
|
||||
if (ratio > 1.0) ratio = 1.0;
|
||||
out->progress = ratio;
|
||||
out->relevance = ratio;
|
||||
out->dlspeed = peer->pipeline.bytes_per_second;
|
||||
out->upspeed = 0.0;
|
||||
out->downloaded = peer->bytes_received;
|
||||
out->uploaded = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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,
|
||||
const peer_t *peers, int npeers,
|
||||
double started_at) {
|
||||
if (!config->on_progress) return;
|
||||
naut_swarm_stats stats = {
|
||||
|
|
@ -139,6 +210,11 @@ static void report_progress(const naut_swarm_config *config,
|
|||
.peers_failed = peers_failed,
|
||||
.elapsed_seconds = now() - started_at,
|
||||
};
|
||||
snapshot_peer_stats(&stats, peers, npeers, metainfo->num_pieces);
|
||||
if (download) {
|
||||
stats.piece_state_count = (uint32_t)naut_download_piece_states(
|
||||
download, stats.piece_states, NAUT_SWARM_MAX_PIECE_STATS);
|
||||
}
|
||||
config->on_progress(config->context, &stats);
|
||||
}
|
||||
|
||||
|
|
@ -476,6 +552,7 @@ static bool peer_start(naut_download *download, const naut_metainfo *metainfo,
|
|||
const uint8_t peerid[20], const endpoint_t *endpoint,
|
||||
peer_t *peer, int fd) {
|
||||
peer->fd = fd;
|
||||
peer->addr = endpoint->addr;
|
||||
snprintf(peer->name, sizeof peer->name, "%s", endpoint->name);
|
||||
peer->peer_choking = true;
|
||||
naut_pipeline_init(&peer->pipeline, NAUT_BLOCK, 4, 1024, 32);
|
||||
|
|
@ -580,6 +657,8 @@ static naut_err peer_process(naut_download *d, const naut_metainfo *mi,
|
|||
p->dead = true;
|
||||
return NAUT_OK;
|
||||
}
|
||||
memcpy(p->peer_id, pid, sizeof p->peer_id);
|
||||
p->have_peer_id = true;
|
||||
pos = NAUT_HANDSHAKE_LEN;
|
||||
p->hs_done = true;
|
||||
}
|
||||
|
|
@ -642,6 +721,7 @@ static naut_err peer_process(naut_download *d, const naut_metainfo *mi,
|
|||
(uint32_t)m.payload_len, &pdone);
|
||||
if (e == NAUT_OK) {
|
||||
p->blocks_received++;
|
||||
p->bytes_received += m.payload_len;
|
||||
cancel_block(d, peers, npeers, p, m.index, m.begin);
|
||||
} else if (e == NAUT_ERR_PROTO) {
|
||||
if (expected) cancel_piece(d, peers, npeers, m.index);
|
||||
|
|
@ -837,7 +917,8 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
|||
uint32_t failed = 0;
|
||||
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, t0);
|
||||
report_progress(config, d, &mi, (uint32_t)npeers, 0, 0, 0,
|
||||
peers, npeers, t0);
|
||||
for (int i = 0; i < npeers; i++) {
|
||||
bool connected = false;
|
||||
int fd = connect_start(&endpoints[i], &connected);
|
||||
|
|
@ -871,7 +952,7 @@ 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, t0);
|
||||
active, failed, peers, npeers, t0);
|
||||
int ready = poll(connect_fds, (nfds_t)neps, 100);
|
||||
if (ready < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
|
|
@ -921,7 +1002,8 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
|||
active, npeers, mi.num_pieces, (long long)mi.total_length);
|
||||
|
||||
emit_event(config, NAUT_EVENT_TORRENT_ADDED, 0, NULL, NULL);
|
||||
report_progress(config, d, &mi, (uint32_t)npeers, 0, active, failed, t0);
|
||||
report_progress(config, d, &mi, (uint32_t)npeers, 0, active, failed,
|
||||
peers, npeers, t0);
|
||||
while (!naut_download_complete(d) && run_error == NAUT_OK) {
|
||||
service_control(config, st);
|
||||
if (stop_requested(config)) {
|
||||
|
|
@ -951,7 +1033,8 @@ naut_err naut_swarm_run(const naut_swarm_config *config) {
|
|||
}
|
||||
report_progress(config, d, &mi, (uint32_t)npeers, 0,
|
||||
(uint32_t)live_peers,
|
||||
(uint32_t)npeers - (uint32_t)live_peers, t0);
|
||||
(uint32_t)npeers - (uint32_t)live_peers,
|
||||
peers, npeers, t0);
|
||||
int r = poll(pfd, nf, 200);
|
||||
if (r < 0) {
|
||||
if (errno == EINTR) continue;
|
||||
|
|
@ -1007,7 +1090,7 @@ 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, t0);
|
||||
(uint32_t)npeers, peers, npeers, 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