|
|
@@ -1,313 +0,0 @@ |
|
|
|
#include <sys/types.h> |
|
|
|
#include <sys/socket.h> |
|
|
|
#include <sys/un.h> |
|
|
|
|
|
|
|
#include <assert.h> |
|
|
|
#include <ctype.h> |
|
|
|
#include <err.h> |
|
|
|
#include <errno.h> |
|
|
|
#include <inttypes.h> |
|
|
|
#include <stdio.h> |
|
|
|
#include <stdlib.h> |
|
|
|
#include <string.h> |
|
|
|
#include <unistd.h> |
|
|
|
|
|
|
|
#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); |
|
|
|
} |