diff --git a/misc/btpd_if.c b/misc/btpd_if.c new file mode 100644 index 0000000..8bd0f21 --- /dev/null +++ b/misc/btpd_if.c @@ -0,0 +1,313 @@ +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "benc.h" +#include "btpd_if.h" +#include "iobuf.h" +#include "subr.h" + +struct ipc { + int sd; +}; + +static const char *errmsgs[] = { +#define ERRDEF(name, msg) msg, +#include "ipcdefs.h" +#undef ERRDEF + NULL +}; + +static const char *tval_names[] = { +#define TVDEF(val, type, name) name, +#include "ipcdefs.h" +#undef TVDEF + NULL +}; + +const char * +ipc_strerror(enum ipc_err err) +{ + if (err < 0 || err >= IPC_ERRCOUNT) + return "unknown error"; + return errmsgs[err]; +} + +const char * +tval_name(enum ipc_tval key) +{ + if (key < 0 || key >= IPC_TVALCOUNT) + return "unknown key"; + return tval_names[key]; +} + +int +ipc_open(const char *dir, struct ipc **out) +{ + int sd = -1, err = 0; + size_t plen; + struct ipc *res; + struct sockaddr_un addr; + + plen = sizeof(addr.sun_path); + if (snprintf(addr.sun_path, plen, "%s/sock", dir) >= plen) + return ENAMETOOLONG; + addr.sun_family = AF_UNIX; + + if ((sd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1) + return errno; + + if (connect(sd, (struct sockaddr *)&addr, sizeof(addr)) == -1) { + err = errno; + close(sd); + return err; + } + + if ((res = malloc(sizeof(*res))) == NULL) { + close(sd); + return ENOMEM; + } + + res->sd = sd; + *out = res; + return 0; +} + +void +ipc_close(struct ipc *ipc) +{ + close(ipc->sd); + free(ipc); +} + +static enum ipc_err +ipc_response(struct ipc *ipc, char **out, uint32_t *len) +{ + uint32_t size; + char *buf; + + if (read_fully(ipc->sd, &size, sizeof(size)) != 0) + return IPC_COMMERR; + if (size == 0) + return IPC_COMMERR; + if ((buf = malloc(size)) == NULL) + return IPC_COMMERR; + if (read_fully(ipc->sd, buf, size) != 0) { + free(buf); + return IPC_COMMERR; + } + *out = buf; + *len = size; + return IPC_OK; +} + +static enum ipc_err +ipc_req_res(struct ipc *ipc, const char *req, uint32_t qlen, char **res, + uint32_t *rlen) +{ + assert(benc_validate(req, qlen) == 0); + if (write_fully(ipc->sd, &qlen, sizeof(qlen)) != 0) + return IPC_COMMERR; + if (write_fully(ipc->sd, req, qlen) != 0) + return IPC_COMMERR; + if (ipc_response(ipc, res, rlen) != 0) + return IPC_COMMERR; + if (benc_validate(*res, *rlen) != 0) + return IPC_COMMERR; + if (!benc_isdct(*res)) + return IPC_COMMERR; + return IPC_OK; +} + +static enum ipc_err +ipc_buf_req_res(struct ipc *ipc, struct io_buffer *iob, char **res, + uint32_t *rlen) +{ + enum ipc_err err = ipc_req_res(ipc, iob->buf, iob->buf_off, res, rlen); + free(iob->buf); + return err; +} + +static enum ipc_err +ipc_buf_req_code(struct ipc *ipc, struct io_buffer *iob) +{ + enum ipc_err err; + char *res; + uint32_t rlen; + + if ((err = ipc_buf_req_res(ipc, iob, &res, &rlen)) == 0) { + err = benc_dget_int(res, "code"); + free(res); + } + return err; +} + +enum ipc_err +btpd_die(struct ipc *ipc, int seconds) +{ + struct io_buffer iob; + buf_init(&iob, 16); + if (seconds >= 0) + buf_print(&iob, "l3:diei%dee", seconds); + else + buf_swrite(&iob, "l3:diee"); + return ipc_buf_req_code(ipc, &iob); +} + +static enum ipc_err +tget_common(char *ans, enum ipc_tval *keys, size_t nkeys, tget_cb_t cb, + void *arg) +{ + int err; + const char *res; + struct ipc_get_res cbres[IPC_TVALCOUNT]; + + if ((err = benc_dget_int(ans, "code")) != 0) + return err; + + res = benc_dget_lst(ans, "result"); + int obji = 0; + for (res = benc_first(res); res != NULL; res = benc_next(res)) { + if (benc_isint(res)) { + cb(obji, benc_int(res, NULL), NULL, arg); + obji++; + continue; + } + const char *t = benc_first(res); + const char *v = benc_next(t); + for (int j = 0; j < nkeys; j++) { + cbres[keys[j]].type = benc_int(t, NULL); + switch (cbres[keys[j]].type) { + case IPC_TYPE_ERR: + case IPC_TYPE_NUM: + cbres[keys[j]].v.num = benc_int(v, NULL); + break; + case IPC_TYPE_STR: + case IPC_TYPE_BIN: + cbres[keys[j]].v.str.p= benc_mem(v, &cbres[keys[j]].v.str.l, + NULL); + break; + } + t = benc_next(v); + if (t != NULL) + v = benc_next(t); + } + cb(obji, IPC_OK, cbres, arg); + obji++; + } + + free(ans); + return IPC_OK; +} + +enum ipc_err +btpd_tget(struct ipc *ipc, struct ipc_torrent *tps, size_t ntps, + enum ipc_tval *keys, size_t nkeys, tget_cb_t cb, void *arg) +{ + char *res; + uint32_t rlen; + enum ipc_err err; + struct io_buffer iob; + + if (nkeys == 0 || ntps == 0) + return IPC_COMMERR; + + buf_init(&iob, 1 << 14); + buf_swrite(&iob, "l4:tgetd4:froml"); + for (int i = 0; i < ntps; i++) { + if (tps[i].by_hash) { + buf_swrite(&iob, "20:"); + buf_write(&iob, tps[i].u.hash, 20); + } else + buf_print(&iob, "i%ue", tps[i].u.num); + } + buf_swrite(&iob, "e4:keysl"); + for (int k = 0; k < nkeys; k++) + buf_print(&iob, "i%de", keys[k]); + buf_swrite(&iob, "eee"); + + if ((err = ipc_buf_req_res(ipc, &iob, &res, &rlen)) == 0) + err = tget_common(res, keys, nkeys, cb, arg); + return err; +} + +enum ipc_err +btpd_tget_wc(struct ipc *ipc, enum ipc_twc twc, enum ipc_tval *keys, + size_t nkeys, tget_cb_t cb, void *arg) +{ + char *res; + uint32_t rlen; + struct io_buffer iob; + enum ipc_err err; + + if (nkeys == 0) + return IPC_COMMERR; + + buf_init(&iob, 1 << 14); + buf_print(&iob, "l4:tgetd4:fromi%de4:keysl", twc); + for (int i = 0; i < nkeys; i++) + buf_print(&iob, "i%de", keys[i]); + buf_swrite(&iob, "eee"); + + if ((err = ipc_buf_req_res(ipc, &iob, &res, &rlen)) == 0) + err = tget_common(res, keys, nkeys, cb, arg); + return err; +} + +enum ipc_err +btpd_add(struct ipc *ipc, const char *mi, size_t mi_size, const char *content, + const char *name) +{ + struct io_buffer iob; + buf_init(&iob, (1 << 10)); + buf_print(&iob, "l3:addd7:content%d:%s", (int)strlen(content), + content); + if (name != NULL) + buf_print(&iob, "4:name%d:%s", (int)strlen(name), name); + buf_print(&iob, "7:torrent%lu:", (unsigned long)mi_size); + buf_write(&iob, mi, mi_size); + buf_swrite(&iob, "ee"); + return ipc_buf_req_code(ipc, &iob); +} + +static enum ipc_err +simple_treq(struct ipc *ipc, char *cmd, struct ipc_torrent *tp) +{ + struct io_buffer iob; + buf_init(&iob, 32); + if (tp->by_hash) { + buf_print(&iob, "l%d:%s20:", (int)strlen(cmd), cmd); + buf_write(&iob, tp->u.hash, 20); + buf_swrite(&iob, "e"); + } else + buf_print(&iob, "l%d:%si%uee", (int)strlen(cmd), cmd, tp->u.num); + return ipc_buf_req_code(ipc, &iob); +} + +enum ipc_err +btpd_del(struct ipc *ipc, struct ipc_torrent *tp) +{ + return simple_treq(ipc, "del", tp); +} + +enum ipc_err +btpd_start(struct ipc *ipc, struct ipc_torrent *tp) +{ + return simple_treq(ipc, "start", tp); +} + +enum ipc_err +btpd_stop(struct ipc *ipc, struct ipc_torrent *tp) +{ + return simple_treq(ipc, "stop", tp); +} diff --git a/misc/btpd_if.h b/misc/btpd_if.h new file mode 100644 index 0000000..ed0d9ba --- /dev/null +++ b/misc/btpd_if.h @@ -0,0 +1,92 @@ +#ifndef BTPD_IPC_H +#define BTPD_IPC_H + +enum ipc_err { +#define ERRDEF(name, msg) IPC_##name, +#include "ipcdefs.h" +#undef ERRDEF + IPC_ERRCOUNT +}; + +enum ipc_type { + IPC_TYPE_ERR, + IPC_TYPE_BIN, + IPC_TYPE_NUM, + IPC_TYPE_STR +}; + +enum ipc_tval { +#define TVDEF(val, type, name) IPC_TVAL_##val, +#include "ipcdefs.h" +#undef TVDEF + IPC_TVALCOUNT +}; + +enum ipc_dval { + IPC_DVAL_MIN, + IPC_DVAL_MAX +}; + +enum ipc_twc { + IPC_TWC_ALL, + IPC_TWC_ACTIVE, + IPC_TWC_INACTIVE +}; + +enum ipc_tstate { + IPC_TSTATE_INACTIVE, + IPC_TSTATE_START, + IPC_TSTATE_STOP, + IPC_TSTATE_LEECH, + IPC_TSTATE_SEED +}; + +#ifndef DAEMON + +struct ipc; + +struct ipc_get_res { + enum ipc_type type; + union { + struct { + const char *p; + size_t l; + } str; + long long num; + } v; +}; + +struct ipc_torrent { + int by_hash; + union { + unsigned num; + uint8_t hash[20]; + } u; +}; + +typedef void (*tget_cb_t)(int obji, enum ipc_err objerr, + struct ipc_get_res *res, void *arg); + +//typedef void (*dget_cb_t)(struct ipc_get_res *res, size_t nres, void *arg); + +int ipc_open(const char *dir, struct ipc **out); +void ipc_close(struct ipc *ipc); + +const char *ipc_strerror(enum ipc_err err); + +enum ipc_err btpd_add(struct ipc *ipc, const char *mi, size_t mi_size, + const char *content, const char *name); +enum ipc_err btpd_del(struct ipc *ipc, struct ipc_torrent *tp); +enum ipc_err btpd_start(struct ipc *ipc, struct ipc_torrent *tp); +enum ipc_err btpd_stop(struct ipc *ipc, struct ipc_torrent *tp); +enum ipc_err btpd_die(struct ipc *ipc, int seconds); +enum ipc_err btpd_get(struct ipc *ipc, enum ipc_dval *keys, size_t nkeys, + tget_cb_t cb, void *arg); +enum ipc_err btpd_tget(struct ipc *ipc, struct ipc_torrent *tps, size_t ntps, + enum ipc_tval *keys, size_t nkeys, tget_cb_t cb, void *arg); +enum ipc_err btpd_tget_wc(struct ipc *ipc, enum ipc_twc, enum ipc_tval *keys, + size_t nkeys, tget_cb_t cb, void *arg); + +#endif + +#endif