A clone of btpd with my configuration changes.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

433 lines
9.0 KiB

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