diff --git a/cli/btpd_if.c b/cli/btpd_if.c index 241a1c0..8bd0f21 100644 --- a/cli/btpd_if.c +++ b/cli/btpd_if.c @@ -2,6 +2,7 @@ #include #include +#include #include #include #include @@ -20,6 +21,36 @@ 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) { @@ -52,76 +83,76 @@ ipc_open(const char *dir, struct ipc **out) return 0; } -int +void ipc_close(struct ipc *ipc) { - int err; - err = close(ipc->sd); + close(ipc->sd); free(ipc); - return err; } -static int +static enum ipc_err ipc_response(struct ipc *ipc, char **out, uint32_t *len) { uint32_t size; char *buf; - if ((errno = read_fully(ipc->sd, &size, sizeof(size))) != 0) - return errno; - + if (read_fully(ipc->sd, &size, sizeof(size)) != 0) + return IPC_COMMERR; if (size == 0) - return ECONNRESET; - + return IPC_COMMERR; if ((buf = malloc(size)) == NULL) - return ENOMEM; - - if ((errno = read_fully(ipc->sd, buf, size)) != 0) { + return IPC_COMMERR; + if (read_fully(ipc->sd, buf, size) != 0) { free(buf); - return errno; + return IPC_COMMERR; } - *out = buf; *len = size; - return 0; + return IPC_OK; } -static int +static enum ipc_err ipc_req_res(struct ipc *ipc, const char *req, uint32_t qlen, char **res, uint32_t *rlen) { - if ((errno = write_fully(ipc->sd, &qlen, sizeof(qlen))) != 0) - goto error; - if ((errno = write_fully(ipc->sd, req, qlen)) != 0) - goto error; - if ((errno = ipc_response(ipc, res, rlen)) != 0) - goto error; - if ((errno = benc_validate(*res, *rlen)) != 0) - goto error; + 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)) - errno = EINVAL; -error: - return errno; + return IPC_COMMERR; + return IPC_OK; } -static enum ipc_code -ipc_buf_req(struct ipc *ipc, struct io_buffer *iob) +static enum ipc_err +ipc_buf_req_res(struct ipc *ipc, struct io_buffer *iob, char **res, + uint32_t *rlen) { - int err; + 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 reslen; + uint32_t rlen; - err = ipc_req_res(ipc, iob->buf, iob->buf_off, &res, &reslen); - free(iob->buf); - if (err != 0) - return IPC_COMMERR; - int code; - code = benc_dget_int(res, "code"); - free(res); - return code; + if ((err = ipc_buf_req_res(ipc, iob, &res, &rlen)) == 0) { + err = benc_dget_int(res, "code"); + free(res); + } + return err; } -enum ipc_code +enum ipc_err btpd_die(struct ipc *ipc, int seconds) { struct io_buffer iob; @@ -130,99 +161,153 @@ btpd_die(struct ipc *ipc, int seconds) buf_print(&iob, "l3:diei%dee", seconds); else buf_swrite(&iob, "l3:diee"); - return ipc_buf_req(ipc, &iob); -} - -enum ipc_code -parse_btstat(const uint8_t *res, struct btstat **out) -{ - int code; - unsigned ntorrents; - const char *tlst; - - code = benc_dget_int(res, "code"); - if (code != IPC_OK) - return code; - - ntorrents = benc_dget_int(res, "ntorrents"); - tlst = benc_dget_lst(res, "torrents"); - - struct btstat *st = - malloc(sizeof(struct btstat) + sizeof(struct tpstat) * ntorrents); - - st->ntorrents = ntorrents; - int i = 0; - for (const char *tp = benc_first(tlst); tp != NULL; tp = benc_next(tp)) { - struct tpstat *ts = &st->torrents[i]; - ts->hash = benc_dget_mema(tp, "info hash", NULL); - ts->name = benc_dget_str(tp, "name", NULL); - ts->state = benc_dget_int(tp, "state"); - ts->peers = benc_dget_int(tp, "peers"); - ts->tr_errors = benc_dget_int(tp, "tracker errors"); - ts->content_got = benc_dget_int(tp, "content got"); - ts->content_size = benc_dget_int(tp, "content size"); - ts->pieces_got = benc_dget_int(tp, "pieces got"); - ts->pieces_seen = benc_dget_int(tp, "pieces seen"); - ts->torrent_pieces = benc_dget_int(tp, "torrent pieces"); - ts->downloaded = benc_dget_int(tp, "downloaded"); - ts->uploaded = benc_dget_int(tp, "uploaded"); - ts->rate_down = benc_dget_int(tp, "rate down"); - ts->rate_up = benc_dget_int(tp, "rate up"); - i++; + 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++; } - *out = st; + + free(ans); return IPC_OK; } -void -free_btstat(struct btstat *st) +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) { - for (unsigned i = 0; i < st->ntorrents; i++) { - if (st->torrents[i].hash != NULL) - free(st->torrents[i].hash); - if (st->torrents[i].name != NULL) - free(st->torrents[i].name); + 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); } - free(st); + 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_code -btpd_stat(struct ipc *ipc, struct btstat **out) +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) { - int err; - const char cmd[] = "l4:state"; - uint32_t cmdlen = sizeof(cmd) - 1; char *res; - uint32_t reslen; + uint32_t rlen; + struct io_buffer iob; + enum ipc_err err; - if ((err = ipc_req_res(ipc, cmd, cmdlen, &res, &reslen)) != 0) + if (nkeys == 0) return IPC_COMMERR; - err = parse_btstat(res, out); - free(res); + 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_code -btpd_add(struct ipc *ipc, const uint8_t *hash, const char *torrent, - const char *content) +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:%s4:hash20:", (int)strlen(content), + buf_print(&iob, "l3:addd7:content%d:%s", (int)strlen(content), content); - buf_write(&iob, hash, 20); - buf_print(&iob, "7:torrent%d:%see", (int)strlen(torrent), torrent); - return ipc_buf_req(ipc, &iob); + 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); } -enum ipc_code -btpd_del(struct ipc *ipc, const uint8_t *hash) +static enum ipc_err +simple_treq(struct ipc *ipc, char *cmd, struct ipc_torrent *tp) { struct io_buffer iob; buf_init(&iob, 32); - buf_swrite(&iob, "l3:del20:"); - buf_write(&iob, hash, 20); - buf_write(&iob, "e", 1); - return ipc_buf_req(ipc, &iob); + 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/cli/btpd_if.h b/cli/btpd_if.h index 1cc51d9..ed0d9ba 100644 --- a/cli/btpd_if.h +++ b/cli/btpd_if.h @@ -1,44 +1,92 @@ -#ifndef BTPD_IF_H -#define BTPD_IF_H +#ifndef BTPD_IPC_H +#define BTPD_IPC_H -struct ipc; +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 torrent_state { //XXX: Same as in btpd/torrent.h - T_STARTING, - T_ACTIVE, - T_STOPPING +enum ipc_dval { + IPC_DVAL_MIN, + IPC_DVAL_MAX }; -enum ipc_code { - IPC_OK, - IPC_FAIL, - IPC_ERROR, - IPC_COMMERR +enum ipc_twc { + IPC_TWC_ALL, + IPC_TWC_ACTIVE, + IPC_TWC_INACTIVE }; -struct btstat { - unsigned ntorrents; - struct tpstat { - uint8_t *hash; - char *name; - enum torrent_state state; - unsigned tr_errors; - unsigned peers; - uint32_t pieces_got, pieces_seen, torrent_pieces; - off_t content_got, content_size; - unsigned long long downloaded, uploaded; - unsigned long rate_up, rate_down; - } torrents[]; +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); -int ipc_close(struct ipc *ipc); - -enum ipc_code btpd_add(struct ipc *ipc, const uint8_t *hash, - const char *torrent, const char *content); -enum ipc_code btpd_del(struct ipc *ipc, const uint8_t *hash); -enum ipc_code btpd_die(struct ipc *ipc, int seconds); -enum ipc_code btpd_stat(struct ipc *ipc, struct btstat **out); -void free_btstat(struct btstat *stat); +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