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.

btcli.c 11 KiB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495
  1. #include <sys/types.h>
  2. #include <sys/stat.h>
  3. #include <err.h>
  4. #include <errno.h>
  5. #include <fcntl.h>
  6. #include <getopt.h>
  7. #include <inttypes.h>
  8. #include <limits.h>
  9. #include <stdio.h>
  10. #include <stdlib.h>
  11. #include <string.h>
  12. #include <unistd.h>
  13. #include "btpd_if.h"
  14. #include "metainfo.h"
  15. #include "subr.h"
  16. const char *btpd_dir;
  17. struct ipc *ipc;
  18. void
  19. btpd_connect(void)
  20. {
  21. if ((errno = ipc_open(btpd_dir, &ipc)) != 0)
  22. err(1, "cannot open connection to btpd in %s", btpd_dir);
  23. }
  24. enum ipc_code
  25. handle_ipc_res(enum ipc_code code, const char *target)
  26. {
  27. switch (code) {
  28. case IPC_OK:
  29. break;
  30. case IPC_FAIL:
  31. warnx("btpd couldn't execute the requested operation for %s", target);
  32. break;
  33. case IPC_ERROR:
  34. warnx("btpd encountered an error for %s", target);
  35. break;
  36. default:
  37. errx(1, "fatal error in communication with btpd");
  38. }
  39. return code;
  40. }
  41. void
  42. print_state_name(struct tpstat *ts)
  43. {
  44. char c;
  45. switch (ts->state) {
  46. case T_STARTING:
  47. c = '+';
  48. break;
  49. case T_ACTIVE:
  50. c = ts->pieces_got == ts->torrent_pieces ? 'S' : 'L';
  51. break;
  52. case T_STOPPING:
  53. c = '-';
  54. break;
  55. default:
  56. c = 'U';
  57. break;
  58. }
  59. printf("%c. %s", c, ts->name);
  60. }
  61. void
  62. print_stat(struct tpstat *ts)
  63. {
  64. printf("%5.1f%% %6.1fM %7.2fkB/s %6.1fM %7.2fkB/s %4u %5.1f%%",
  65. 100.0 * ts->content_got / ts->content_size,
  66. (double)ts->downloaded / (1 << 20),
  67. (double)ts->rate_down / (20 << 10),
  68. (double)ts->uploaded / (1 << 20),
  69. (double)ts->rate_up / (20 << 10),
  70. ts->peers,
  71. 100.0 * ts->pieces_seen / ts->torrent_pieces);
  72. if (ts->tr_errors > 0)
  73. printf(" E%u", ts->tr_errors);
  74. printf("\n");
  75. }
  76. void
  77. usage_add(void)
  78. {
  79. printf(
  80. "Add torrents to btpd.\n"
  81. "\n"
  82. "Usage: add [--topdir] -d dir file\n"
  83. " add file ...\n"
  84. "\n"
  85. "Arguments:\n"
  86. "file ...\n"
  87. "\tOne or more torrents to add.\n"
  88. "\n"
  89. "Options:\n"
  90. "-d dir\n"
  91. "\tUse the dir for content.\n"
  92. "\n"
  93. "--topdir\n"
  94. "\tAppend the torrent top directory (if any) to the content path.\n"
  95. "\tThis option cannot be used without the '-d' option.\n"
  96. "\n"
  97. );
  98. exit(1);
  99. }
  100. struct option add_opts [] = {
  101. { "help", no_argument, NULL, 'H' },
  102. { "topdir", no_argument, NULL, 'T'},
  103. {NULL, 0, NULL, 0}
  104. };
  105. int
  106. content_link(uint8_t *hash, char *buf)
  107. {
  108. int n;
  109. char relpath[41];
  110. char path[PATH_MAX];
  111. for (int i = 0; i < 20; i++)
  112. snprintf(relpath + i * 2, 3, "%.2x", hash[i]);
  113. snprintf(path, PATH_MAX, "%s/torrents/%s/content", btpd_dir, relpath);
  114. if ((n = readlink(path, buf, PATH_MAX)) == -1)
  115. return errno;
  116. buf[min(n, PATH_MAX)] = '\0';
  117. return 0;
  118. }
  119. void
  120. cmd_add(int argc, char **argv)
  121. {
  122. int ch, topdir = 0;
  123. char *dir = NULL;
  124. while ((ch = getopt_long(argc, argv, "d:", add_opts, NULL)) != -1) {
  125. switch (ch) {
  126. case 'T':
  127. topdir = 1;
  128. break;
  129. case 'd':
  130. dir = optarg;
  131. break;
  132. default:
  133. usage_add();
  134. }
  135. }
  136. argc -= optind;
  137. argv += optind;
  138. if (argc < 1 || (topdir == 1 && dir == NULL) || (dir != NULL && argc > 1))
  139. usage_add();
  140. btpd_connect();
  141. for (int i = 0; i < argc; i++) {
  142. struct metainfo *mi;
  143. char rdpath[PATH_MAX], dpath[PATH_MAX], fpath[PATH_MAX];
  144. if ((errno = load_metainfo(argv[i], -1, 0, &mi)) != 0) {
  145. warn("error loading torrent %s", argv[i]);
  146. continue;
  147. }
  148. if ((topdir &&
  149. !(mi->nfiles == 1
  150. && strcmp(mi->name, mi->files[0].path) == 0)))
  151. snprintf(dpath, PATH_MAX, "%s/%s", dir, mi->name);
  152. else if (dir != NULL)
  153. strncpy(dpath, dir, PATH_MAX);
  154. else {
  155. if (content_link(mi->info_hash, dpath) != 0) {
  156. warnx("unknown content dir for %s", argv[i]);
  157. errx(1, "use the '-d' option");
  158. }
  159. }
  160. if (mkdir(dpath, 0777) != 0 && errno != EEXIST)
  161. err(1, "couldn't create directory %s", dpath);
  162. if (realpath(dpath, rdpath) == NULL)
  163. err(1, "path error on %s", dpath);
  164. if (realpath(argv[i], fpath) == NULL)
  165. err(1, "path error on %s", fpath);
  166. handle_ipc_res(btpd_add(ipc, mi->info_hash, fpath, rdpath), argv[i]);
  167. clear_metainfo(mi);
  168. free(mi);
  169. }
  170. }
  171. void
  172. usage_del(void)
  173. {
  174. printf(
  175. "Remove torrents from btpd.\n"
  176. "\n"
  177. "Usage: del file ...\n"
  178. "\n"
  179. "Arguments:\n"
  180. "file ...\n"
  181. "\tThe torrents to remove.\n"
  182. "\n");
  183. exit(1);
  184. }
  185. void
  186. cmd_del(int argc, char **argv)
  187. {
  188. if (argc < 2)
  189. usage_del();
  190. btpd_connect();
  191. for (int i = 1; i < argc; i++) {
  192. struct metainfo *mi;
  193. if ((errno = load_metainfo(argv[i], -1, 0, &mi)) != 0) {
  194. warn("error loading torrent %s", argv[i]);
  195. continue;
  196. }
  197. handle_ipc_res(btpd_del(ipc, mi->info_hash), argv[i]);
  198. clear_metainfo(mi);
  199. free(mi);
  200. }
  201. }
  202. void
  203. usage_kill(void)
  204. {
  205. printf(
  206. "Shutdown btpd.\n"
  207. "\n"
  208. "Usage: kill [seconds]\n"
  209. "\n"
  210. "Arguments:\n"
  211. "seconds\n"
  212. "\tThe number of seconds btpd waits before giving up on unresponsive\n"
  213. "\ttrackers.\n"
  214. "\n"
  215. );
  216. exit(1);
  217. }
  218. void
  219. cmd_kill(int argc, char **argv)
  220. {
  221. int seconds = -1;
  222. char *endptr;
  223. if (argc == 2) {
  224. seconds = strtol(argv[1], &endptr, 10);
  225. if (strlen(argv[1]) > endptr - argv[1] || seconds < 0)
  226. usage_kill();
  227. } else if (argc > 2)
  228. usage_kill();
  229. btpd_connect();
  230. handle_ipc_res(btpd_die(ipc, seconds), "kill");
  231. }
  232. void
  233. usage_list(void)
  234. {
  235. printf(
  236. "List active torrents.\n"
  237. "\n"
  238. "Usage: list\n"
  239. "\n"
  240. );
  241. exit(1);
  242. }
  243. void
  244. cmd_list(int argc, char **argv)
  245. {
  246. struct btstat *st;
  247. if (argc > 1)
  248. usage_list();
  249. btpd_connect();
  250. if (handle_ipc_res(btpd_stat(ipc, &st), "list") != IPC_OK)
  251. exit(1);
  252. for (int i = 0; i < st->ntorrents; i++) {
  253. print_state_name(&st->torrents[i]);
  254. putchar('\n');
  255. }
  256. printf("%u torrent%s.\n", st->ntorrents,
  257. st->ntorrents == 1 ? "" : "s");
  258. }
  259. void
  260. usage_stat(void)
  261. {
  262. printf(
  263. "Display stats for active torrents.\n"
  264. "The displayed stats are:\n"
  265. "%% got, MB down, rate down. MB up, rate up\n"
  266. "peer count, %% of pieces seen, tracker errors\n"
  267. "\n"
  268. "Usage: stat [-i] [-w seconds] [file ...]\n"
  269. "\n"
  270. "Arguments:\n"
  271. "file ...\n"
  272. "\tOnly display stats for the given torrent(s).\n"
  273. "\n"
  274. "Options:\n"
  275. "-i\n"
  276. "\tDisplay individual lines for each torrent.\n"
  277. "\n"
  278. "-w n\n"
  279. "\tDisplay stats every n seconds.\n"
  280. "\n");
  281. exit(1);
  282. }
  283. void
  284. do_stat(int individual, int seconds, int hash_count, uint8_t (*hashes)[20])
  285. {
  286. struct btstat *st;
  287. struct tpstat tot;
  288. again:
  289. bzero(&tot, sizeof(tot));
  290. if (handle_ipc_res(btpd_stat(ipc, &st), "stat") != IPC_OK)
  291. exit(1);
  292. for (int i = 0; i < st->ntorrents; i++) {
  293. struct tpstat *cur = &st->torrents[i];
  294. if (hash_count > 0) {
  295. int found = 0;
  296. for (int h = 0; !found && h < hash_count; h++)
  297. if (bcmp(cur->hash, hashes[h], 20) == 0)
  298. found = 1;
  299. if (!found)
  300. continue;
  301. }
  302. tot.uploaded += cur->uploaded;
  303. tot.downloaded += cur->downloaded;
  304. tot.rate_up += cur->rate_up;
  305. tot.rate_down += cur->rate_down;
  306. tot.peers += cur->peers;
  307. tot.pieces_seen += cur->pieces_seen;
  308. tot.torrent_pieces += cur->torrent_pieces;
  309. tot.content_got += cur->content_got;
  310. tot.content_size += cur->content_size;
  311. if (individual) {
  312. print_state_name(cur);
  313. printf(":\n");
  314. print_stat(cur);
  315. }
  316. }
  317. free_btstat(st);
  318. if (individual)
  319. printf("Total:\n");
  320. print_stat(&tot);
  321. if (seconds > 0) {
  322. sleep(seconds);
  323. goto again;
  324. }
  325. }
  326. struct option stat_opts [] = {
  327. { "help", no_argument, NULL, 'H' },
  328. {NULL, 0, NULL, 0}
  329. };
  330. void
  331. cmd_stat(int argc, char **argv)
  332. {
  333. int ch;
  334. int wflag = 0, iflag = 0, seconds = 0;
  335. uint8_t (*hashes)[20] = NULL;
  336. char *endptr;
  337. while ((ch = getopt_long(argc, argv, "iw:", stat_opts, NULL)) != -1) {
  338. switch (ch) {
  339. case 'i':
  340. iflag = 1;
  341. break;
  342. case 'w':
  343. wflag = 1;
  344. seconds = strtol(optarg, &endptr, 10);
  345. if (strlen(optarg) > endptr - optarg || seconds < 1)
  346. usage_stat();
  347. break;
  348. default:
  349. usage_stat();
  350. }
  351. }
  352. argc -= optind;
  353. argv += optind;
  354. if (argc > 0) {
  355. hashes = malloc(argc * 20);
  356. for (int i = 0; i < argc; i++) {
  357. struct metainfo *mi;
  358. if ((errno = load_metainfo(argv[i], -1, 0, &mi)) != 0)
  359. err(1, "error loading torrent %s", argv[i]);
  360. bcopy(mi->info_hash, hashes[i], 20);
  361. clear_metainfo(mi);
  362. free(mi);
  363. }
  364. }
  365. btpd_connect();
  366. do_stat(iflag, seconds, argc, hashes);
  367. }
  368. struct {
  369. const char *name;
  370. void (*fun)(int, char **);
  371. void (*help)(void);
  372. } cmd_table[] = {
  373. { "add", cmd_add, usage_add },
  374. { "del", cmd_del, usage_del },
  375. { "kill", cmd_kill, usage_kill },
  376. { "list", cmd_list, usage_list },
  377. { "stat", cmd_stat, usage_stat }
  378. };
  379. int ncmds = sizeof(cmd_table) / sizeof(cmd_table[0]);
  380. void
  381. usage(void)
  382. {
  383. printf(
  384. "btcli is the btpd command line interface.\n"
  385. "\n"
  386. "Usage: btcli [main options] command [command options]\n"
  387. "\n"
  388. "Main options:\n"
  389. "-d dir\n"
  390. "\tThe btpd directory.\n"
  391. "\n"
  392. "--help [command]\n"
  393. "\tShow this text or help for the specified command.\n"
  394. "\n"
  395. "Commands:\n"
  396. "add\n"
  397. "del\n"
  398. "kill\n"
  399. "list\n"
  400. "stat\n"
  401. "\n");
  402. exit(1);
  403. }
  404. struct option base_opts [] = {
  405. { "help", no_argument, NULL, 'H' },
  406. {NULL, 0, NULL, 0}
  407. };
  408. int
  409. main(int argc, char **argv)
  410. {
  411. int ch, help = 0;
  412. if (argc < 2)
  413. usage();
  414. while ((ch = getopt_long(argc, argv, "+d:", base_opts, NULL)) != -1) {
  415. switch (ch) {
  416. case 'd':
  417. btpd_dir = optarg;
  418. break;
  419. case 'H':
  420. help = 1;
  421. break;
  422. default:
  423. usage();
  424. }
  425. }
  426. argc -= optind;
  427. argv += optind;
  428. if (argc == 0)
  429. usage();
  430. if (btpd_dir == NULL)
  431. if ((btpd_dir = find_btpd_dir()) == NULL)
  432. errx(1, "cannot find the btpd directory");
  433. optind = 0;
  434. int found = 0;
  435. for (int i = 0; !found && i < ncmds; i++) {
  436. if (strcmp(argv[0], cmd_table[i].name) == 0) {
  437. found = 1;
  438. if (help)
  439. cmd_table[i].help();
  440. else
  441. cmd_table[i].fun(argc, argv);
  442. }
  443. }
  444. if (!found)
  445. usage();
  446. return 0;
  447. }