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.

462 linhas
10 KiB

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