240 lines
5.6 KiB
C
240 lines
5.6 KiB
C
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include <unistd.h>
|
||
|
#include <string.h>
|
||
|
|
||
|
#include "configfile.h"
|
||
|
#include "event.h"
|
||
|
#include "linebuffer.h"
|
||
|
#include "list.h"
|
||
|
#include "logging.h"
|
||
|
#include "sockaddr.h"
|
||
|
#include "tcpsocket.h"
|
||
|
|
||
|
/*
|
||
|
startup:
|
||
|
PROTOCOL 0002
|
||
|
CTORRENT -CD0300-0xECFCD73A6132281994AF75C3 1179237904 1179239410 debian-40r0-i386-netinst.iso.torrent
|
||
|
CTSTATUS 0:174/1:5/0 636/636/636 0,1070 169914368,638976 0,0 32
|
||
|
|
||
|
periodic:
|
||
|
CTBW 0,1070 0,0
|
||
|
|
||
|
-------------------------
|
||
|
|
||
|
"SENDDETAIL":
|
||
|
CTDETAIL 166621184 262144 1179239468 1179238848
|
||
|
CTFILES
|
||
|
CTFILE 1 636 636 636 166621184 debian-40r0-i386-netinst.iso
|
||
|
CTFDONE
|
||
|
|
||
|
"CTDETAIL %lld %d %ld %ld",
|
||
|
BTCONTENT.GetTotalFilesLength(),
|
||
|
(int)(BTCONTENT.GetPieceLength()),
|
||
|
(long)now,
|
||
|
(long)(BTCONTENT.GetSeedTime()) );
|
||
|
|
||
|
"CTFILE %d %d %d %d %llu %s",
|
||
|
(int)n,
|
||
|
(int)(BTCONTENT.getFilePieces(n)),
|
||
|
(int)(tmpBitField.Count()),
|
||
|
(int)(tmpavail.Count()),
|
||
|
(unsigned long long)(file->bf_length),
|
||
|
file->bf_filename );
|
||
|
|
||
|
-------------------------
|
||
|
|
||
|
"SENDSTATUS":
|
||
|
CTSTATUS 0:174/1:5/0 636/636/636 0,959 169914368,655360 0,0 32
|
||
|
|
||
|
"CTSTATUS %d:%d/%d:%d/%d %d/%d/%d %d,%d %llu,%llu %d,%d %d",
|
||
|
(int)(m_ctstatus.seeders),
|
||
|
(int)(Tracker.GetSeedsCount() - ((BTCONTENT.pBF->IsFull() && Tracker.GetSeedsCount() > 0) ? 1 : 0)),
|
||
|
(int)(m_ctstatus.leechers),
|
||
|
(int)(Tracker.GetPeersCount() - Tracker.GetSeedsCount() - ((!BTCONTENT.pBF->IsFull() && Tracker.GetPeersCount() > 0) ? 1 : 0)),
|
||
|
(int)(WORLD.GetConnCount()),
|
||
|
|
||
|
(int)(m_ctstatus.nhave),
|
||
|
(int)(m_ctstatus.ntotal),
|
||
|
(int)(WORLD.Pieces_I_Can_Get()),
|
||
|
|
||
|
(int)(m_ctstatus.dlrate),
|
||
|
(int)(m_ctstatus.ulrate),
|
||
|
|
||
|
(unsigned long long)(m_ctstatus.dltotal),
|
||
|
(unsigned long long)(m_ctstatus.ultotal),
|
||
|
|
||
|
(int)(m_ctstatus.dlimit),
|
||
|
(int)(m_ctstatus.ulimit),
|
||
|
|
||
|
(int)(m_ctstatus.cacheused) );
|
||
|
|
||
|
-------------------------
|
||
|
|
||
|
"SENDCONF":
|
||
|
CTCONFIG 0 72 0.000000 100 1 0 16 0
|
||
|
|
||
|
"SENDPEERS":
|
||
|
CTPEERS
|
||
|
CTPEER M3-4-2--bae925c0c070 159.148.184.187 UnUi 0 862 114688 688128 520
|
||
|
CTPDONE
|
||
|
*/
|
||
|
|
||
|
static LIST_HEAD(client_list);
|
||
|
|
||
|
struct client_con {
|
||
|
struct list_head list;
|
||
|
|
||
|
struct sockaddr_in addr;
|
||
|
struct event_timeout *trigger;
|
||
|
struct event_fd *event;
|
||
|
struct linebuffer *lbuf;
|
||
|
|
||
|
int bw_up;
|
||
|
int bw_dn;
|
||
|
|
||
|
unsigned long long total_up;
|
||
|
unsigned long long total_dn;
|
||
|
|
||
|
int chunk_total;
|
||
|
int chunk_avail;
|
||
|
int chunk_have;
|
||
|
};
|
||
|
|
||
|
static void free_client(struct client_con *con)
|
||
|
{
|
||
|
close(event_get_fd(con->event));
|
||
|
event_remove_fd(con->event);
|
||
|
event_remove_timeout(con->trigger);
|
||
|
free(con->lbuf);
|
||
|
free(con);
|
||
|
}
|
||
|
|
||
|
static int trigger_status(void *privdata)
|
||
|
{
|
||
|
struct client_con *con = (struct client_con *)privdata;
|
||
|
write(event_get_fd(con->event), "SENDSTATUS\n", 11);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int data_cb(int fd, void *privdata)
|
||
|
{
|
||
|
struct client_con *con = (struct client_con *)privdata;
|
||
|
|
||
|
if (linebuffer_read(con->lbuf, fd) < 0) {
|
||
|
log_print(LOG_WARN, "data_cb(): client %s read", get_sockaddr_buf(&con->addr));
|
||
|
list_del(&con->list);
|
||
|
free_client(con);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
char *line;
|
||
|
while ((line = linebuffer_get(con->lbuf)) != NULL) {
|
||
|
// log_print(LOG_DEBUG, "%s: %s", get_sockaddr_buf(&con->addr), line);
|
||
|
|
||
|
if (strncmp(line, "CTBW ", 5) == 0) {
|
||
|
int bwup, bwdn, liup, lidn;
|
||
|
if (sscanf(line +5, "%d,%d %d,%d", &bwdn, &bwup, &lidn, &liup) == 4) {
|
||
|
con->bw_up = bwup;
|
||
|
con->bw_dn = bwdn;
|
||
|
}
|
||
|
|
||
|
} else if (strncmp(line, "CTSTATUS ", 9) == 0) {
|
||
|
int seeds1 = 0, seeds2 = 0, leech1 = 0, leech2 = 0, count = 0;
|
||
|
int chunk1 = 0, chunk2 = 0, chunk3 = 0, bwdn = 0, bwup = 0;
|
||
|
int lidn = 0, liup = 0, cache = 0;
|
||
|
unsigned long long totdn = 0, totup = 0;
|
||
|
|
||
|
if (sscanf(line +9, "%d:%d/%d:%d/%d %d/%d/%d %d,%d %llu,%llu %d,%d %d",
|
||
|
&seeds1, &seeds2, &leech1, &leech2, &count,
|
||
|
&chunk1, &chunk2, &chunk3,
|
||
|
&bwdn, &bwup, &totdn, &totup,
|
||
|
&lidn, &liup, &cache) == 15) {
|
||
|
|
||
|
con->total_up = totup;
|
||
|
con->total_dn = totdn;
|
||
|
con->chunk_have = chunk1;
|
||
|
con->chunk_total = chunk2;
|
||
|
con->chunk_avail = chunk3;
|
||
|
}
|
||
|
|
||
|
log_print(LOG_INFO, "%s: got %3.2lf%% of %3.2lf%% [down: %llu (%d), up: %llu (%d)] ",
|
||
|
get_sockaddr_buf(&con->addr),
|
||
|
(double)con->chunk_have / (double)con->chunk_total * 100.0,
|
||
|
(double)con->chunk_avail / (double)con->chunk_total * 100.0,
|
||
|
con->total_dn, con->bw_dn, con->total_up, con->bw_up);
|
||
|
}
|
||
|
|
||
|
free(line);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int accept_cb(int fd, void *privdata)
|
||
|
{
|
||
|
struct client_con *con = malloc(sizeof(struct client_con));
|
||
|
if (con == NULL) {
|
||
|
log_print(LOG_WARN, "accept_cb(): out of memory");
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
memset(con, 0, sizeof(struct client_con));
|
||
|
|
||
|
con->lbuf = create_linebuffer(1024);
|
||
|
if (con->lbuf == NULL) {
|
||
|
log_print(LOG_WARN, "accept_cb(): out of memory");
|
||
|
free(con);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
unsigned int i = sizeof(con->addr);
|
||
|
int sockfd = accept(fd, (struct sockaddr *)&con->addr, &i);
|
||
|
if (sockfd < 0) {
|
||
|
log_print(LOG_WARN, "accept_cb(): accept()");
|
||
|
free(con->lbuf);
|
||
|
free(con);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
log_print(LOG_INFO, "new client %s", get_sockaddr_buf(&con->addr));
|
||
|
|
||
|
struct timeval tv = { .tv_sec = 10, .tv_usec = 0 };
|
||
|
con->trigger = event_add_timeout(&tv, trigger_status, con);
|
||
|
|
||
|
con->event = event_add_readfd(NULL, sockfd, data_cb, con);
|
||
|
|
||
|
list_add(&con->list, &client_list);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static int listen_cb(const char *parameter, void *privdata)
|
||
|
{
|
||
|
struct sockaddr_in addr;
|
||
|
|
||
|
if (parse_sockaddr(parameter, &addr) < 0) {
|
||
|
log_print(LOG_WARN, "listen_cb(): invalid address");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
int sockfd = tcp_listen(&addr);
|
||
|
if (sockfd < 0) {
|
||
|
log_print(LOG_WARN, "listen_cb(): tcp_listen()");
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
log_print(LOG_INFO, "listen on %s", get_sockaddr_buf(&addr));
|
||
|
event_add_readfd(NULL, sockfd, accept_cb, NULL);
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
int main(int argc, char *argv[])
|
||
|
{
|
||
|
if (config_parse("torrent-stats.conf") < 0)
|
||
|
return 1;
|
||
|
|
||
|
config_get_strings("global", "listen", listen_cb, NULL);
|
||
|
|
||
|
event_loop();
|
||
|
return 0;
|
||
|
}
|