A clone of btpd with my configuration changes.

281 行
7.2 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. uint32_t seen_npieces = 0;
  60. for (uint32_t i = 0; i < tp->meta.npieces; i++)
  61. if (tp->net->piece_count[i] > 0)
  62. seen_npieces++;
  63. buf_print(&iob, "d4:downi%llde", tp->net->downloaded);
  64. buf_print(&iob, "6:errorsi%ue", tr_errors(tp));
  65. buf_swrite(&iob, "4:hash20:");
  66. buf_write(&iob, tp->meta.info_hash, 20);
  67. buf_print(&iob, "4:havei%jde", (intmax_t)cm_get_size(tp));
  68. buf_print(&iob, "6:npeersi%ue", tp->net->npeers);
  69. buf_print(&iob, "7:npiecesi%ue", tp->meta.npieces);
  70. buf_print(&iob, "4:path%d:%s", (int)strlen(tp->meta.name),
  71. tp->meta.name);
  72. buf_print(&iob, "2:rdi%lue", tp->net->rate_dwn);
  73. buf_print(&iob, "2:rui%lue", tp->net->rate_up);
  74. buf_print(&iob, "12:seen npiecesi%ue", seen_npieces);
  75. buf_print(&iob, "5:statei%ue", tp->state);
  76. buf_print(&iob, "5:totali%jde", (intmax_t)tp->meta.total_length);
  77. buf_print(&iob, "2:upi%lldee", tp->net->uploaded);
  78. }
  79. buf_swrite(&iob, "ee");
  80. return write_buffer(cli, &iob);
  81. }
  82. static int
  83. cmd_add(struct cli *cli, int argc, const char *args)
  84. {
  85. if (argc != 1)
  86. return EINVAL;
  87. if (btpd_is_stopping())
  88. return write_code_buffer(cli, IPC_FAIL);
  89. size_t hlen;
  90. struct torrent *tp;
  91. enum ipc_code code = IPC_OK;
  92. const uint8_t *hash = benc_dget_mem(args, "hash", &hlen);
  93. char *content = benc_dget_str(args, "content", NULL);
  94. char *torrent = benc_dget_str(args, "torrent", NULL);
  95. if (!(hlen == 20 && content != NULL && torrent != NULL)) {
  96. code = IPC_COMMERR;
  97. goto out;
  98. }
  99. if ((tp = torrent_get(hash)) != NULL) {
  100. code = tp->state == T_STOPPING ? IPC_FAIL : IPC_OK;
  101. goto out;
  102. }
  103. if (torrent_set_links(hash, torrent, content) != 0) {
  104. code = IPC_ERROR;
  105. goto out;
  106. }
  107. if (torrent_start(hash) != 0)
  108. code = IPC_ERROR;
  109. out:
  110. if (content != NULL)
  111. free(content);
  112. if (torrent != NULL)
  113. free(torrent);
  114. if (code == IPC_COMMERR)
  115. return EINVAL;
  116. else
  117. return write_code_buffer(cli, code);
  118. }
  119. static int
  120. cmd_del(struct cli *cli, int argc, const char *args)
  121. {
  122. if (argc != 1 || !benc_isstr(args))
  123. return EINVAL;
  124. size_t hlen;
  125. uint8_t *hash = (uint8_t *)benc_mem(args, &hlen, NULL);
  126. if (hlen != 20)
  127. return EINVAL;
  128. // Stopping a torrent may trigger exit so we need to reply before.
  129. int ret = write_code_buffer(cli, IPC_OK);
  130. struct torrent *tp = torrent_get(hash);
  131. if (tp != NULL)
  132. torrent_stop(tp);
  133. return ret;
  134. }
  135. static int
  136. cmd_die(struct cli *cli, int argc, const char *args)
  137. {
  138. int err = write_code_buffer(cli, IPC_OK);
  139. if (!btpd_is_stopping()) {
  140. int grace_seconds = -1;
  141. if (argc == 1 && benc_isint(args))
  142. grace_seconds = benc_int(args, NULL);
  143. btpd_log(BTPD_L_BTPD, "Someone wants me dead.\n");
  144. btpd_shutdown(grace_seconds);
  145. }
  146. return err;
  147. }
  148. static struct {
  149. const char *name;
  150. int nlen;
  151. int (*fun)(struct cli *cli, int, const char *);
  152. } cmd_table[] = {
  153. { "add", 3, cmd_add },
  154. { "del", 3, cmd_del },
  155. { "die", 3, cmd_die },
  156. { "stat", 4, cmd_stat }
  157. };
  158. static int ncmds = sizeof(cmd_table) / sizeof(cmd_table[0]);
  159. static int
  160. cmd_dispatch(struct cli *cli, const char *buf)
  161. {
  162. size_t cmdlen;
  163. const char *cmd;
  164. const char *args;
  165. cmd = benc_mem(benc_first(buf), &cmdlen, &args);
  166. for (int i = 0; i < ncmds; i++) {
  167. if ((cmdlen == cmd_table[i].nlen &&
  168. strncmp(cmd_table[i].name, cmd, cmdlen) == 0)) {
  169. return cmd_table[i].fun(cli, benc_nelems(buf) - 1, args);
  170. }
  171. }
  172. return ENOENT;
  173. }
  174. static void
  175. cli_read_cb(int sd, short type, void *arg)
  176. {
  177. struct cli *cli = arg;
  178. uint32_t cmdlen;
  179. uint8_t *msg = NULL;
  180. if (read_fully(sd, &cmdlen, sizeof(cmdlen)) != 0)
  181. goto error;
  182. msg = btpd_malloc(cmdlen);
  183. if (read_fully(sd, msg, cmdlen) != 0)
  184. goto error;
  185. if (!(benc_validate(msg, cmdlen) == 0 && benc_islst(msg) &&
  186. benc_first(msg) != NULL && benc_isstr(benc_first(msg))))
  187. goto error;
  188. if (cmd_dispatch(cli, msg) != 0)
  189. goto error;
  190. event_add(&cli->read, NULL);
  191. return;
  192. error:
  193. close(cli->sd);
  194. free(cli);
  195. if (msg != NULL)
  196. free(msg);
  197. }
  198. void
  199. client_connection_cb(int sd, short type, void *arg)
  200. {
  201. int nsd;
  202. if ((nsd = accept(sd, NULL, NULL)) < 0) {
  203. if (errno == EWOULDBLOCK || errno == ECONNABORTED)
  204. return;
  205. else
  206. btpd_err("client accept: %s\n", strerror(errno));
  207. }
  208. if ((errno = set_blocking(nsd)) != 0)
  209. btpd_err("set_blocking: %s.\n", strerror(errno));
  210. struct cli *cli = btpd_calloc(1, sizeof(*cli));
  211. cli->sd = nsd;
  212. event_set(&cli->read, cli->sd, EV_READ, cli_read_cb, cli);
  213. event_add(&cli->read, NULL);
  214. }
  215. void
  216. ipc_init(void)
  217. {
  218. int sd;
  219. struct sockaddr_un addr;
  220. size_t psiz = sizeof(addr.sun_path);
  221. addr.sun_family = PF_UNIX;
  222. if (snprintf(addr.sun_path, psiz, "%s/sock", btpd_dir) >= psiz)
  223. btpd_err("'%s/sock' is too long.\n", btpd_dir);
  224. if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0)
  225. btpd_err("sock: %s\n", strerror(errno));
  226. if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0) {
  227. if (errno == EADDRINUSE) {
  228. unlink(addr.sun_path);
  229. if (bind(sd, (struct sockaddr *)&addr, sizeof(addr)) != 0)
  230. btpd_err("bind: %s\n", strerror(errno));
  231. } else
  232. btpd_err("bind: %s\n", strerror(errno));
  233. }
  234. if (chmod(addr.sun_path, 0600) == -1)
  235. btpd_err("chmod: %s (%s).\n", addr.sun_path, strerror(errno));
  236. listen(sd, 4);
  237. set_nonblocking(sd);
  238. event_set(&m_cli_incoming, sd, EV_READ | EV_PERSIST,
  239. client_connection_cb, NULL);
  240. event_add(&m_cli_incoming, NULL);
  241. }