A clone of btpd with my configuration changes.

287 lines
7.5 KiB

  1. #include <sys/types.h>
  2. #include <sys/socket.h>
  3. #include <sys/un.h>
  4. #include <arpa/inet.h>
  5. #include <sys/stat.h>
  6. #include <inttypes.h>
  7. #include <limits.h>
  8. #include <stdarg.h>
  9. #include <stdio.h>
  10. #include <string.h>
  11. #include <unistd.h>
  12. #include "btpd.h"
  13. #include "tracker_req.h"
  14. struct cli {
  15. int sd;
  16. struct event read;
  17. };
  18. static struct event m_cli_incoming;
  19. enum ipc_code { // XXX: Same as in cli/btpd_if.h
  20. IPC_OK,
  21. IPC_FAIL,
  22. IPC_ERROR,
  23. IPC_COMMERR
  24. };
  25. static int
  26. write_buffer(struct cli *cli, struct io_buffer *iob)
  27. {
  28. int err = 0;
  29. if (!iob->error) {
  30. uint32_t len = iob->buf_off;
  31. write_fully(cli->sd, &len, sizeof(len));
  32. err = write_fully(cli->sd, iob->buf, iob->buf_off);
  33. } else
  34. btpd_err("Out of memory.\n");
  35. if (iob->buf != NULL)
  36. free(iob->buf);
  37. return err;
  38. }
  39. static int
  40. write_code_buffer(struct cli *cli, enum ipc_code code)
  41. {
  42. struct io_buffer iob;
  43. buf_init(&iob, 16);
  44. buf_print(&iob, "d4:codei%uee", code);
  45. return write_buffer(cli, &iob);
  46. }
  47. static int
  48. cmd_stat(struct cli *cli, int argc, const char *args)
  49. {
  50. struct torrent *tp;
  51. struct io_buffer iob;
  52. buf_init(&iob, (1 << 14));
  53. buf_swrite(&iob, "d");
  54. buf_swrite(&iob, "4:codei0e");
  55. buf_print(&iob, "6:npeersi%ue", net_npeers);
  56. buf_print(&iob, "9:ntorrentsi%ue", torrent_count());
  57. buf_swrite(&iob, "8:torrentsl");
  58. BTPDQ_FOREACH(tp, torrent_get_all(), entry) {
  59. if (tp->state == T_ACTIVE) {
  60. uint32_t seen_npieces = 0;
  61. for (uint32_t i = 0; i < tp->meta.npieces; i++)
  62. if (tp->net->piece_count[i] > 0)
  63. seen_npieces++;
  64. buf_print(&iob, "d4:downi%jue", (intmax_t)tp->net->downloaded);
  65. buf_print(&iob, "6:errorsi%ue", tr_errors(tp));
  66. buf_swrite(&iob, "4:hash20:");
  67. buf_write(&iob, tp->meta.info_hash, 20);
  68. buf_print(&iob, "4:havei%jde", (intmax_t)cm_get_size(tp));
  69. buf_print(&iob, "6:npeersi%ue", tp->net->npeers);
  70. buf_print(&iob, "7:npiecesi%ue", tp->meta.npieces);
  71. buf_print(&iob, "4:path%d:%s", (int)strlen(tp->meta.name),
  72. tp->meta.name);
  73. buf_print(&iob, "2:rdi%lue", tp->net->rate_dwn);
  74. buf_print(&iob, "2:rui%lue", tp->net->rate_up);
  75. buf_print(&iob, "12:seen npiecesi%ue", seen_npieces);
  76. buf_print(&iob, "5:statei%ue", tp->state);
  77. buf_print(&iob, "5:totali%jde", (intmax_t)tp->meta.total_length);
  78. buf_print(&iob, "2:upi%juee", (intmax_t)tp->net->uploaded);
  79. } else {
  80. buf_swrite(&iob, "d4:hash20:");
  81. buf_write(&iob, tp->meta.info_hash, 20);
  82. buf_print(&iob, "4:path%d:%s", (int)strlen(tp->meta.name),
  83. tp->meta.name);
  84. buf_print(&iob, "5:statei%uee", tp->state);
  85. }
  86. }
  87. buf_swrite(&iob, "ee");
  88. return write_buffer(cli, &iob);
  89. }
  90. static int
  91. cmd_add(struct cli *cli, int argc, const char *args)
  92. {
  93. if (argc != 1)
  94. return EINVAL;
  95. if (btpd_is_stopping())
  96. return write_code_buffer(cli, IPC_FAIL);
  97. size_t hlen;
  98. struct torrent *tp;
  99. enum ipc_code code = IPC_OK;
  100. const uint8_t *hash = benc_dget_mem(args, "hash", &hlen);
  101. char *content = benc_dget_str(args, "content", NULL);
  102. char *torrent = benc_dget_str(args, "torrent", NULL);
  103. if (!(hlen == 20 && content != NULL && torrent != NULL)) {
  104. code = IPC_COMMERR;
  105. goto out;
  106. }
  107. if ((tp = torrent_get(hash)) != NULL) {
  108. code = tp->state == T_STOPPING ? IPC_FAIL : IPC_OK;
  109. goto out;
  110. }
  111. if (torrent_set_links(hash, torrent, content) != 0) {
  112. code = IPC_ERROR;
  113. goto out;
  114. }
  115. if (torrent_start(hash) != 0)
  116. code = IPC_ERROR;
  117. out:
  118. if (content != NULL)
  119. free(content);
  120. if (torrent != NULL)
  121. free(torrent);
  122. if (code == IPC_COMMERR)
  123. return EINVAL;
  124. else
  125. return write_code_buffer(cli, code);
  126. }
  127. static int
  128. cmd_del(struct cli *cli, int argc, const char *args)
  129. {
  130. if (argc != 1 || !benc_isstr(args))
  131. return EINVAL;
  132. size_t hlen;
  133. uint8_t *hash = (uint8_t *)benc_mem(args, &hlen, NULL);
  134. if (hlen != 20)
  135. return EINVAL;
  136. struct torrent *tp = torrent_get(hash);
  137. if (tp != NULL)
  138. torrent_stop(tp);
  139. return write_code_buffer(cli, IPC_OK);
  140. }
  141. static int
  142. cmd_die(struct cli *cli, int argc, const char *args)
  143. {
  144. int err = write_code_buffer(cli, IPC_OK);
  145. if (!btpd_is_stopping()) {
  146. int grace_seconds = -1;
  147. if (argc == 1 && benc_isint(args))
  148. grace_seconds = benc_int(args, NULL);
  149. btpd_log(BTPD_L_BTPD, "Someone wants me dead.\n");
  150. btpd_shutdown(grace_seconds);
  151. }
  152. return err;
  153. }
  154. static struct {
  155. const char *name;
  156. int nlen;
  157. int (*fun)(struct cli *cli, int, const char *);
  158. } cmd_table[] = {
  159. { "add", 3, cmd_add },
  160. { "del", 3, cmd_del },
  161. { "die", 3, cmd_die },
  162. { "stat", 4, cmd_stat }
  163. };
  164. static int ncmds = sizeof(cmd_table) / sizeof(cmd_table[0]);
  165. static int
  166. cmd_dispatch(struct cli *cli, const char *buf)
  167. {
  168. size_t cmdlen;
  169. const char *cmd;
  170. const char *args;
  171. cmd = benc_mem(benc_first(buf), &cmdlen, &args);
  172. for (int i = 0; i < ncmds; i++) {
  173. if ((cmdlen == cmd_table[i].nlen &&
  174. strncmp(cmd_table[i].name, cmd, cmdlen) == 0)) {
  175. return cmd_table[i].fun(cli, benc_nelems(buf) - 1, args);
  176. }
  177. }
  178. return ENOENT;
  179. }
  180. static void
  181. cli_read_cb(int sd, short type, void *arg)
  182. {
  183. struct cli *cli = arg;
  184. uint32_t cmdlen;
  185. uint8_t *msg = NULL;
  186. if (read_fully(sd, &cmdlen, sizeof(cmdlen)) != 0)
  187. goto error;
  188. msg = btpd_malloc(cmdlen);
  189. if (read_fully(sd, msg, cmdlen) != 0)
  190. goto error;
  191. if (!(benc_validate(msg, cmdlen) == 0 && benc_islst(msg) &&
  192. benc_first(msg) != NULL && benc_isstr(benc_first(msg))))
  193. goto error;
  194. if (cmd_dispatch(cli, msg) != 0)
  195. goto error;
  196. event_add(&cli->read, NULL);
  197. return;
  198. error:
  199. close(cli->sd);
  200. free(cli);
  201. if (msg != NULL)
  202. free(msg);
  203. }
  204. void
  205. client_connection_cb(int sd, short type, void *arg)
  206. {
  207. int nsd;
  208. if ((nsd = accept(sd, NULL, NULL)) < 0) {
  209. if (errno == EWOULDBLOCK || errno == ECONNABORTED)
  210. return;
  211. else
  212. btpd_err("client accept: %s\n", strerror(errno));
  213. }
  214. if ((errno = set_blocking(nsd)) != 0)
  215. btpd_err("set_blocking: %s.\n", strerror(errno));
  216. struct cli *cli = btpd_calloc(1, sizeof(*cli));
  217. cli->sd = nsd;
  218. event_set(&cli->read, cli->sd, EV_READ, cli_read_cb, cli);
  219. event_add(&cli->read, NULL);
  220. }
  221. void
  222. ipc_init(void)
  223. {
  224. int sd;
  225. struct sockaddr_un addr;
  226. size_t psiz = sizeof(addr.sun_path);
  227. addr.sun_family = PF_UNIX;
  228. if (snprintf(addr.sun_path, psiz, "%s/sock", btpd_dir) >= psiz)
  229. btpd_err("'%s/sock' is too long.\n", btpd_dir);
  230. if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
  231. btpd_err("sock: %s\n", strerror(errno));
  232. if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
  233. if (errno == EADDRINUSE) {
  234. unlink(addr.sun_path);
  235. if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
  236. btpd_err("bind: %s\n", strerror(errno));
  237. } else
  238. btpd_err("bind: %s\n", strerror(errno));
  239. }
  240. if (chmod(addr.sun_path, 0600) == -1)
  241. btpd_err("chmod: %s (%s).\n", addr.sun_path, strerror(errno));
  242. listen(sd, 4);
  243. set_nonblocking(sd);
  244. event_set(&m_cli_incoming, sd, EV_READ | EV_PERSIST,
  245. client_connection_cb, NULL);
  246. event_add(&m_cli_incoming, NULL);
  247. }