Преглед на файлове

The btpd interface now consists of these commands:

add   - adds a torrent to btpd's library
del   - removes a torrent from btpd's library
start - activates a torrent from the library
stop  - deactivates an active torrent
tget  - get data from torrents

tget obsoletes stat and list.

Commands for setting values and query other data will be
added later.
master
Richard Nyberg преди 18 години
родител
ревизия
d3f404c07e
променени са 2 файла, в които са добавени 277 реда и са изтрити 144 реда
  1. +196
    -111
      cli/btpd_if.c
  2. +81
    -33
      cli/btpd_if.h

+ 196
- 111
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);
}

+ 81
- 33
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

Loading…
Отказ
Запис