A clone of btpd with my configuration changes.
Vous ne pouvez pas sélectionner plus de 25 sujets Les noms de sujets doivent commencer par une lettre ou un nombre, peuvent contenir des tirets ('-') et peuvent comporter jusqu'à 35 caractères.

590 lignes
14 KiB

  1. #include <sys/types.h>
  2. #include <sys/time.h>
  3. #include <err.h>
  4. #include <errno.h>
  5. #include <fcntl.h>
  6. #include <getopt.h>
  7. #include <inttypes.h>
  8. #include <math.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <unistd.h>
  13. #include <openssl/sha.h>
  14. #include "benc.h"
  15. #include "metainfo.h"
  16. #include "stream.h"
  17. #include "subr.h"
  18. #include "btpd_if.h"
  19. static void
  20. usage()
  21. {
  22. printf("Usage: btcli command [options] [files]\n"
  23. "Commands:\n"
  24. "add <file_1> ... [file_n]\n"
  25. "\tAdd the given torrents to btpd.\n"
  26. "\n"
  27. "del <file_1> ... [file_n]\n"
  28. "\tRemove the given torrents from btpd.\n"
  29. "\n"
  30. "die\n"
  31. "\tShut down btpd.\n"
  32. "\n"
  33. "list\n"
  34. "\tList active torrents.\n"
  35. "\n"
  36. "stat [-i] [-w n] [file_1] ... [file_n]\n"
  37. "\tShow stats for either all active or the given torrents.\n"
  38. "\tThe stats displayed are:\n"
  39. "\t%% of pieces seen, %% of pieces verified, \n"
  40. "\tMB down, rate down, MB up, rate up, no peers\n"
  41. "-i\n"
  42. "\tShow stats per torrent in addition to total stats.\n"
  43. "-w n\n"
  44. "\tRepeat every n seconds.\n"
  45. "\n"
  46. "Common options:\n"
  47. "--ipc key\n"
  48. "\tTalk to the btpd started with the same key.\n"
  49. "\n"
  50. "--help\n"
  51. "\tShow this help.\n"
  52. "\n");
  53. exit(1);
  54. }
  55. static void
  56. handle_error(int error)
  57. {
  58. switch (error) {
  59. case 0:
  60. break;
  61. case ENOENT:
  62. case ECONNREFUSED:
  63. errx(1, "Couldn't connect. Check that btpd is running.");
  64. default:
  65. errx(1, "%s", strerror(error));
  66. }
  67. }
  68. static void
  69. do_ipc_open(char *ipctok, struct ipc **ipc)
  70. {
  71. switch (ipc_open(ipctok, ipc)) {
  72. case 0:
  73. break;
  74. case EINVAL:
  75. errx(1, "--ipc argument only takes letters and digits.");
  76. case ENAMETOOLONG:
  77. errx(1, "--ipc argument is too long.");
  78. }
  79. }
  80. struct cb {
  81. char *path;
  82. uint8_t *piece_field;
  83. uint32_t have;
  84. struct metainfo *meta;
  85. };
  86. static void
  87. hash_cb(uint32_t index, uint8_t *hash, void *arg)
  88. {
  89. struct cb *cb = arg;
  90. if (hash != NULL)
  91. if (bcmp(hash, cb->meta->piece_hash[index], SHA_DIGEST_LENGTH) == 0) {
  92. set_bit(cb->piece_field, index);
  93. cb->have++;
  94. }
  95. printf("\rTested: %5.1f%%", 100.0 * (index + 1) / cb->meta->npieces);
  96. fflush(stdout);
  97. }
  98. static int
  99. fd_cb(const char *path, int *fd, void *arg)
  100. {
  101. struct cb *fp = arg;
  102. return vopen(fd, O_RDONLY, "%s.d/%s", fp->path, path);
  103. }
  104. static void
  105. gen_ifile(char *path)
  106. {
  107. int fd;
  108. struct cb cb;
  109. struct metainfo *mi;
  110. size_t field_len;
  111. if ((errno = load_metainfo(path, -1, 1, &mi)) != 0)
  112. err(1, "load_metainfo: %s", path);
  113. field_len = ceil(mi->npieces / 8.0);
  114. cb.path = path;
  115. cb.piece_field = calloc(1, field_len);
  116. cb.have = 0;
  117. cb.meta = mi;
  118. if (cb.piece_field == NULL)
  119. errx(1, "Out of memory.\n");
  120. if ((errno = bts_hashes(mi, fd_cb, hash_cb, &cb)) != 0)
  121. err(1, "bts_hashes");
  122. printf("\nHave: %5.1f%%\n", 100.0 * cb.have / cb.meta->npieces);
  123. if ((errno = vopen(&fd, O_WRONLY|O_CREAT, "%s.i", path)) != 0)
  124. err(1, "opening %s.i", path);
  125. if (ftruncate(fd, field_len + mi->npieces *
  126. (off_t)ceil(mi->piece_length / (double)(1 << 17))) < 0)
  127. err(1, "ftruncate: %s", path);
  128. if (write(fd, cb.piece_field, field_len) != field_len)
  129. err(1, "write %s.i", path);
  130. if (close(fd) < 0)
  131. err(1, "close %s.i", path);
  132. clear_metainfo(mi);
  133. free(mi);
  134. }
  135. static struct option add_opts[] = {
  136. { "ipc", required_argument, NULL, 1 },
  137. { "help", required_argument, NULL, 2},
  138. {NULL, 0, NULL, 0}
  139. };
  140. static void
  141. do_add(char *ipctok, char **paths, int npaths, char **out)
  142. {
  143. struct ipc *ipc;
  144. do_ipc_open(ipctok, &ipc);
  145. handle_error(btpd_add(ipc, paths, npaths, out));
  146. ipc_close(ipc);
  147. }
  148. static void
  149. cmd_add(int argc, char **argv)
  150. {
  151. int ch;
  152. char *ipctok = NULL;
  153. while ((ch = getopt_long(argc, argv, "", add_opts, NULL)) != -1) {
  154. switch(ch) {
  155. case 1:
  156. ipctok = optarg;
  157. break;
  158. default:
  159. usage();
  160. }
  161. }
  162. argc -= optind;
  163. argv += optind;
  164. if (argc < 1)
  165. usage();
  166. for (int i = 0; i < argc; i++) {
  167. int64_t code;
  168. char *res;
  169. int fd;
  170. char *path;
  171. errno = vopen(&fd, O_RDONLY, "%s.i", argv[i]);
  172. if (errno == ENOENT) {
  173. printf("Testing %s for content.\n", argv[i]);
  174. gen_ifile(argv[i]);
  175. } else if (errno != 0)
  176. err(1, "open %s.i", argv[i]);
  177. else
  178. close(fd);
  179. if ((errno = canon_path(argv[i], &path)) != 0)
  180. err(1, "canon_path");
  181. do_add(ipctok, &path, 1, &res);
  182. free(path);
  183. benc_dget_int64(benc_first(res), "code", &code);
  184. if (code == EEXIST)
  185. printf("btpd already had %s.\n", argv[i]);
  186. else if (code != 0) {
  187. printf("btpd indicates error: %s for %s.\n",
  188. strerror(code), argv[i]);
  189. }
  190. free(res);
  191. }
  192. }
  193. static struct option del_opts[] = {
  194. { "ipc", required_argument, NULL, 1 },
  195. { "help", required_argument, NULL, 2},
  196. {NULL, 0, NULL, 0}
  197. };
  198. static void
  199. do_del(char *ipctok, uint8_t (*hashes)[20], int nhashes, char **out)
  200. {
  201. struct ipc *ipc;
  202. do_ipc_open(ipctok, &ipc);
  203. handle_error(btpd_del(ipc, hashes, nhashes, out));
  204. ipc_close(ipc);
  205. }
  206. static void
  207. cmd_del(int argc, char **argv)
  208. {
  209. int ch;
  210. char *ipctok = NULL;
  211. while ((ch = getopt_long(argc, argv, "", del_opts, NULL)) != -1) {
  212. switch(ch) {
  213. case 1:
  214. ipctok = optarg;
  215. break;
  216. default:
  217. usage();
  218. }
  219. }
  220. argc -= optind;
  221. argv += optind;
  222. if (argc < 1)
  223. usage();
  224. uint8_t hashes[argc][20];
  225. char *res;
  226. const char *d;
  227. for (int i = 0; i < argc; i++) {
  228. struct metainfo *mi;
  229. if ((errno = load_metainfo(argv[i], -1, 0, &mi)) != 0)
  230. err(1, "load_metainfo: %s", argv[i]);
  231. bcopy(mi->info_hash, hashes[i], 20);
  232. clear_metainfo(mi);
  233. free(mi);
  234. }
  235. do_del(ipctok, hashes, argc, &res);
  236. d = benc_first(res);
  237. for (int i = 0; i < argc; i++) {
  238. int64_t code;
  239. benc_dget_int64(d, "code", &code);
  240. if (code == ENOENT)
  241. printf("btpd didn't have %s.\n", argv[i]);
  242. else if (code != 0) {
  243. printf("btpd indicates error: %s for %s.\n",
  244. strerror(code), argv[i]);
  245. }
  246. d = benc_next(d);
  247. }
  248. free(res);
  249. }
  250. static struct option die_opts[] = {
  251. { "ipc", required_argument, NULL, 1 },
  252. { "help", no_argument, NULL, 2 },
  253. {NULL, 0, NULL, 0}
  254. };
  255. static void
  256. do_die(char *ipctok)
  257. {
  258. struct ipc *ipc;
  259. do_ipc_open(ipctok, &ipc);
  260. handle_error(btpd_die(ipc));
  261. ipc_close(ipc);
  262. }
  263. static void
  264. cmd_die(int argc, char **argv)
  265. {
  266. int ch;
  267. char *ipctok = NULL;
  268. while ((ch = getopt_long(argc, argv, "", die_opts, NULL)) != -1) {
  269. switch (ch) {
  270. case 1:
  271. ipctok = optarg;
  272. break;
  273. default:
  274. usage();
  275. }
  276. }
  277. do_die(ipctok);
  278. }
  279. static struct option stat_opts[] = {
  280. { "ipc", required_argument, NULL, 1 },
  281. { "help", no_argument, NULL, 2 },
  282. {NULL, 0, NULL, 0}
  283. };
  284. static void
  285. do_stat(char *ipctok, char **out)
  286. {
  287. struct ipc *ipc;
  288. do_ipc_open(ipctok, &ipc);
  289. handle_error(btpd_stat(ipc, out));
  290. ipc_close(ipc);
  291. }
  292. struct tor {
  293. char *path;
  294. uint8_t hash[20];
  295. uint64_t down;
  296. uint64_t up;
  297. uint64_t npeers;
  298. uint64_t npieces;
  299. uint64_t have_npieces;
  300. uint64_t seen_npieces;
  301. };
  302. struct tor **parse_tors(char *res, uint8_t (*hashes)[20], int nhashes)
  303. {
  304. struct tor **tors;
  305. int64_t num;
  306. const char *p;
  307. benc_dget_int64(res, "ntorrents", &num);
  308. benc_dget_lst(res, "torrents", &p);
  309. tors = calloc(sizeof(*tors), num + 1);
  310. int i = 0;
  311. for (p = benc_first(p); p; p = benc_next(p)) {
  312. int j;
  313. const char *hash;
  314. benc_dget_str(p, "hash", &hash, NULL);
  315. for (j = 0; j < nhashes; j++) {
  316. if (bcmp(hashes[i], hash, 20) == 0)
  317. break;
  318. }
  319. if (j < nhashes || nhashes == 0) {
  320. tors[i] = calloc(sizeof(*tors[i]), 1);
  321. bcopy(hash, tors[i]->hash, 20);
  322. benc_dget_int64(p, "down", &tors[i]->down);
  323. benc_dget_int64(p, "up", &tors[i]->up);
  324. benc_dget_int64(p, "npeers", &tors[i]->npeers);
  325. benc_dget_int64(p, "npieces", &tors[i]->npieces);
  326. benc_dget_int64(p, "have npieces", &tors[i]->have_npieces);
  327. benc_dget_int64(p, "seen npieces", &tors[i]->seen_npieces);
  328. benc_dget_strz(p, "path", &tors[i]->path, NULL);
  329. i++;
  330. }
  331. }
  332. return tors;
  333. }
  334. static void
  335. free_tors(struct tor **tors)
  336. {
  337. for (int i = 0; tors[i] != NULL; i++) {
  338. free(tors[i]->path);
  339. free(tors[i]);
  340. }
  341. free(tors);
  342. }
  343. static void
  344. print_stat(struct tor *cur, struct tor *old, double ds)
  345. {
  346. if (old == NULL) {
  347. printf("%5.1f%% %5.1f%% %6.1fM - kB/s %6.1fM - kB/s %4u\n",
  348. 100 * cur->seen_npieces / (double)cur->npieces,
  349. 100 * cur->have_npieces / (double)cur->npieces,
  350. cur->down / (double)(1 << 20),
  351. cur->up / (double)(1 << 20),
  352. (unsigned)cur->npeers);
  353. } else {
  354. printf("%5.1f%% %5.1f%% %6.1fM %7.2fkB/s %6.1fM %7.2fkB/s %4u\n",
  355. 100 * cur->seen_npieces / (double)cur->npieces,
  356. 100 * cur->have_npieces / (double)cur->npieces,
  357. cur->down / (double)(1 << 20),
  358. (cur->down - old->down) / ds / (1 << 10),
  359. cur->up / (double)(1 << 20),
  360. (cur->up - old->up) / ds / (1 << 10),
  361. (unsigned)cur->npeers
  362. );
  363. }
  364. }
  365. static void
  366. grok_stat(char *ipctok, int iflag, int wait,
  367. uint8_t (*hashes)[20], int nhashes)
  368. {
  369. int i, j;
  370. char *res;
  371. struct tor **cur, **old = NULL;
  372. struct tor curtot, oldtot;
  373. struct timeval tv_cur, tv_old;
  374. double ds;
  375. again:
  376. do_stat(ipctok, &res);
  377. gettimeofday(&tv_cur, NULL);
  378. if (old == NULL)
  379. ds = wait;
  380. else {
  381. struct timeval delta;
  382. timersub(&tv_old, &tv_cur, &delta);
  383. ds = delta.tv_sec + delta.tv_usec / 1000000.0;
  384. if (ds < 0)
  385. ds = wait;
  386. }
  387. tv_old = tv_cur;
  388. cur = parse_tors(res, hashes, nhashes);
  389. free(res);
  390. if (iflag) {
  391. for (i = 0; cur[i] != NULL; i++) {
  392. if (old == NULL) {
  393. printf("%s:\n", rindex(cur[i]->path, '/') + 1);
  394. print_stat(cur[i], NULL, ds);
  395. } else {
  396. for (j = 0; old[j] != NULL; j++)
  397. if (bcmp(cur[i]->hash, old[j]->hash, 20) == 0)
  398. break;
  399. printf("%s:\n", rindex(cur[i]->path, '/') + 1);
  400. print_stat(cur[i], old[j], ds);
  401. }
  402. }
  403. }
  404. bzero(&curtot, sizeof(curtot));
  405. for (i = 0; cur[i] != NULL; i++) {
  406. curtot.down += cur[i]->down;
  407. curtot.up += cur[i]->up;
  408. curtot.npeers += cur[i]->npeers;
  409. curtot.npieces += cur[i]->npieces;
  410. curtot.have_npieces += cur[i]->have_npieces;
  411. curtot.seen_npieces += cur[i]->seen_npieces;
  412. }
  413. if (iflag)
  414. printf("Total:\n");
  415. if (old != NULL)
  416. print_stat(&curtot, &oldtot, ds);
  417. else
  418. print_stat(&curtot, NULL, ds);
  419. if (wait) {
  420. if (old != NULL)
  421. free_tors(old);
  422. old = cur;
  423. oldtot = curtot;
  424. sleep(wait);
  425. goto again;
  426. }
  427. free_tors(cur);
  428. }
  429. static void
  430. cmd_stat(int argc, char **argv)
  431. {
  432. int ch;
  433. char *ipctok = NULL;
  434. int wait = 0;
  435. int iflag = 0;
  436. while ((ch = getopt_long(argc, argv, "iw:", stat_opts, NULL)) != -1) {
  437. switch (ch) {
  438. case 'i':
  439. iflag = 1;
  440. break;
  441. case 'w':
  442. wait = atoi(optarg);
  443. if (wait <= 0)
  444. errx(1, "-w argument must be an integer > 0.");
  445. break;
  446. case 1:
  447. ipctok = optarg;
  448. break;
  449. default:
  450. usage();
  451. }
  452. }
  453. argc -= optind;
  454. argv += optind;
  455. if (argc > 0) {
  456. uint8_t hashes[argc][20];
  457. for (int i = 0; i < argc; i++) {
  458. struct metainfo *mi;
  459. if ((errno = load_metainfo(argv[i], -1, 0, &mi)) != 0)
  460. err(1, "load_metainfo: %s", argv[i]);
  461. bcopy(mi->info_hash, hashes[i], 20);
  462. clear_metainfo(mi);
  463. free(mi);
  464. }
  465. grok_stat(ipctok, iflag, wait, hashes, argc);
  466. } else
  467. grok_stat(ipctok, iflag, wait, NULL, 0);
  468. }
  469. static struct option list_opts[] = {
  470. { "ipc", required_argument, NULL, 1 },
  471. { "help", no_argument, NULL, 2 },
  472. {NULL, 0, NULL, 0}
  473. };
  474. static void
  475. cmd_list(int argc, char **argv)
  476. {
  477. int ch;
  478. char *ipctok = NULL;
  479. while ((ch = getopt_long(argc, argv, "", list_opts, NULL)) != -1) {
  480. switch (ch) {
  481. case 1:
  482. ipctok = optarg;
  483. break;
  484. default:
  485. usage();
  486. }
  487. }
  488. char *res;
  489. const char *p;
  490. char *path;
  491. do_stat(ipctok, &res);
  492. benc_dget_lst(res, "torrents", &p);
  493. int count = 0;
  494. for (p = benc_first(p); p; p = benc_next(p)) {
  495. count++;
  496. benc_dget_strz(p, "path", &path, NULL);
  497. printf("%s\n", path);
  498. free(path);
  499. }
  500. printf("%d torrent%s.\n", count, count == 1 ? "" : "s");
  501. }
  502. static struct {
  503. const char *name;
  504. void (*fun)(int, char **);
  505. } cmd_table[] = {
  506. { "add", cmd_add },
  507. { "del", cmd_del },
  508. { "die", cmd_die },
  509. { "list", cmd_list},
  510. { "stat", cmd_stat }
  511. };
  512. static int ncmds = sizeof(cmd_table) / sizeof(cmd_table[0]);
  513. int
  514. main(int argc, char **argv)
  515. {
  516. if (argc < 2)
  517. usage();
  518. int found = 0;
  519. for (int i = 0; !found && i < ncmds; i++) {
  520. if (strcmp(argv[1], cmd_table[i].name) == 0) {
  521. found = 1;
  522. cmd_table[i].fun(argc - 1, argv + 1);
  523. }
  524. }
  525. if (!found)
  526. usage();
  527. return 0;
  528. }