#include #include #include #include #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; }