A clone of btpd with my configuration changes.

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