about summary refs log tree commit diff
path: root/cli
diff options
context:
space:
mode:
Diffstat (limited to 'cli')
-rw-r--r--cli/btpd_if.c307
-rw-r--r--cli/btpd_if.h114
2 files changed, 277 insertions, 144 deletions
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 <sys/socket.h>
 #include <sys/un.h>
 
+#include <assert.h>
 #include <ctype.h>
 #include <err.h>
 #include <errno.h>
@@ -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