ctorrent stat collector
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

connection.c 7.5KB

13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
13 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <unistd.h>
  4. #include <string.h>
  5. #include <time.h>
  6. #include "event.h"
  7. #include "httpd.h"
  8. #include "linebuffer.h"
  9. #include "list.h"
  10. #include "logging.h"
  11. #include "sockaddr.h"
  12. #include "tcpsocket.h"
  13. static LIST_HEAD(torrent_list);
  14. struct torrent_file {
  15. struct list_head list;
  16. struct list_head client_list;
  17. char *name;
  18. };
  19. struct client_con {
  20. struct list_head list;
  21. struct sockaddr_in addr;
  22. struct event_fd *event;
  23. struct linebuffer *lbuf;
  24. int bw_up;
  25. int bw_dn;
  26. unsigned long long total_up;
  27. unsigned long long total_dn;
  28. int chunk_total;
  29. int chunk_avail;
  30. int chunk_have;
  31. long completed;
  32. struct torrent_file *torrent;
  33. };
  34. static struct torrent_file * find_create_torrent(const char *filename)
  35. {
  36. struct torrent_file *torrent;
  37. list_for_each_entry(torrent, &torrent_list, list) {
  38. if (strcmp(torrent->name, filename) == 0)
  39. return torrent;
  40. }
  41. torrent = malloc(sizeof(struct torrent_file) + strlen(filename));
  42. if (torrent == NULL) {
  43. log_print(LOG_WARN, "find_create_torrent(): out of memory");
  44. return NULL;
  45. }
  46. INIT_LIST_HEAD(&torrent->client_list);
  47. torrent->name = strdup(filename);
  48. struct torrent_file *search;
  49. list_for_each_entry(search, &torrent_list, list)
  50. if (strcmp(search->name, torrent->name) > 0)
  51. break;
  52. list_add_tail(&torrent->list, &search->list);
  53. return torrent;
  54. }
  55. static void free_client(struct client_con *con)
  56. {
  57. list_del(&con->list);
  58. /* remove torrents without clients */
  59. if (list_empty(&con->torrent->client_list)) {
  60. list_del(&con->torrent->list);
  61. free(con->torrent->name);
  62. free(con->torrent);
  63. }
  64. close(event_get_fd(con->event));
  65. event_remove_fd(con->event);
  66. free(con->lbuf);
  67. free(con);
  68. }
  69. static int data_cb(int fd, void *privdata)
  70. {
  71. struct client_con *con = (struct client_con *)privdata;
  72. if (linebuffer_readfd(con->lbuf, fd) < 0) {
  73. free_client(con);
  74. return -1;
  75. }
  76. char *line;
  77. while ((line = linebuffer_getline(con->lbuf, NULL)) != NULL) {
  78. if (strncmp(line, "CTBW ", 5) == 0) {
  79. int bwup, bwdn, liup, lidn;
  80. if (sscanf(line +5, "%d,%d %d,%d", &bwdn, &bwup, &lidn, &liup) == 4) {
  81. con->bw_up = bwup;
  82. con->bw_dn = bwdn;
  83. }
  84. } else if (strncmp(line, "CTSTATUS ", 9) == 0) {
  85. int seeds1 = 0, seeds2 = 0, leech1 = 0, leech2 = 0, count = 0;
  86. int chunk1 = 0, chunk2 = 0, chunk3 = 0, bwdn = 0, bwup = 0;
  87. int lidn = 0, liup = 0, cache = 0;
  88. unsigned long long totdn = 0, totup = 0;
  89. if (sscanf(line +9, "%d:%d/%d:%d/%d %d/%d/%d %d,%d %llu,%llu %d,%d %d",
  90. &seeds1, &seeds2, &leech1, &leech2, &count,
  91. &chunk1, &chunk2, &chunk3,
  92. &bwdn, &bwup, &totdn, &totup,
  93. &lidn, &liup, &cache) == 15) {
  94. con->total_up = totup;
  95. con->total_dn = totdn;
  96. con->chunk_have = chunk1;
  97. con->chunk_total = chunk2;
  98. con->chunk_avail = chunk3;
  99. }
  100. } else if (strncmp(line, "CTORRENT ", 9) == 0) {
  101. char *filename = strrchr(line +9, ' ');
  102. if (filename != NULL) {
  103. struct torrent_file *torrent = find_create_torrent(filename +1);
  104. if (torrent != NULL) {
  105. list_del(&con->list);
  106. con->torrent = torrent;
  107. list_add_tail(&con->list, &con->torrent->client_list);
  108. }
  109. }
  110. }
  111. linebuffer_freeline(con->lbuf);
  112. }
  113. /* move completed clients to top of the list, ordered by their timestamp */
  114. if (con->chunk_have == con->chunk_total && con->chunk_total != 0 && con->completed == 0) {
  115. con->completed = time(NULL);
  116. list_del(&con->list);
  117. struct client_con *search;
  118. list_for_each_entry(search, &con->torrent->client_list, list) {
  119. if (search->completed == 0)
  120. break;
  121. if (search->completed > con->completed)
  122. break;
  123. }
  124. list_add_tail(&con->list, &search->list);
  125. return 0;
  126. }
  127. return 0;
  128. }
  129. int ctcs_trigger_status(void *privdata)
  130. {
  131. long timeout = time(NULL) - 300;
  132. struct torrent_file *torrent;
  133. list_for_each_entry(torrent, &torrent_list, list) {
  134. int delete = 0;
  135. struct client_con *con;
  136. list_for_each_entry(con, &torrent->client_list, list) {
  137. write(event_get_fd(con->event), "SENDSTATUS\n", 11);
  138. delete += (con->completed == 0) ? -1 : 1;
  139. }
  140. /* delete holds the number of clients to quit */
  141. list_for_each_entry(con, &torrent->client_list, list) {
  142. if (delete <= 0)
  143. break;
  144. if (con->completed == 0 || con->completed > timeout)
  145. continue;
  146. write(event_get_fd(con->event), "CTQUIT\n", 7);
  147. delete--;
  148. }
  149. }
  150. return 0;
  151. }
  152. int ctcs_accept_handler(int fd, void *privdata)
  153. {
  154. struct client_con *con = malloc(sizeof(struct client_con));
  155. if (con == NULL) {
  156. log_print(LOG_WARN, "accept_cb(): out of memory");
  157. return 0;
  158. }
  159. memset(con, 0, sizeof(struct client_con));
  160. con->lbuf = create_linebuffer(1024);
  161. if (con->lbuf == NULL) {
  162. log_print(LOG_WARN, "accept_cb(): out of memory");
  163. free(con);
  164. return 0;
  165. }
  166. unsigned int i = sizeof(con->addr);
  167. int sockfd = accept(fd, (struct sockaddr *)&con->addr, &i);
  168. if (sockfd < 0) {
  169. log_print(LOG_WARN, "accept_cb(): accept()");
  170. free(con->lbuf);
  171. free(con);
  172. return 0;
  173. }
  174. con->event = event_add_readfd(NULL, sockfd, data_cb, con);
  175. con->torrent = find_create_torrent("[unknown]");
  176. list_add_tail(&con->list, &con->torrent->client_list);
  177. return 0;
  178. }
  179. int ctcs_httpd_show(struct httpd_con *con, void *privdata)
  180. {
  181. struct linebuffer *lbuf = create_linebuffer(16384);
  182. if (lbuf == NULL) {
  183. httpd_send_error(con, "500 ERROR", "Out of Memory");
  184. return -1;
  185. }
  186. linebuffer_printf(lbuf, "HTTP/1.0 200 OK\r\nContent-Type: text/html\r\nConnection: close\r\n\r\n");
  187. linebuffer_printf(lbuf, "<html><head><meta http-equiv=\"refresh\" content=\"30;\"></head><body><h1>ctorrent stats</h1>\n");
  188. struct torrent_file *torrent;
  189. list_for_each_entry(torrent, &torrent_list, list) {
  190. if (list_empty(&torrent->client_list))
  191. continue;
  192. linebuffer_printf(lbuf, "<table border=\"1\">\n<tr><td colspan=\"6\" align=\"center\">%s</td></tr>\n", torrent->name);
  193. linebuffer_printf(lbuf, "<tr></td><td><b>Client IP:Port</b></td><td><b>Chunks (have/total/avail)</b></td><td><b>Download total(current)</b></td>");
  194. linebuffer_printf(lbuf, "<td><b>Upload total(current)</b></td><td><b>Completed since</b></td><td><b>Quit</b></td></tr>\n");
  195. struct client_con *tmp;
  196. list_for_each_entry(tmp, &torrent->client_list, list) {
  197. linebuffer_printf(lbuf, "<tr><td align=\"right\">%s</td><td align=\"right\">%3.2lf%% (%d/%d/%d)</td>",
  198. get_sockaddr_buf(&tmp->addr),
  199. (double)tmp->chunk_have / (double)tmp->chunk_total * 100.0, tmp->chunk_have, tmp->chunk_total, tmp->chunk_avail);
  200. linebuffer_printf(lbuf, "<td align=\"right\">%llu (%d)</td><td align=\"right\">%llu (%d)</td><td align=\"right\">%s</td>",
  201. tmp->total_dn, tmp->bw_dn, tmp->total_up, tmp->bw_up,
  202. (tmp->completed != 0) ? ctime(&tmp->completed) : "-");
  203. linebuffer_printf(lbuf, "<td><a href=\"/quit?client=%s\">Quit</td></tr>\n", get_sockaddr_buf(&tmp->addr));
  204. }
  205. linebuffer_printf(lbuf, "</table>\n<br><br>\n");
  206. }
  207. linebuffer_printf(lbuf, "</body></html>\n");
  208. linebuffer_writefd(lbuf, con->fd);
  209. linebuffer_free(lbuf);
  210. return 0;
  211. }
  212. int ctcs_httpd_quit(struct httpd_con *con, void *privdata)
  213. {
  214. if (con->req_arg_cnt == 2 && strncmp(con->req_args[1], "client=", 7) == 0) {
  215. struct sockaddr_in addr;
  216. if (parse_sockaddr(con->req_args[1] +7, &addr) == 0) {
  217. struct torrent_file *torrent;
  218. list_for_each_entry(torrent, &torrent_list, list) {
  219. struct client_con *search;
  220. list_for_each_entry(search, &torrent->client_list, list) {
  221. if (!same_sockaddr(&search->addr, &addr))
  222. continue;
  223. write(event_get_fd(search->event), "CTQUIT\n", 7);
  224. }
  225. }
  226. }
  227. }
  228. char *text = "HTTP/1.0 302 OK\r\nContent-Type: text/html\r\nConnection: close\r\nLocation: /\r\n\r\n";
  229. write(con->fd, text, strlen(text));
  230. return 0;
  231. }