A clone of btpd with my configuration changes.
Você não pode selecionar mais de 25 tópicos Os tópicos devem começar com uma letra ou um número, podem incluir traços ('-') e podem ter até 35 caracteres.

591 linhas
13 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,
  126. field_len +
  127. (off_t)ceil(mi->npieces * mi->piece_length / (double)(1<<17))) < 0)
  128. err(1, "ftruncate: %s", path);
  129. if (write(fd, cb.piece_field, field_len) != field_len)
  130. err(1, "write %s.i", path);
  131. if (close(fd) < 0)
  132. err(1, "close %s.i", path);
  133. clear_metainfo(mi);
  134. free(mi);
  135. }
  136. static struct option add_opts[] = {
  137. { "ipc", required_argument, NULL, 1 },
  138. { "help", required_argument, NULL, 2},
  139. {NULL, 0, NULL, 0}
  140. };
  141. static void
  142. do_add(char *ipctok, char **paths, int npaths, char **out)
  143. {
  144. struct ipc *ipc;
  145. do_ipc_open(ipctok, &ipc);
  146. handle_error(btpd_add(ipc, paths, npaths, out));
  147. ipc_close(ipc);
  148. }
  149. static void
  150. cmd_add(int argc, char **argv)
  151. {
  152. int ch;
  153. char *ipctok = NULL;
  154. while ((ch = getopt_long(argc, argv, "", add_opts, NULL)) != -1) {
  155. switch(ch) {
  156. case 1:
  157. ipctok = optarg;
  158. break;
  159. default:
  160. usage();
  161. }
  162. }
  163. argc -= optind;
  164. argv += optind;
  165. if (argc < 1)
  166. usage();
  167. for (int i = 0; i < argc; i++) {
  168. int64_t code;
  169. char *res;
  170. int fd;
  171. char *path;
  172. errno = vopen(&fd, O_RDONLY, "%s.i", argv[i]);
  173. if (errno == ENOENT) {
  174. printf("Testing %s for content.\n", argv[i]);
  175. gen_ifile(argv[i]);
  176. } else if (errno != 0)
  177. err(1, "open %s.i", argv[i]);
  178. else
  179. close(fd);
  180. if ((errno = canon_path(argv[i], &path)) != 0)
  181. err(1, "canon_path");
  182. do_add(ipctok, &path, 1, &res);
  183. free(path);
  184. benc_dget_int64(benc_first(res), "code", &code);
  185. if (code == EEXIST)
  186. printf("btpd already had %s.\n", argv[i]);
  187. else if (code != 0) {
  188. printf("btpd indicates error: %s for %s.\n",
  189. strerror(code), argv[i]);
  190. }
  191. free(res);
  192. }
  193. }
  194. static struct option del_opts[] = {
  195. { "ipc", required_argument, NULL, 1 },
  196. { "help", required_argument, NULL, 2},
  197. {NULL, 0, NULL, 0}
  198. };
  199. static void
  200. do_del(char *ipctok, uint8_t (*hashes)[20], int nhashes, char **out)
  201. {
  202. struct ipc *ipc;
  203. do_ipc_open(ipctok, &ipc);
  204. handle_error(btpd_del(ipc, hashes, nhashes, out));
  205. ipc_close(ipc);
  206. }
  207. static void
  208. cmd_del(int argc, char **argv)
  209. {
  210. int ch;
  211. char *ipctok = NULL;
  212. while ((ch = getopt_long(argc, argv, "", del_opts, NULL)) != -1) {
  213. switch(ch) {
  214. case 1:
  215. ipctok = optarg;
  216. break;
  217. default:
  218. usage();
  219. }
  220. }
  221. argc -= optind;
  222. argv += optind;
  223. if (argc < 1)
  224. usage();
  225. uint8_t hashes[argc][20];
  226. char *res;
  227. const char *d;
  228. for (int i = 0; i < argc; i++) {
  229. struct metainfo *mi;
  230. if ((errno = load_metainfo(argv[i], -1, 0, &mi)) != 0)
  231. err(1, "load_metainfo: %s", argv[i]);
  232. bcopy(mi->info_hash, hashes[i], 20);
  233. clear_metainfo(mi);
  234. free(mi);
  235. }
  236. do_del(ipctok, hashes, argc, &res);
  237. d = benc_first(res);
  238. for (int i = 0; i < argc; i++) {
  239. int64_t code;
  240. benc_dget_int64(d, "code", &code);
  241. if (code == ENOENT)
  242. printf("btpd didn't have %s.\n", argv[i]);
  243. else if (code != 0) {
  244. printf("btpd indicates error: %s for %s.\n",
  245. strerror(code), argv[i]);
  246. }
  247. d = benc_next(d);
  248. }
  249. free(res);
  250. }
  251. static struct option die_opts[] = {
  252. { "ipc", required_argument, NULL, 1 },
  253. { "help", no_argument, NULL, 2 },
  254. {NULL, 0, NULL, 0}
  255. };
  256. static void
  257. do_die(char *ipctok)
  258. {
  259. struct ipc *ipc;
  260. do_ipc_open(ipctok, &ipc);
  261. handle_error(btpd_die(ipc));
  262. ipc_close(ipc);
  263. }
  264. static void
  265. cmd_die(int argc, char **argv)
  266. {
  267. int ch;
  268. char *ipctok = NULL;
  269. while ((ch = getopt_long(argc, argv, "", die_opts, NULL)) != -1) {
  270. switch (ch) {
  271. case 1:
  272. ipctok = optarg;
  273. break;
  274. default:
  275. usage();
  276. }
  277. }
  278. do_die(ipctok);
  279. }
  280. static struct option stat_opts[] = {
  281. { "ipc", required_argument, NULL, 1 },
  282. { "help", no_argument, NULL, 2 },
  283. {NULL, 0, NULL, 0}
  284. };
  285. static void
  286. do_stat(char *ipctok, char **out)
  287. {
  288. struct ipc *ipc;
  289. do_ipc_open(ipctok, &ipc);
  290. handle_error(btpd_stat(ipc, out));
  291. ipc_close(ipc);
  292. }
  293. struct tor {
  294. char *path;
  295. uint8_t hash[20];
  296. uint64_t down;
  297. uint64_t up;
  298. uint64_t npeers;
  299. uint64_t npieces;
  300. uint64_t have_npieces;
  301. uint64_t seen_npieces;
  302. };
  303. struct tor **parse_tors(char *res, uint8_t (*hashes)[20], int nhashes)
  304. {
  305. struct tor **tors;
  306. int64_t num;
  307. const char *p;
  308. benc_dget_int64(res, "ntorrents", &num);
  309. benc_dget_lst(res, "torrents", &p);
  310. tors = calloc(sizeof(*tors), num + 1);
  311. int i = 0;
  312. for (p = benc_first(p); p; p = benc_next(p)) {
  313. int j;
  314. const char *hash;
  315. benc_dget_str(p, "hash", &hash, NULL);
  316. for (j = 0; j < nhashes; j++) {
  317. if (bcmp(hashes[i], hash, 20) == 0)
  318. break;
  319. }
  320. if (j < nhashes || nhashes == 0) {
  321. tors[i] = calloc(sizeof(*tors[i]), 1);
  322. bcopy(hash, tors[i]->hash, 20);
  323. benc_dget_int64(p, "down", &tors[i]->down);
  324. benc_dget_int64(p, "up", &tors[i]->up);
  325. benc_dget_int64(p, "npeers", &tors[i]->npeers);
  326. benc_dget_int64(p, "npieces", &tors[i]->npieces);
  327. benc_dget_int64(p, "have npieces", &tors[i]->have_npieces);
  328. benc_dget_int64(p, "seen npieces", &tors[i]->seen_npieces);
  329. benc_dget_strz(p, "path", &tors[i]->path, NULL);
  330. i++;
  331. }
  332. }
  333. return tors;
  334. }
  335. static void
  336. free_tors(struct tor **tors)
  337. {
  338. for (int i = 0; tors[i] != NULL; i++) {
  339. free(tors[i]->path);
  340. free(tors[i]);
  341. }
  342. free(tors);
  343. }
  344. static void
  345. print_stat(struct tor *cur, struct tor *old, double ds)
  346. {
  347. if (old == NULL) {
  348. printf("%5.1f%% %5.1f%% %6.1fM - kB/s %6.1fM - kB/s %4u\n",
  349. 100 * cur->seen_npieces / (double)cur->npieces,
  350. 100 * cur->have_npieces / (double)cur->npieces,
  351. cur->down / (double)(1 << 20),
  352. cur->up / (double)(1 << 20),
  353. (unsigned)cur->npeers);
  354. } else {
  355. printf("%5.1f%% %5.1f%% %6.1fM %7.2fkB/s %6.1fM %7.2fkB/s %4u\n",
  356. 100 * cur->seen_npieces / (double)cur->npieces,
  357. 100 * cur->have_npieces / (double)cur->npieces,
  358. cur->down / (double)(1 << 20),
  359. (cur->down - old->down) / ds / (1 << 10),
  360. cur->up / (double)(1 << 20),
  361. (cur->up - old->up) / ds / (1 << 10),
  362. (unsigned)cur->npeers
  363. );
  364. }
  365. }
  366. static void
  367. grok_stat(char *ipctok, int iflag, int wait,
  368. uint8_t (*hashes)[20], int nhashes)
  369. {
  370. int i, j;
  371. char *res;
  372. struct tor **cur, **old = NULL;
  373. struct tor curtot, oldtot;
  374. struct timeval tv_cur, tv_old;
  375. double ds;
  376. again:
  377. do_stat(ipctok, &res);
  378. gettimeofday(&tv_cur, NULL);
  379. if (old == NULL)
  380. ds = wait;
  381. else {
  382. struct timeval delta;
  383. timersub(&tv_old, &tv_cur, &delta);
  384. ds = delta.tv_sec + delta.tv_usec / 1000000.0;
  385. if (ds < 0)
  386. ds = wait;
  387. }
  388. tv_old = tv_cur;
  389. cur = parse_tors(res, hashes, nhashes);
  390. free(res);
  391. if (iflag) {
  392. for (i = 0; cur[i] != NULL; i++) {
  393. if (old == NULL) {
  394. printf("%s:\n", rindex(cur[i]->path, '/') + 1);
  395. print_stat(cur[i], NULL, ds);
  396. } else {
  397. for (j = 0; old[j] != NULL; j++)
  398. if (bcmp(cur[i]->hash, old[j]->hash, 20) == 0)
  399. break;
  400. printf("%s:\n", rindex(cur[i]->path, '/') + 1);
  401. print_stat(cur[i], old[j], ds);
  402. }
  403. }
  404. }
  405. bzero(&curtot, sizeof(curtot));
  406. for (i = 0; cur[i] != NULL; i++) {
  407. curtot.down += cur[i]->down;
  408. curtot.up += cur[i]->up;
  409. curtot.npeers += cur[i]->npeers;
  410. curtot.npieces += cur[i]->npieces;
  411. curtot.have_npieces += cur[i]->have_npieces;
  412. curtot.seen_npieces += cur[i]->seen_npieces;
  413. }
  414. if (iflag)
  415. printf("Total:\n");
  416. if (old != NULL)
  417. print_stat(&curtot, &oldtot, ds);
  418. else
  419. print_stat(&curtot, NULL, ds);
  420. if (wait) {
  421. if (old != NULL)
  422. free_tors(old);
  423. old = cur;
  424. oldtot = curtot;
  425. sleep(wait);
  426. goto again;
  427. }
  428. free_tors(cur);
  429. }
  430. static void
  431. cmd_stat(int argc, char **argv)
  432. {
  433. int ch;
  434. char *ipctok = NULL;
  435. int wait = 0;
  436. int iflag = 0;
  437. while ((ch = getopt_long(argc, argv, "iw:", stat_opts, NULL)) != -1) {
  438. switch (ch) {
  439. case 'i':
  440. iflag = 1;
  441. break;
  442. case 'w':
  443. wait = atoi(optarg);
  444. if (wait <= 0)
  445. errx(1, "-w argument must be an integer > 0.");
  446. break;
  447. case 1:
  448. ipctok = optarg;
  449. break;
  450. default:
  451. usage();
  452. }
  453. }
  454. argc -= optind;
  455. argv += optind;
  456. if (argc > 0) {
  457. uint8_t hashes[argc][20];
  458. for (int i = 0; i < argc; i++) {
  459. struct metainfo *mi;
  460. if ((errno = load_metainfo(argv[i], -1, 0, &mi)) != 0)
  461. err(1, "load_metainfo: %s", argv[i]);
  462. bcopy(mi->info_hash, hashes[i], 20);
  463. clear_metainfo(mi);
  464. free(mi);
  465. }
  466. grok_stat(ipctok, iflag, wait, hashes, argc);
  467. } else
  468. grok_stat(ipctok, iflag, wait, NULL, 0);
  469. }
  470. static struct option list_opts[] = {
  471. { "ipc", required_argument, NULL, 1 },
  472. { "help", no_argument, NULL, 2 },
  473. {NULL, 0, NULL, 0}
  474. };
  475. static void
  476. cmd_list(int argc, char **argv)
  477. {
  478. int ch;
  479. char *ipctok = NULL;
  480. while ((ch = getopt_long(argc, argv, "", list_opts, NULL)) != -1) {
  481. switch (ch) {
  482. case 1:
  483. ipctok = optarg;
  484. break;
  485. default:
  486. usage();
  487. }
  488. }
  489. char *res;
  490. const char *p;
  491. char *path;
  492. do_stat(ipctok, &res);
  493. benc_dget_lst(res, "torrents", &p);
  494. int count = 0;
  495. for (p = benc_first(p); p; p = benc_next(p)) {
  496. count++;
  497. benc_dget_strz(p, "path", &path, NULL);
  498. printf("%s\n", path);
  499. free(path);
  500. }
  501. printf("%d torrent%s.\n", count, count == 1 ? "" : "s");
  502. }
  503. static struct {
  504. const char *name;
  505. void (*fun)(int, char **);
  506. } cmd_table[] = {
  507. { "add", cmd_add },
  508. { "del", cmd_del },
  509. { "die", cmd_die },
  510. { "list", cmd_list},
  511. { "stat", cmd_stat }
  512. };
  513. static int ncmds = sizeof(cmd_table) / sizeof(cmd_table[0]);
  514. int
  515. main(int argc, char **argv)
  516. {
  517. if (argc < 2)
  518. usage();
  519. int found = 0;
  520. for (int i = 0; !found && i < ncmds; i++) {
  521. if (strcmp(argv[1], cmd_table[i].name) == 0) {
  522. found = 1;
  523. cmd_table[i].fun(argc - 1, argv + 1);
  524. }
  525. }
  526. if (!found)
  527. usage();
  528. return 0;
  529. }