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.

462 lignes
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[i]);
  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. }