Pārlūkot izejas kodu

o Changed the benc_ api to make it easier to use.

o Lot of work on the cli and its communication with btpd.
master
Richard Nyberg pirms 18 gadiem
vecāks
revīzija
01c92051d5
8 mainītis faili ar 949 papildinājumiem un 948 dzēšanām
  1. +172
    -88
      btpd/cli_if.c
  2. +33
    -47
      btpd/tracker_req.c
  3. +316
    -481
      cli/btcli.c
  4. +144
    -140
      cli/btpd_if.c
  5. +31
    -8
      cli/btpd_if.h
  6. +203
    -120
      misc/benc.c
  7. +26
    -24
      misc/benc.h
  8. +24
    -40
      misc/metainfo.c

+ 172
- 88
btpd/cli_if.c Parādīt failu

@@ -12,58 +12,80 @@
#include <unistd.h>

#include "btpd.h"
#include "tracker_req.h"

struct cli {
int sd;
struct event read;
};

#define buf_swrite(iob, s) buf_write(iob, s, sizeof(s) - 1)

static struct event m_cli_incoming;

static void
errdie(int error)
{
if (error != 0)
btpd_err("io_buf: %s.\n", strerror(error));
}

static void
cmd_stat(int argc, const char *args, FILE *fp)
static int
cmd_stat(struct cli *cli, int argc, const char *args)
{
struct torrent *tp;
struct io_buffer iob;
errdie(buf_init(&iob, (1 << 14)));
buf_init(&iob, (1 << 14));

errdie(buf_swrite(&iob, "d"));
errdie(buf_print(&iob, "6:npeersi%ue", net_npeers));
errdie(buf_print(&iob, "9:ntorrentsi%ue", btpd_get_ntorrents()));
errdie(buf_swrite(&iob, "8:torrentsl"));
buf_swrite(&iob, "d");
buf_swrite(&iob, "4:codei0e");
buf_print(&iob, "6:npeersi%ue", net_npeers);
buf_print(&iob, "9:ntorrentsi%ue", btpd_get_ntorrents());
buf_swrite(&iob, "8:torrentsl");
BTPDQ_FOREACH(tp, btpd_get_torrents(), entry) {
if (tp->state != T_ACTIVE)
continue;

uint32_t seen_npieces = 0;
for (uint32_t i = 0; i < tp->meta.npieces; i++)
if (tp->net->piece_count[i] > 0)
seen_npieces++;
errdie(buf_print(&iob, "d4:downi%jue", (intmax_t)tp->net->downloaded));
errdie(buf_swrite(&iob, "4:hash20:"));
errdie(buf_write(&iob, tp->meta.info_hash, 20));
errdie(buf_print(&iob, "4:havei%jde", (intmax_t)cm_get_size(tp)));
errdie(buf_print(&iob, "6:npeersi%ue", tp->net->npeers));
errdie(buf_print(&iob, "7:npiecesi%ue", tp->meta.npieces));
errdie(buf_print(&iob, "4:path%d:%s",
(int)strlen(tp->relpath), tp->relpath));
errdie(buf_print(&iob, "2:rdi%lue", tp->net->rate_dwn));
errdie(buf_print(&iob, "2:rui%lue", tp->net->rate_up));
errdie(buf_print(&iob, "12:seen npiecesi%ue", seen_npieces));
errdie(buf_print(&iob, "5:totali%jde",
(intmax_t)tp->meta.total_length));
errdie(buf_print(&iob, "2:upi%juee", (intmax_t)tp->net->uploaded));
if (tp->state == T_ACTIVE) {
uint32_t seen_npieces = 0;
for (uint32_t i = 0; i < tp->meta.npieces; i++)
if (tp->net->piece_count[i] > 0)
seen_npieces++;

buf_print(&iob, "d4:downi%jue", (intmax_t)tp->net->downloaded);
buf_print(&iob, "6:errorsi%ue", tr_errors(tp));
buf_swrite(&iob, "4:hash20:");
buf_write(&iob, tp->meta.info_hash, 20);
buf_print(&iob, "4:havei%jde", (intmax_t)cm_get_size(tp));
buf_print(&iob, "6:npeersi%ue", tp->net->npeers);
buf_print(&iob, "7:npiecesi%ue", tp->meta.npieces);
buf_print(&iob, "3:numi%ue", tp->num);
buf_print(&iob, "4:path%d:%s", (int)strlen(tp->meta.name),
tp->meta.name);
buf_print(&iob, "2:rdi%lue", tp->net->rate_dwn);
buf_print(&iob, "2:rui%lue", tp->net->rate_up);
buf_print(&iob, "12:seen npiecesi%ue", seen_npieces);
buf_swrite(&iob, "5:state1:A");
buf_print(&iob, "5:totali%jde", (intmax_t)tp->meta.total_length);
buf_print(&iob, "2:upi%juee", (intmax_t)tp->net->uploaded);
} else {
buf_swrite(&iob, "d4:hash20:");
buf_write(&iob, tp->meta.info_hash, 20);
buf_print(&iob, "3:numi%ue", tp->num);
buf_print(&iob, "4:path%d:%s", (int)strlen(tp->meta.name),
tp->meta.name);
switch (tp->state) {
case T_INACTIVE:
buf_swrite(&iob, "5:state1:Ie");
break;
case T_STARTING:
buf_swrite(&iob, "5:state1:Be");
break;
case T_STOPPING:
buf_swrite(&iob, "5:state1:Ee");
break;
case T_ACTIVE:
abort();
}
}
}
errdie(buf_swrite(&iob, "ee"));
buf_swrite(&iob, "ee");

uint32_t len = iob.buf_off;
fwrite(&len, sizeof(len), 1, fp);
fwrite(iob.buf, 1, iob.buf_off, fp);
write_fully(cli->sd, &len, sizeof(len));
write_fully(cli->sd, iob.buf, iob.buf_off);
free(iob.buf);
return 0;
}

#if 0
@@ -71,9 +93,9 @@ static void
cmd_add(int argc, const char *args, FILE *fp)
{
struct io_buffer iob;
errdie(buf_init(&iob, (1 << 10)));
buf_init(&iob, (1 << 10));

errdie(buf_write(&iob, "l", 1));
buf_write(&iob, "l", 1);
while (args != NULL) {
size_t plen;
char path[PATH_MAX];
@@ -87,16 +109,16 @@ cmd_add(int argc, const char *args, FILE *fp)
benc_str(args, &pathp, &plen, &args);

if (plen >= PATH_MAX) {
errdie(buf_print(&iob, "d4:codei%dee", ENAMETOOLONG));
buf_print(&iob, "d4:codei%dee", ENAMETOOLONG);
continue;
}

bcopy(pathp, path, plen);
path[plen] = '\0';
btpd_log(BTPD_L_BTPD, "add request for %s.\n", path);
errdie(buf_print(&iob, "d4:codei%dee", torrent_load(path)));
buf_print(&iob, "d4:codei%dee", torrent_load(path));
}
errdie(buf_write(&iob, "e", 1));
buf_write(&iob, "e", 1);

uint32_t len = iob.buf_off;
fwrite(&len, sizeof(len), 1, fp);
@@ -108,9 +130,9 @@ static void
cmd_del(int argc, const char *args, FILE *fp)
{
struct io_buffer iob;
errdie(buf_init(&iob, (1 << 10)));
buf_init(&iob, (1 << 10));

errdie(buf_swrite(&iob, "l"));
buf_swrite(&iob, "l");

while (args != NULL) {
size_t len;
@@ -127,13 +149,13 @@ cmd_del(int argc, const char *args, FILE *fp)
if (tp != NULL) {
btpd_log(BTPD_L_BTPD, "del request for %s.\n", tp->relpath);
torrent_unload(tp);
errdie(buf_swrite(&iob, "d4:codei0ee"));
buf_swrite(&iob, "d4:codei0ee");
} else {
btpd_log(BTPD_L_BTPD, "del request didn't match.\n");
errdie(buf_print(&iob, "d4:codei%dee", ENOENT));
buf_print(&iob, "d4:codei%dee", ENOENT);
}
}
errdie(buf_swrite(&iob, "e"));
buf_swrite(&iob, "e");

uint32_t len = iob.buf_off;
fwrite(&len, sizeof(len), 1, fp);
@@ -141,78 +163,144 @@ cmd_del(int argc, const char *args, FILE *fp)
free(iob.buf);
}

static void
cmd_die(int argc, const char *args, FILE *fp)
#endif

static int
cmd_die(struct cli *cli, int argc, const char *args)
{
char res[] = "d4:codei0ee";
uint32_t len = sizeof(res) - 1;
fwrite(&len, sizeof(len), 1, fp);
fwrite(res, 1, len, fp);
fflush(fp);
write_fully(cli->sd, &len, sizeof(len));
write_fully(cli->sd, res, len);
btpd_log(BTPD_L_BTPD, "Someone wants me dead.\n");
btpd_shutdown();
btpd_shutdown((& (struct timeval) { 0, 0 }));
return 0;
}

static int
cmd_start(struct cli *cli, int argc, const char *args)
{
if (argc != 1 || !benc_isint(args))
return EINVAL;

int code;
unsigned num;
num = benc_int(args, NULL);
struct torrent *tp = btpd_get_torrent_num(num);
if (tp != NULL) {
torrent_activate(tp);
code = 0;
} else
code = 1;

struct io_buffer iob;
buf_init(&iob, 16);
buf_print(&iob, "d4:codei%dee", code);
uint32_t len = iob.buf_off;
write_fully(cli->sd, &len, sizeof(len));
write_fully(cli->sd, iob.buf, iob.buf_off);
return 0;
}

static int
cmd_stop(struct cli *cli, int argc, const char *args)
{
btpd_log(BTPD_L_BTPD, "%d\n", argc);
if (argc != 1 || !benc_isint(args))
return EINVAL;

int code;
unsigned num;
num = benc_int(args, NULL);
struct torrent *tp = btpd_get_torrent_num(num);
if (tp != NULL) {
torrent_deactivate(tp);
code = 0;
} else
code = 1;

struct io_buffer iob;
buf_init(&iob, 16);
buf_print(&iob, "d4:codei%dee", code);
uint32_t len = iob.buf_off;
write_fully(cli->sd, &len, sizeof(len));
write_fully(cli->sd, iob.buf, iob.buf_off);
return 0;
}
#endif

static struct {
const char *name;
int nlen;
void (*fun)(int, const char *, FILE *);
int (*fun)(struct cli *cli, int, const char *);
} cmd_table[] = {
#if 0
{ "add", 3, cmd_add },
{ "del", 3, cmd_del },
{ "die", 3, cmd_die },
#endif
{ "stat", 4, cmd_stat }
{ "die", 3, cmd_die },
{ "start", 5, cmd_start },
{ "stat", 4, cmd_stat },
{ "stop", 4, cmd_stop }
};

static int ncmds = sizeof(cmd_table) / sizeof(cmd_table[0]);

static void
cmd_dispatch(const char *buf, FILE *fp)
static int
cmd_dispatch(struct cli *cli, const char *buf)
{
int err = 0;
size_t cmdlen;
const char *cmd;
const char *args;
int found = 0;

benc_str(benc_first(buf), &cmd, &cmdlen, &args);
cmd = benc_mem(benc_first(buf), &cmdlen, &args);

for (int i = 0; !found && i < ncmds; i++) {
if (cmdlen == cmd_table[i].nlen &&
strncmp(cmd_table[i].name, cmd, cmdlen) == 0) {
cmd_table[i].fun(benc_nelems(buf) - 1, args, fp);
found = 1;
btpd_log(BTPD_L_BTPD, "%.*s\n", (int)cmdlen, cmd);
for (int i = 0; i < ncmds; i++) {
if ((cmdlen == cmd_table[i].nlen &&
strncmp(cmd_table[i].name, cmd, cmdlen) == 0)) {
err = cmd_table[i].fun(cli, benc_nelems(buf) - 1, args);
break;
}
}
return err;
}

static void
do_ipc(FILE *fp)
cli_read_cb(int sd, short type, void *arg)
{
uint32_t cmdlen, nread;
char *buf;
struct cli *cli = arg;
uint32_t cmdlen;
uint8_t *msg = NULL;

if (fread(&cmdlen, sizeof(cmdlen), 1, fp) != 1)
return;
if (read_fully(sd, &cmdlen, sizeof(cmdlen)) != 0)
goto error;

buf = btpd_malloc(cmdlen);
msg = btpd_malloc(cmdlen);
if (read_fully(sd, msg, cmdlen) != 0)
goto error;

if ((nread = fread(buf, 1, cmdlen, fp)) == cmdlen) {
if (benc_validate(buf, cmdlen) == 0 && benc_islst(buf) &&
benc_first(buf) != NULL && benc_isstr(benc_first(buf)))
cmd_dispatch(buf, fp);
}
if (!(benc_validate(msg, cmdlen) == 0 && benc_islst(msg) &&
benc_first(msg) != NULL && benc_isstr(benc_first(msg))))
goto error;

free(buf);
if (cmd_dispatch(cli, msg) != 0)
goto error;

event_add(&cli->read, NULL);
return;

error:
close(cli->sd);
free(cli);
if (msg != NULL)
free(msg);
}

void
client_connection_cb(int sd, short type, void *arg)
{
int nsd;
FILE *fp;

if ((nsd = accept(sd, NULL, NULL)) < 0) {
if (errno == EWOULDBLOCK || errno == ECONNABORTED)
@@ -224,14 +312,10 @@ client_connection_cb(int sd, short type, void *arg)
if ((errno = set_blocking(nsd)) != 0)
btpd_err("set_blocking: %s.\n", strerror(errno));

if ((fp = fdopen(nsd, "r+")) == NULL) {
close(nsd);
return;
}

do_ipc(fp);

fclose(fp);
struct cli *cli = btpd_calloc(1, sizeof(*cli));
cli->sd = nsd;
event_set(&cli->read, cli->sd, EV_READ, cli_read_cb, cli);
event_add(&cli->read, NULL);
}

void


+ 33
- 47
btpd/tracker_req.c Parādīt failu

@@ -40,15 +40,12 @@ static void tr_send(struct torrent *tp, enum tr_event event);
void
maybe_connect_to(struct torrent *tp, const char *pinfo)
{
const char *pid = NULL;
char *ip = NULL;
int64_t port;
const char *pid;
char *ip;
int port;
size_t len;

if (!benc_isdct(pinfo))
return;

if (benc_dget_str(pinfo, "peer id", &pid, &len) != 0 || len != 20)
if ((pid = benc_dget_mem(pinfo, "peer id", &len)) == NULL || len != 20)
return;

if (bcmp(btpd_get_peer_id(), pid, 20) == 0)
@@ -56,71 +53,60 @@ maybe_connect_to(struct torrent *tp, const char *pinfo)

if (net_torrent_has_peer(tp->net, pid))
return;
if ((ip = benc_dget_str(pinfo, "ip", NULL)) == NULL)
return;

if (benc_dget_strz(pinfo, "ip", &ip, NULL) != 0)
goto out;

if (benc_dget_int64(pinfo, "port", &port) != 0)
goto out;

port = benc_dget_int(pinfo, "port");
peer_create_out(tp->net, pid, ip, port);

out:
if (ip != NULL)
free(ip);
}


static int
parse_reply(struct torrent *tp, const char *content, size_t size)
{
char *buf;
const char *buf;
size_t len;
const char *peers;
uint32_t interval;
int interval;

if (benc_validate(content, size) != 0 || !benc_isdct(content)) {
btpd_log(BTPD_L_ERROR, "Bad data from tracker.\n");
if ((buf = benc_dget_mem(content, "failure reason", &len)) != NULL) {
btpd_log(BTPD_L_ERROR, "Tracker failure: %.*s.\n", (int)len, buf);
return 1;
}

if ((benc_dget_strz(content, "failure reason", &buf, NULL)) == 0) {
btpd_log(BTPD_L_ERROR, "Tracker failure: %s.\n", buf);
free(buf);
return 1;
}
if ((benc_validate(content, size) != 0 ||
!benc_dct_chk(content, 2, BE_INT, 1, "interval",
BE_ANY, 1, "peers")))
goto bad_data;

if ((benc_dget_uint32(content, "interval", &interval)) != 0) {
btpd_log(BTPD_L_ERROR, "Bad data from tracker.\n");
return 1;
}
interval = benc_dget_int(content, "interval");
if (interval < 1)
goto bad_data;

tp->tr->interval = interval;
btpd_log(BTPD_L_BTPD, "Got interval %d.\n", interval);

int error = 0;
size_t length;
peers = benc_dget_any(content, "peers");

if ((error = benc_dget_lst(content, "peers", &peers)) == 0) {
if (benc_islst(peers)) {
for (peers = benc_first(peers);
peers != NULL && net_npeers < net_max_peers;
peers = benc_next(peers))
maybe_connect_to(tp, peers);
}

if (error == EINVAL) {
error = benc_dget_str(content, "peers", &peers, &length);
if (error == 0 && length % 6 == 0) {
size_t i;
for (i = 0; i < length && net_npeers < net_max_peers; i += 6)
peer_create_out_compact(tp->net, peers + i);
}
}

if (error != 0) {
btpd_log(BTPD_L_ERROR, "Bad data from tracker.\n");
return 1;
}
} else if (benc_isstr(peers)) {
peers = benc_dget_mem(content, "peers", &len);
for (size_t i = 0; i < len && net_npeers < net_max_peers; i += 6)
peer_create_out_compact(tp->net, peers + i);
} else
goto bad_data;

return 0;

bad_data:
btpd_log(BTPD_L_ERROR, "Bad data from tracker.\n");
return 1;
}

static void


+ 316
- 481
cli/btcli.c Parādīt failu

@@ -1,587 +1,422 @@
#include <sys/types.h>
#include <sys/time.h>

#include <err.h>
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <inttypes.h>
#include <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <openssl/sha.h>

#include "benc.h"
#include "metainfo.h"
#include "stream.h"
#include "subr.h"
#include "btpd_if.h"

static void
usage()
{
printf("Usage: btcli command [options] [files]\n"
"Commands:\n"
"add <file_1> ... [file_n]\n"
"\tAdd the given torrents to btpd.\n"
"\n"
"del <file_1> ... [file_n]\n"
"\tRemove the given torrents from btpd.\n"
"\n"
"die\n"
"\tShut down btpd.\n"
"\n"
"list\n"
"\tList active torrents.\n"
"\n"
"stat [-i] [-w n] [file_1] ... [file_n]\n"
"\tShow stats for either all active or the given torrents.\n"
"\tThe stats displayed are:\n"
"\t%% of pieces seen, %% of pieces verified, \n"
"\tMB down, rate down, MB up, rate up, no peers\n"
"-i\n"
"\tShow stats per torrent in addition to total stats.\n"
"-w n\n"
"\tRepeat every n seconds.\n"
"\n"
"Common options:\n"
"--ipc key\n"
"\tTalk to the btpd started with the same key.\n"
"\n"
"--help\n"
"\tShow this help.\n"
"\n");
exit(1);
}
static const char *btpd_dir = "/usr/btpd";
static struct ipc *ipc;

static void
handle_error(int error)
handle_ipc_res(enum ipc_code code)
{
switch (error) {
case 0:
switch (code) {
case IPC_OK:
return;
case IPC_FAIL:
warnx("Ipc failed.\n");
break;
case ENOENT:
case ECONNREFUSED:
errx(1, "Couldn't connect. Check that btpd is running.");
default:
errx(1, "%s", strerror(error));
case IPC_COMMERR:
errx(1, "Communication error.\n");
}
}

static void
do_ipc_open(char *ipctok, struct ipc **ipc)
{
switch (ipc_open(ipctok, ipc)) {
case 0:
break;
case EINVAL:
errx(1, "--ipc argument only takes letters and digits.");
case ENAMETOOLONG:
errx(1, "--ipc argument is too long.");
}
}

struct cb {
char *path;
uint8_t *piece_field;
uint32_t have;
struct metainfo *meta;
};

static void
hash_cb(uint32_t index, uint8_t *hash, void *arg)
{
struct cb *cb = arg;
if (hash != NULL)
if (bcmp(hash, cb->meta->piece_hash[index], SHA_DIGEST_LENGTH) == 0) {
set_bit(cb->piece_field, index);
cb->have++;
}
printf("\rTested: %5.1f%%", 100.0 * (index + 1) / cb->meta->npieces);
fflush(stdout);
}

static int
fd_cb(const char *path, int *fd, void *arg)
{
struct cb *fp = arg;
return vopen(fd, O_RDONLY, "%s.d/%s", fp->path, path);
}

static void
gen_ifile(char *path)
btpd_connect(void)
{
int fd;
struct cb cb;
struct metainfo *mi;
size_t field_len;

if ((errno = load_metainfo(path, -1, 1, &mi)) != 0)
err(1, "load_metainfo: %s", path);

field_len = ceil(mi->npieces / 8.0);
cb.path = path;
cb.piece_field = calloc(1, field_len);
cb.have = 0;
cb.meta = mi;

if (cb.piece_field == NULL)
errx(1, "Out of memory.\n");

if ((errno = bts_hashes(mi, fd_cb, hash_cb, &cb)) != 0)
err(1, "bts_hashes");
printf("\nHave: %5.1f%%\n", 100.0 * cb.have / cb.meta->npieces);

if ((errno = vopen(&fd, O_WRONLY|O_CREAT, "%s.i", path)) != 0)
err(1, "opening %s.i", path);

if (ftruncate(fd, field_len + mi->npieces *
(off_t)ceil(mi->piece_length / (double)(1 << 17))) < 0)
err(1, "ftruncate: %s", path);

if (write(fd, cb.piece_field, field_len) != field_len)
err(1, "write %s.i", path);

if (close(fd) < 0)
err(1, "close %s.i", path);

clear_metainfo(mi);
free(mi);
if ((errno = ipc_open(btpd_dir, &ipc)) != 0)
errx(1, "Couldn't connect to btpd in %s (%s).\n",
btpd_dir, strerror(errno));
}

static struct option add_opts[] = {
{ "ipc", required_argument, NULL, 1 },
{ "help", required_argument, NULL, 2},
{NULL, 0, NULL, 0}
};

static void
do_add(char *ipctok, char **paths, int npaths, char **out)
void
usage_add(void)
{
struct ipc *ipc;
do_ipc_open(ipctok, &ipc);
handle_error(btpd_add(ipc, paths, npaths, out));
ipc_close(ipc);
printf(
"Add a torrent to btpd.\n"
"\n"
"Usage: add [-a] [-s] [-c dir] -f file\n"
"\n"
"Options:\n"
"-a\n"
"\tAppend the torrent top directory (if any) to the content path.\n"
"\n"
"-c dir\n"
"\tThe directory where the content is (or will be downloaded to).\n"
"\tDefault is the directory containing the torrent file.\n"
"\n"
"-f file\n"
"\tThe torrent to add.\n"
"\n"
"-s\n"
"\tStart the torrent.\n"
"\n"
);
exit(1);
}

static void
void
cmd_add(int argc, char **argv)
{
int ch;
char *ipctok = NULL;
while ((ch = getopt_long(argc, argv, "", add_opts, NULL)) != -1) {
switch(ch) {
case 1:
ipctok = optarg;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;

if (argc < 1)
usage();

for (int i = 0; i < argc; i++) {
int64_t code;
char *res;
int fd;
char *path;
errno = vopen(&fd, O_RDONLY, "%s.i", argv[i]);
if (errno == ENOENT) {
printf("Testing %s for content.\n", argv[i]);
gen_ifile(argv[i]);
} else if (errno != 0)
err(1, "open %s.i", argv[i]);
else
close(fd);

if ((errno = canon_path(argv[i], &path)) != 0)
err(1, "canon_path");
do_add(ipctok, &path, 1, &res);
free(path);
benc_dget_int64(benc_first(res), "code", &code);
if (code == EEXIST)
printf("btpd already had %s.\n", argv[i]);
else if (code != 0) {
printf("btpd indicates error: %s for %s.\n",
strerror(code), argv[i]);
}
free(res);
}
}

static struct option del_opts[] = {
{ "ipc", required_argument, NULL, 1 },
{ "help", required_argument, NULL, 2},
{NULL, 0, NULL, 0}
};

static void
do_del(char *ipctok, uint8_t (*hashes)[20], int nhashes, char **out)
void
usage_del(void)
{
struct ipc *ipc;
do_ipc_open(ipctok, &ipc);
handle_error(btpd_del(ipc, hashes, nhashes, out));
ipc_close(ipc);
printf(
"Remove torrents from btpd.\n"
"\n"
"Usage: del num ...\n"
"\n"
"Arguments:\n"
"num\n"
"\tThe number of the torrent to remove.\n"
"\n");
exit(1);
}

static void
void
cmd_del(int argc, char **argv)
{
int ch;
char *ipctok = NULL;
while ((ch = getopt_long(argc, argv, "", del_opts, NULL)) != -1) {
switch(ch) {
case 1:
ipctok = optarg;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;

if (argc < 1)
usage();

uint8_t hashes[argc][20];
char *res;
const char *d;

for (int i = 0; i < argc; i++) {
struct metainfo *mi;
if ((errno = load_metainfo(argv[i], -1, 0, &mi)) != 0)
err(1, "load_metainfo: %s", argv[i]);
bcopy(mi->info_hash, hashes[i], 20);
clear_metainfo(mi);
free(mi);
}

do_del(ipctok, hashes, argc, &res);
d = benc_first(res);
for (int i = 0; i < argc; i++) {
int64_t code;
benc_dget_int64(d, "code", &code);
if (code == ENOENT)
printf("btpd didn't have %s.\n", argv[i]);
else if (code != 0) {
printf("btpd indicates error: %s for %s.\n",
strerror(code), argv[i]);
}
d = benc_next(d);
if (argc < 2)
usage_del();

unsigned nums[argc - 1];
char *endptr;
for (int i = 0; i < argc - 1; i++) {
nums[i] = strtoul(argv[i + 1], &endptr, 10);
if (strlen(argv[i + 1]) > endptr - argv[i + 1])
usage_del();
}
free(res);
btpd_connect();
for (int i = 0; i < argc -1; i++)
handle_ipc_res(btpd_del_num(ipc, nums[i]));
}

static struct option die_opts[] = {
{ "ipc", required_argument, NULL, 1 },
{ "help", no_argument, NULL, 2 },
{NULL, 0, NULL, 0}
};

static void
do_die(char *ipctok)
void
usage_kill(void)
{
struct ipc *ipc;
do_ipc_open(ipctok, &ipc);
handle_error(btpd_die(ipc));
ipc_close(ipc);
printf(
"Shutdown btpd.\n"
"\n"
"Usage: kill [seconds]\n"
"\n"
"Arguments:\n"
"seconds\n"
"\tThe number of seconds btpd waits before giving up on unresponsive\n"
"\ttrackers.\n"
"\n"
);
exit(1);
}

static void
cmd_die(int argc, char **argv)
void
cmd_kill(int argc, char **argv)
{
int ch;
char *ipctok = NULL;

while ((ch = getopt_long(argc, argv, "", die_opts, NULL)) != -1) {
switch (ch) {
case 1:
ipctok = optarg;
break;
default:
usage();
}
}
do_die(ipctok);
}

static struct option stat_opts[] = {
{ "ipc", required_argument, NULL, 1 },
{ "help", no_argument, NULL, 2 },
{NULL, 0, NULL, 0}
};
int seconds = -1;
char *endptr;
if (argc == 1)
;
else if (argc == 2) {
seconds = strtol(argv[1], &endptr, 10);
if (strlen(argv[1]) > endptr - argv[1] || seconds < 0)
usage_kill();
} else
usage_kill();

static void
do_stat(char *ipctok, char **out)
{
struct ipc *ipc;
do_ipc_open(ipctok, &ipc);
handle_error(btpd_stat(ipc, out));
ipc_close(ipc);
btpd_connect();
btpd_die(ipc, seconds);
}

struct tor {
char *path;
uint8_t hash[20];
uint64_t down;
uint64_t up;
uint64_t npeers;
uint64_t npieces;
uint64_t have_npieces;
uint64_t seen_npieces;
};

struct tor **parse_tors(char *res, uint8_t (*hashes)[20], int nhashes)
void
usage_list(void)
{
struct tor **tors;
int64_t num;
const char *p;
benc_dget_int64(res, "ntorrents", &num);
benc_dget_lst(res, "torrents", &p);

tors = calloc(sizeof(*tors), num + 1);
int i = 0;
for (p = benc_first(p); p; p = benc_next(p)) {
int j;
const char *hash;
benc_dget_str(p, "hash", &hash, NULL);

for (j = 0; j < nhashes; j++) {
if (bcmp(hashes[i], hash, 20) == 0)
break;
}
if (j < nhashes || nhashes == 0) {
tors[i] = calloc(sizeof(*tors[i]), 1);
bcopy(hash, tors[i]->hash, 20);
benc_dget_int64(p, "down", &tors[i]->down);
benc_dget_int64(p, "up", &tors[i]->up);
benc_dget_int64(p, "npeers", &tors[i]->npeers);
benc_dget_int64(p, "npieces", &tors[i]->npieces);
benc_dget_int64(p, "have npieces", &tors[i]->have_npieces);
benc_dget_int64(p, "seen npieces", &tors[i]->seen_npieces);
benc_dget_strz(p, "path", &tors[i]->path, NULL);
i++;
}
}
return tors;
printf(
"List btpd's torrents.\n"
"\n"
"Usage: list\n"
"\n"
);
exit(1);
}

static void
free_tors(struct tor **tors)
void
cmd_list(int argc, char **argv)
{
for (int i = 0; tors[i] != NULL; i++) {
free(tors[i]->path);
free(tors[i]);
}
free(tors);
struct btstat *st;

if (argc > 1)
usage_list();

btpd_connect();
if ((errno = btpd_stat(ipc, &st)) != 0)
err(1, "btpd_stat");
for (int i = 0; i < st->ntorrents; i++)
printf("%u. %s (%c)\n", st->torrents[i].num, st->torrents[i].name,
st->torrents[i].state);
printf("Listed %u torrent%s.\n", st->ntorrents,
st->ntorrents == 1 ? "" : "s");
}

static void
print_stat(struct tor *cur, struct tor *old, double ds)
void
usage_stat(void)
{
if (old == NULL) {
printf("%5.1f%% %5.1f%% %6.1fM - kB/s %6.1fM - kB/s %4u\n",
100 * cur->seen_npieces / (double)cur->npieces,
100 * cur->have_npieces / (double)cur->npieces,
cur->down / (double)(1 << 20),
cur->up / (double)(1 << 20),
(unsigned)cur->npeers);
} else {
printf("%5.1f%% %5.1f%% %6.1fM %7.2fkB/s %6.1fM %7.2fkB/s %4u\n",
100 * cur->seen_npieces / (double)cur->npieces,
100 * cur->have_npieces / (double)cur->npieces,
cur->down / (double)(1 << 20),
(cur->down - old->down) / ds / (1 << 10),
cur->up / (double)(1 << 20),
(cur->up - old->up) / ds / (1 << 10),
(unsigned)cur->npeers
);
}
printf(
"Display btpd stats.\n"
"\n"
"Usage: stat [-i] [-w seconds]\n"
"\n"
"Options:\n"
"-i\n"
"\tDisplay indivudal lines for each active torrent.\n"
"\n"
"-w n\n"
"\tDisplay stats every n seconds.\n"
"\n");
exit(1);
}

static void
grok_stat(char *ipctok, int iflag, int wait,
uint8_t (*hashes)[20], int nhashes)
void
do_stat(int individual, int seconds)
{
int i, j;
char *res;
struct tor **cur, **old = NULL;
struct tor curtot, oldtot;
struct timeval tv_cur, tv_old;
double ds;
struct btstat *st;
struct tpstat tot;
again:
do_stat(ipctok, &res);
gettimeofday(&tv_cur, NULL);
if (old == NULL)
ds = wait;
else {
struct timeval delta;
timersub(&tv_old, &tv_cur, &delta);
ds = delta.tv_sec + delta.tv_usec / 1000000.0;
if (ds < 0)
ds = wait;
}
tv_old = tv_cur;
cur = parse_tors(res, hashes, nhashes);
free(res);

if (iflag) {
for (i = 0; cur[i] != NULL; i++) {
if (old == NULL) {
printf("%s:\n", rindex(cur[i]->path, '/') + 1);
print_stat(cur[i], NULL, ds);
} else {
for (j = 0; old[j] != NULL; j++)
if (bcmp(cur[i]->hash, old[j]->hash, 20) == 0)
break;
printf("%s:\n", rindex(cur[i]->path, '/') + 1);
print_stat(cur[i], old[j], ds);
}
bzero(&tot, sizeof(tot));
if ((errno = btpd_stat(ipc, &st)) != 0)
err(1, "btpd_stat");
for (int i = 0; i < st->ntorrents; i++) {
struct tpstat *cur = &st->torrents[i];
if (cur->state != 'A')
continue;
if (!individual) {
tot.uploaded += cur->uploaded;
tot.downloaded += cur->downloaded;
tot.rate_up += cur->rate_up;
tot.rate_down += cur->rate_down;
tot.npeers += cur->npeers;
continue;
}
printf("%u. %5.1f%% %6.1fM %7.2fkB/s %6.1fM %7.2fkB/s %4u %5.1f%%",
cur->num,
100.0 * cur->have / cur->total,
(double)cur->downloaded / (1 << 20),
(double)cur->rate_down / (20 << 10),
(double)cur->uploaded / (1 << 20),
(double)cur->rate_up / (20 << 10),
cur->npeers,
100.0 * cur->nseen / cur->npieces
);
if (cur->errors > 0)
printf(" E%u", cur->errors);
printf("\n");
}

bzero(&curtot, sizeof(curtot));
for (i = 0; cur[i] != NULL; i++) {
curtot.down += cur[i]->down;
curtot.up += cur[i]->up;
curtot.npeers += cur[i]->npeers;
curtot.npieces += cur[i]->npieces;
curtot.have_npieces += cur[i]->have_npieces;
curtot.seen_npieces += cur[i]->seen_npieces;
free_btstat(st);
if (!individual) {
printf("%6.1fM %7.2fkB/s %6.1fM %7.2fkB/s %4u\n",
(double)tot.downloaded / (1 << 20),
(double)tot.rate_down / (20 << 10),
(double)tot.uploaded / (1 << 20),
(double)tot.rate_up / (20 << 10),
tot.npeers);
}
if (iflag)
printf("Total:\n");
if (old != NULL)
print_stat(&curtot, &oldtot, ds);
else
print_stat(&curtot, NULL, ds);

if (wait) {
if (old != NULL)
free_tors(old);
old = cur;
oldtot = curtot;
sleep(wait);
if (seconds > 0) {
sleep(seconds);
goto again;
}
free_tors(cur);
}

static void
static struct option stat_opts [] = {
{ "help", no_argument, NULL, 1 },
{NULL, 0, NULL, 0}
};

void
cmd_stat(int argc, char **argv)
{
int ch;
char *ipctok = NULL;
int wait = 0;
int iflag = 0;

int wflag = 0, iflag = 0, seconds = 0;
char *endptr;
while ((ch = getopt_long(argc, argv, "iw:", stat_opts, NULL)) != -1) {
switch (ch) {
case 'i':
iflag = 1;
break;
case 'w':
wait = atoi(optarg);
if (wait <= 0)
errx(1, "-w argument must be an integer > 0.");
break;
case 1:
ipctok = optarg;
wflag = 1;
seconds = strtol(optarg, &endptr, 10);
if (strlen(optarg) > endptr - optarg || seconds < 1)
usage_stat();
break;
default:
usage();
usage_stat();
}
}
argc -= optind;
argv += optind;
if (argc > 0)
usage_stat();

if (argc > 0) {
uint8_t hashes[argc][20];
for (int i = 0; i < argc; i++) {
struct metainfo *mi;
if ((errno = load_metainfo(argv[i], -1, 0, &mi)) != 0)
err(1, "load_metainfo: %s", argv[i]);
bcopy(mi->info_hash, hashes[i], 20);
clear_metainfo(mi);
free(mi);
}
grok_stat(ipctok, iflag, wait, hashes, argc);
} else
grok_stat(ipctok, iflag, wait, NULL, 0);
btpd_connect();
do_stat(iflag, seconds);
}

static struct option list_opts[] = {
{ "ipc", required_argument, NULL, 1 },
{ "help", no_argument, NULL, 2 },
{NULL, 0, NULL, 0}
};

static void
cmd_list(int argc, char **argv)
void
usage_start(void)
{
int ch;
char *ipctok = NULL;
printf(
"Activate torrents.\n"
"\n"
"Usage: start num ...\n"
"\n"
"Arguments:\n"
"num\n"
"\tThe number of the torrent to activate.\n"
"\n");
exit(1);
}

while ((ch = getopt_long(argc, argv, "", list_opts, NULL)) != -1) {
switch (ch) {
case 1:
ipctok = optarg;
break;
default:
usage();
}
void
cmd_start(int argc, char **argv)
{
if (argc < 2)
usage_start();

unsigned nums[argc - 1];
char *endptr;
for (int i = 0; i < argc - 1; i++) {
nums[i] = strtoul(argv[i + 1], &endptr, 10);
if (strlen(argv[i + 1]) > endptr - argv[i + 1])
usage_start();
}
char *res;
const char *p;
char *path;
do_stat(ipctok, &res);

benc_dget_lst(res, "torrents", &p);
int count = 0;
for (p = benc_first(p); p; p = benc_next(p)) {
count++;
benc_dget_strz(p, "path", &path, NULL);
printf("%s\n", path);
free(path);
btpd_connect();
for (int i = 0; i < argc -1; i++)
handle_ipc_res(btpd_start_num(ipc, nums[i]));
}

void
usage_stop(void)
{
printf(
"Deactivate torrents.\n"
"\n"
"Usage: stop num ...\n"
"\n"
"Arguments:\n"
"num\n"
"\tThe number of the torrent to deactivate.\n"
"\n");
exit(1);
}

void
cmd_stop(int argc, char **argv)
{
if (argc < 2)
usage_stop();

unsigned nums[argc - 1];
char *endptr;
for (int i = 0; i < argc - 1; i++) {
nums[i] = strtoul(argv[i + 1], &endptr, 10);
if (strlen(argv[i + 1]) > endptr - argv[i + 1])
usage_stop();
}
printf("%d torrent%s.\n", count, count == 1 ? "" : "s");
btpd_connect();
for (int i = 0; i < argc -1; i++)
handle_ipc_res(btpd_stop_num(ipc, nums[i]));
}

static struct {
const char *name;
void (*fun)(int, char **);
void (*help)(void);
} cmd_table[] = {
{ "add", cmd_add },
{ "del", cmd_del },
{ "die", cmd_die },
{ "list", cmd_list},
{ "stat", cmd_stat }
{ "add", cmd_add, usage_add },
{ "del", cmd_del, usage_del },
{ "kill", cmd_kill, usage_kill },
{ "list", cmd_list, usage_list },
{ "start", cmd_start, usage_start },
{ "stat", cmd_stat, usage_stat },
{ "stop", cmd_stop, usage_stop }
};

static int ncmds = sizeof(cmd_table) / sizeof(cmd_table[0]);

void
usage(void)
{
printf(
"btcli is the btpd command line interface. Use this tool to interact\n"
"with a btpd process.\n"
"\n"
"Usage: btcli [main options] command [command options]\n"
"\n"
"Main options:\n"
"-d dir\n"
"\tThe btpd directory.\n"
"\n"
"--help [command]\n"
"\tShow this text or help for the specified command.\n"
"\n"
"Commands:\n"
"add\n"
"del\n"
"kill\n"
"list\n"
"start\n"
"stat\n"
"stop\n"
"\n");
exit(1);
}

static struct option base_opts [] = {
{ "help", no_argument, NULL, 1 },
{NULL, 0, NULL, 0}
};

int
main(int argc, char **argv)
{
int ch, help = 0;

if (argc < 2)
usage();

while ((ch = getopt_long(argc, argv, "+d:", base_opts, NULL)) != -1) {
switch (ch) {
case 'd':
btpd_dir = optarg;
break;
case 1:
help = 1;
break;
default:
usage();
}
}
argc -= optind;
argv += optind;

if (argc == 0)
usage();

optind = 0;
int found = 0;
for (int i = 0; !found && i < ncmds; i++) {
if (strcmp(argv[1], cmd_table[i].name) == 0) {
if (strcmp(argv[0], cmd_table[i].name) == 0) {
found = 1;
cmd_table[i].fun(argc - 1, argv + 1);
if (help)
cmd_table[i].help();
else
cmd_table[i].fun(argc, argv);
}
}

if (!found)
usage();



+ 144
- 140
cli/btpd_if.c Parādīt failu

@@ -8,34 +8,42 @@
#include <unistd.h>

#include "benc.h"
#include "iobuf.h"
#include "btpd_if.h"
#include "iobuf.h"
#include "subr.h"

struct ipc {
int sd;
};

int
ipc_open(const char *key, struct ipc **out)
ipc_open(const char *dir, struct ipc **out)
{
int sd = -1, err = 0;
size_t plen;
size_t keylen;
struct ipc *res;
struct sockaddr_un addr;

if (key == NULL)
key = "default";
keylen = strlen(key);
for (int i = 0; i < keylen; i++)
if (!isalnum(key[i]))
return EINVAL;
plen = sizeof(addr.sun_path);
if (snprintf(addr.sun_path, plen, "%s/sock", dir) >= plen)
return ENAMETOOLONG;
addr.sun_family = AF_UNIX;

res = malloc(sizeof(*res));
if (res == NULL)
return ENOMEM;
if ((sd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
return errno;

plen = sizeof(res->addr.sun_path);
if (snprintf(res->addr.sun_path, plen,
"/tmp/btpd_%u_%s", geteuid(), key) >= plen) {
free(res);
return ENAMETOOLONG;
if (connect(sd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
err = errno;
close(sd);
return err;
}
res->addr.sun_family = AF_UNIX;

if ((res = malloc(sizeof(*res))) == NULL) {
close(sd);
return ENOMEM;
}

res->sd = sd;
*out = res;
return 0;
}
@@ -43,179 +51,175 @@ ipc_open(const char *key, struct ipc **out)
int
ipc_close(struct ipc *ipc)
{
int err;
err = close(ipc->sd);
free(ipc);
return 0;
}

static int
ipc_connect(struct ipc *ipc, FILE **out)
{
FILE *fp;
int sd;
int error;

if ((sd = socket(PF_UNIX, SOCK_STREAM, 0)) == -1)
return errno;

if (connect(sd, (struct sockaddr *)&ipc->addr, sizeof(ipc->addr)) == -1)
goto error;

if ((fp = fdopen(sd, "r+")) == NULL)
goto error;

*out = fp;
return 0;
error:
error = errno;
close(sd);
return error;
return err;
}

static int
ipc_response(FILE *fp, char **out, uint32_t *len)
ipc_response(struct ipc *ipc, char **out, uint32_t *len)
{
uint32_t size;
char *buf;

if (fread(&size, sizeof(size), 1, fp) != 1) {
if (ferror(fp))
return errno;
else
return ECONNRESET;
}
if ((errno = read_fully(ipc->sd, &size, sizeof(size))) != 0)
return errno;

if (size == 0)
return EINVAL;
return ECONNRESET;

if ((buf = malloc(size)) == NULL)
return ENOMEM;

if (fread(buf, 1, size, fp) != size) {
if (ferror(fp))
return errno;
else
return ECONNRESET;
if ((errno = read_fully(ipc->sd, buf, size)) != 0) {
free(buf);
return errno;
}

*out = buf;
*len = size;
return 0;
}
static int
ipc_req_res(struct ipc *ipc,
const char *req, uint32_t qlen,
char **res, uint32_t *rlen)
ipc_req_res(struct ipc *ipc, const char *req, uint32_t qlen, char **res,
uint32_t *rlen)
{
FILE *fp;
int error;

if ((error = ipc_connect(ipc, &fp)) != 0)
return error;

if (fwrite(&qlen, sizeof(qlen), 1, fp) != 1)
goto error;
if (fwrite(req, 1, qlen, fp) != qlen)
if ((errno = write_fully(ipc->sd, &qlen, sizeof(qlen))) != 0)
goto error;
if (fflush(fp) != 0)
if ((errno = write_fully(ipc->sd, req, qlen)) != 0)
goto error;
if ((errno = ipc_response(fp, res, rlen)) != 0)
if ((errno = ipc_response(ipc, res, rlen)) != 0)
goto error;
if ((errno = benc_validate(*res, *rlen)) != 0)
goto error;

fclose(fp);
return 0;
if (!benc_isdct(*res))
errno = EINVAL;
error:
error = errno;
fclose(fp);
return error;
return errno;
}

int
btpd_die(struct ipc *ipc)
static enum ipc_code
ipc_buf_req(struct ipc *ipc, struct io_buffer *iob)
{
int error;
char *response = NULL;
const char shutdown[] = "l3:diee";
uint32_t size = sizeof(shutdown) - 1;
uint32_t rsiz;

if ((error = ipc_req_res(ipc, shutdown, size, &response, &rsiz)) != 0)
return error;

error = benc_validate(response, rsiz);

if (error == 0) {
int64_t tmp;
benc_dget_int64(response, "code", &tmp);
error = tmp;
}

free(response);
return error;
int err;
char *res;
size_t reslen;

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;
}

int
btpd_add(struct ipc *ipc, char **paths, unsigned npaths, char **out)
enum ipc_code
btpd_die(struct ipc *ipc, int seconds)
{
int error;
struct io_buffer iob;
char *res = NULL;
uint32_t reslen;
buf_init(&iob, 16);
if (seconds >= 0)
buf_print(&iob, "l3:diei%dee", seconds);
else
buf_print(&iob, "l3:diee");
return ipc_buf_req(ipc, &iob);
}

buf_init(&iob, 1024);
buf_print(&iob, "l3:add");
for (unsigned i = 0; i < npaths; i++) {
int plen = strlen(paths[i]);
buf_print(&iob, "%d:", plen);
buf_write(&iob, paths[i], plen);
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->num = benc_dget_int(tp, "num");
ts->name = benc_dget_str(tp, "path", NULL);
ts->state = *benc_dget_str(tp, "state", NULL);
if (ts->state == 'A') {
ts->errors = benc_dget_int(tp, "errors");
ts->npieces = benc_dget_int(tp, "npieces");
ts->nseen = benc_dget_int(tp, "seen npieces");
ts->npeers = benc_dget_int(tp, "npeers");
ts->downloaded = benc_dget_int(tp, "downloaded");
ts->uploaded = benc_dget_int(tp, "uploaded");
ts->rate_down = benc_dget_int(tp, "rd");
ts->rate_up = benc_dget_int(tp, "ru");
ts->have = benc_dget_int(tp, "have");
ts->total = benc_dget_int(tp, "total");
}
i++;
}
buf_print(&iob, "e");

error = ipc_req_res(ipc, iob.buf, iob.buf_off, &res, &reslen);
free(iob.buf);
if (error == 0)
*out = res;
*out = st;
return IPC_OK;
}

return error;
void
free_btstat(struct btstat *st)
{
for (unsigned i = 0; i < st->ntorrents; i++)
if (st->torrents[i].name != NULL)
free(st->torrents[i].name);
free(st);
}

int
btpd_stat(struct ipc *ipc, char **out)
enum ipc_code
btpd_stat(struct ipc *ipc, struct btstat **out)
{
int err;
const char cmd[] = "l4:state";
uint32_t cmdlen = sizeof(cmd) - 1;
char *res;
uint32_t reslen;

if ((errno = ipc_req_res(ipc, cmd, cmdlen, &res, &reslen)) != 0)
return errno;
*out = res;
return 0;
if ((err = ipc_req_res(ipc, cmd, cmdlen, &res, &reslen)) != 0)
return IPC_COMMERR;

err = parse_btstat(res, out);
free(res);
return err;
}

int
btpd_del(struct ipc *ipc, uint8_t (*hash)[20], unsigned nhashes, char **out)
static enum ipc_code
btpd_common_num(struct ipc *ipc, const char *cmd, unsigned num)
{
int error;
struct io_buffer iob;
char *res = NULL;
uint32_t reslen;
buf_init(&iob, 16);
buf_print(&iob, "l%d:%si%uee", (int)strlen(cmd), cmd, num);
return ipc_buf_req(ipc, &iob);
}

buf_init(&iob, 1024);
buf_write(&iob, "l3:del", 6);
for (unsigned i = 0; i < nhashes; i++) {
buf_write(&iob, "20:", 3);
buf_write(&iob, hash[i], 20);
}
buf_write(&iob, "e", 1);
enum ipc_code
btpd_del_num(struct ipc *ipc, unsigned num)
{
return btpd_common_num(ipc, "del", num);
}

error = ipc_req_res(ipc, iob.buf, iob.buf_off, &res, &reslen);
free(iob.buf);
if (error != 0)
return error;
enum ipc_code
btpd_start_num(struct ipc *ipc, unsigned num)
{
return btpd_common_num(ipc, "start", num);
}

*out = res;
return 0;
enum ipc_code
btpd_stop_num(struct ipc *ipc, unsigned num)
{
return btpd_common_num(ipc, "stop", num);
}

+ 31
- 8
cli/btpd_if.h Parādīt failu

@@ -5,17 +5,40 @@
#include <sys/socket.h>
#include <sys/un.h>

struct ipc {
struct sockaddr_un addr;
struct ipc;

enum ipc_code {
IPC_OK,
IPC_FAIL,
IPC_COMMERR
};

struct btstat {
unsigned ntorrents;
struct tpstat {
char *name;
unsigned num;
char state;

unsigned errors;
unsigned npeers;
uint32_t npieces, nseen;
off_t have, total;
long long downloaded, uploaded;
unsigned long rate_up, rate_down;
} torrents[];
};

int ipc_open(const char *key, struct ipc **out);
int ipc_open(const char *dir, struct ipc **out);
int ipc_close(struct ipc *ipc);

int btpd_add(struct ipc *ipc, char **path, unsigned npaths, char **out);
int btpd_del(struct ipc *ipc, uint8_t (*hash)[20],
unsigned nhashes, char **out);
int btpd_die(struct ipc *ipc);
int btpd_stat(struct ipc *ipc, char **out);
enum ipc_code btpd_die(struct ipc *ipc, int seconds);
enum ipc_code btpd_stat(struct ipc *ipc, struct btstat **out);

enum ipc_code btpd_del_num(struct ipc *ipc, unsigned num);
enum ipc_code btpd_start_num(struct ipc *ipc, unsigned num);
enum ipc_code btpd_stop_num(struct ipc *ipc, unsigned num);

void free_btstat(struct btstat *stat);

#endif

+ 203
- 120
misc/benc.c Parādīt failu

@@ -2,6 +2,7 @@
#include <ctype.h>
#include <errno.h>
#include <inttypes.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>

@@ -99,7 +100,7 @@ benc_length(const char *p)
;
return next - p + 1;
default:
assert(benc_str(p, &next, &blen, NULL) == 0);
assert((next = benc_mem(p, &blen, NULL)) != NULL);
return next - p + blen;
}
}
@@ -127,11 +128,12 @@ benc_next(const char *p)
return *(p + blen) == 'e' ? NULL : p + blen;
}

int
benc_str(const char *p, const char **out, size_t *len, const char**next)
const char *
benc_mem(const char *p, size_t *len, const char**next)
{
if (!benc_isstr(p))
return NULL;
size_t blen = 0;
assert(isdigit(*p));
blen = *p - '0';
p++;
while (isdigit(*p)) {
@@ -141,53 +143,49 @@ benc_str(const char *p, const char **out, size_t *len, const char**next)
}
assert(*p == ':');
benc_safeset(len, blen);
benc_safeset(out, p + 1);
benc_safeset(next, *(p + blen + 1) == 'e' ? NULL : p + blen + 1);
return 0;
return p + 1;
}

int
benc_strz(const char *p, char **out, size_t *len, const char **next)
char *
benc_str(const char *p, size_t *len, const char **next)
{
int err;
size_t blen;
const char *bstr;

if ((err = benc_str(p, &bstr, &blen, next)) == 0) {
if ((*out = malloc(blen + 1)) != NULL) {
memcpy(*out, bstr, blen);
(*out)[blen] = '\0';
benc_safeset(len, blen);
} else
err = ENOMEM;
}
return err;
char *ret;
if ((bstr = benc_mem(p, &blen, next)) == NULL)
return NULL;
if ((ret = malloc(blen + 1)) == NULL)
return NULL;
bcopy(bstr, ret, blen);
ret[blen] = '\0';
benc_safeset(len, blen);
return ret;
}

int
benc_stra(const char *p, char **out, size_t *len, const char **next)
char *
benc_mema(const char *p, size_t *len, const char **next)
{
int err;
size_t blen;
const char *bstr;

if ((err = benc_str(p, &bstr, &blen, next)) == 0) {
if ((*out = malloc(blen)) != NULL) {
memcpy(*out, bstr, blen);
benc_safeset(len, blen);
} else
err = ENOMEM;
}
return err;
char *ret;
if ((bstr = benc_mem(p, &blen, next)) == NULL)
return NULL;
if ((ret = malloc(blen)) == NULL)
return NULL;
bcopy(bstr, ret, blen);
benc_safeset(len, blen);
return ret;
}

int
benc_int64(const char *p, int64_t *out, const char **next)
long long
benc_int(const char *p, const char **next)
{
int sign = 1;
int64_t res = 0;
long long res = 0;

assert(*p == 'i');
if (!benc_isint(p))
return 0;
p++;
if (*p == '-') {
sign = -1;
@@ -202,122 +200,79 @@ benc_int64(const char *p, int64_t *out, const char **next)
p++;
}
assert(*p == 'e');
benc_safeset(out, res);
benc_safeset(next, *(p + 1) == 'e' ? NULL : p + 1);

return 0;
}

int
benc_uint32(const char *p, uint32_t *out, const char **next)
{
int err;
int64_t res;
if ((err = benc_int64(p, &res, next)) == 0) {
if (res >= 0 && res <= 0xffffffffUL)
*out = (uint32_t)res;
else
err = EINVAL;
}
return err;
benc_safeset(next, *(p + 1) == 'e' ? NULL : p + 1);
return res;
}

int
benc_dget_any(const char *p, const char *key, const char **val)
const char *
benc_dget_any(const char *p, const char *key)
{
int res;
int cmp;
size_t len, blen;
const char *bstr;

assert(benc_isdct(p));
if (!benc_isdct(p))
return NULL;

len = strlen(key);

p = benc_first(p);
while (p != NULL) {
if ((res = benc_str(p, &bstr, &blen, &p)) != 0)
return res;
res = strncmp(bstr, key, blen);
if (res == 0 && len == blen) {
*val = p;
return 0;
} else if (res <= 0) {
if (!benc_isstr(p))
return NULL;
bstr = benc_mem(p, &blen, &p);
cmp = strncmp(bstr, key, blen);
if (cmp == 0 && len == blen)
return p;
else if (cmp <= 0)
p = benc_next(p);
} else
return ENOENT;
else
return NULL;
}
return ENOENT;
return NULL;
}

int
benc_dget_lst(const char *p, const char *key, const char **val)
{
int err;
if ((err = benc_dget_any(p, key, val)) == 0)
if (!benc_islst(*val))
err = EINVAL;
return err;
}

int
benc_dget_dct(const char *p, const char *key, const char **val)
const char *
benc_dget_lst(const char *p, const char *key)
{
int err;
if ((err = benc_dget_any(p, key, val)) == 0)
if (!benc_isdct(*val))
err = EINVAL;
return err;
const char *ret = benc_dget_any(p, key);
return ret != NULL && benc_islst(ret) ? ret : NULL;
}

int
benc_dget_str(const char *p, const char *key, const char **val, size_t *len)
const char *
benc_dget_dct(const char *p, const char *key)
{
int err;
const char *sp;
if ((err = benc_dget_any(p, key, &sp)) == 0)
err = benc_isstr(sp) ? benc_str(sp, val, len, NULL) : EINVAL;
return err;
const char *ret = benc_dget_any(p, key);
return ret != NULL && benc_isdct(ret) ? ret : NULL;
}

int
benc_dget_stra(const char *p, const char *key, char **val, size_t *len)
const char *
benc_dget_mem(const char *p, const char *key, size_t *len)
{
int err;
const char *sp;
if ((err = benc_dget_any(p, key, &sp)) == 0)
err = benc_isstr(sp) ? benc_stra(sp, val, len, NULL) : EINVAL;
return err;
const char *str = benc_dget_any(p, key);
return str != NULL && benc_isstr(str) ? benc_mem(str, len, NULL) : NULL;
}

int
benc_dget_strz(const char *p, const char *key, char **val, size_t *len)
char *
benc_dget_mema(const char *p, const char *key, size_t *len)
{
int err;
const char *sp;
if ((err = benc_dget_any(p, key, &sp)) == 0)
err = benc_isstr(sp) ? benc_strz(sp, val, len, NULL) : EINVAL;
return err;
const char *str = benc_dget_any(p, key);
return str != NULL && benc_isstr(str) ? benc_mema(str, len, NULL) : NULL;
}

int
benc_dget_int64(const char *p, const char *key, int64_t *val)
char *
benc_dget_str(const char *p, const char *key, size_t *len)
{
int err;
const char *ip;
if ((err = benc_dget_any(p, key, &ip)) == 0)
err = benc_isint(ip) ? benc_int64(ip, val, NULL) : EINVAL;
return err;
const char *str = benc_dget_any(p, key);
return str != NULL && benc_isstr(str) ? benc_str(str, len, NULL) : NULL;
}

int
benc_dget_uint32(const char *p, const char *key, uint32_t *val)
long long
benc_dget_int(const char *p, const char *key)
{
int err;
const char *ip;
if ((err = benc_dget_any(p, key, &ip)) == 0)
err = benc_isint(ip) ? benc_uint32(ip, val, NULL) : EINVAL;
return err;
const char *intp = benc_dget_any(p, key);
return intp != NULL && benc_isint(intp) ? benc_int(intp, NULL) : 0;
}

int
@@ -343,3 +298,131 @@ benc_isstr(const char *p)
{
return isdigit(*p);
}

int
benc_istype(const char *p, enum be_type type)
{
switch (type) {
case BE_ANY:
return benc_isdct(p) || benc_isint(p) ||
benc_islst(p) || benc_isstr(p);
case BE_DCT:
return benc_isdct(p);
case BE_INT:
return benc_isint(p);
case BE_LST:
return benc_islst(p);
case BE_STR:
return benc_isstr(p);
default:
abort();
}
}

int
benc_dct_chk(const char *p, int count, ...)
{
int i, ok = 1;
va_list ap;

if (!benc_isdct(p))
ok = 0;

va_start(ap, count);
for (i = 0; ok && i < count; i++) {
enum be_type type = va_arg(ap, enum be_type);
int level = va_arg(ap, int);
const char *dct = p;
const char *key = va_arg(ap, const char *);
while (ok && level > 1) {
if ((dct = benc_dget_dct(dct, key)) != NULL) {
level--;
key = va_arg(ap, const char *);
} else
ok = 0;
}
if (ok) {
const char *val = benc_dget_any(dct, key);
if (val == NULL || !benc_istype(val, type))
ok = 0;
}
}
va_end(ap);
return ok;
}

#if 0
int
benc_dct_type_check(const char *p, int count, ...)
{
int i;
va_list ap;

benc_validate_dct(p, 2, BE_INT, "code", BE_STR, "hash",

if (!benc_isdct(p))
return EINVAL;

va_start(ap, count);
for (i = 0; i < count; i++) {


}
}

int
benc_dget_many(const char *p, int count, ...)
{
int i;
va_list ap;
if (!benc_isdct(p))
return 0;
va_start(ap, count);
for (i = 0; i < count; i++) {
const char *name = va_arg(ap, const char *);
enum be_type type = va_arg(ap, enum be_type);
int64_t *iret;
size_t *lret;
const char **mret;
char **aret;

switch (type) {
case BE_INT:
iret = va_arg(ap, int64_t *);
if (benc_dget_int64(p, name, iret) != 0)
goto out;
break;
case BE_LST:
mret = va_arg(ap, const char **);
lret = va_arg(ap, size_t *);
if (benc_dget_lst(p, name, mret) != 0)
goto out;
if (lret != NULL)
*lret = benc_nelems(*mret);
break;
case BE_DCT:
mret = va_arg(ap, const char **);
if (benc_dget_dct(p, name, mret) != 0)
goto out;
break;
case BE_MEM:
mret = va_arg(ap, const char **);
lret = va_arg(ap, size_t *);
if (benc_dget_str(p, name, mret, lret) != 0)
goto out;
break;
case BE_STRZ:
aret = va_arg(ap, char **);
lret = va_arg(ap, size_t *);
if (benc_dget_strz(p, name, aret, lret) != 0)
goto out;
break;
default:
abort();
}
}
out:
va_end(ap);
return i;
}
#endif

+ 26
- 24
misc/benc.h Parādīt failu

@@ -1,7 +1,21 @@
#ifndef BTPD_BENC_H
#define BTPD_BENC_H

enum be_type {
BE_ANY,
BE_DCT,
BE_INT,
BE_LST,
BE_STR
};

int benc_validate(const char *p, size_t len);
int benc_dct_chk(const char *p, int count, ...);

int benc_islst(const char *p);
int benc_isdct(const char *p);
int benc_isint(const char *p);
int benc_isstr(const char *p);

size_t benc_length(const char *p);
size_t benc_nelems(const char *p);
@@ -9,29 +23,17 @@ size_t benc_nelems(const char *p);
const char *benc_first(const char *p);
const char *benc_next(const char *p);

int benc_str(const char *p, const char **mem, size_t *len, const char**next);
int benc_stra(const char *p, char **out, size_t *len, const char **next);
int benc_strz(const char *p, char **out, size_t *len, const char **next);
int benc_int64(const char *p, int64_t *out, const char **next);
int benc_uint32(const char *p, uint32_t *out, const char **next);

#define benc_off benc_int64

int benc_dget_any(const char *p, const char *key, const char **val);
int benc_dget_lst(const char *p, const char *key, const char **val);
int benc_dget_dct(const char *p, const char *key, const char **val);
int benc_dget_str(const char *p, const char *key,
const char **val, size_t *len);
int benc_dget_stra(const char *p, const char *key, char **val, size_t *len);
int benc_dget_strz(const char *p, const char *key, char **val, size_t *len);
int benc_dget_int64(const char *p, const char *key, int64_t *val);
int benc_dget_uint32(const char *p, const char *key, uint32_t *val);

#define benc_dget_off benc_dget_int64

int benc_islst(const char *p);
int benc_isdct(const char *p);
int benc_isint(const char *p);
int benc_isstr(const char *p);
long long benc_int(const char *p, const char **next);
const char *benc_mem(const char *p, size_t *len, const char **next);
char *benc_mema(const char *p, size_t *len, const char **next);
char *benc_str(const char *p, size_t *len, const char **next);

const char *benc_dget_any(const char *p, const char *key);
const char *benc_dget_lst(const char *p, const char *key);
const char *benc_dget_dct(const char *p, const char *key);
long long benc_dget_int(const char *p, const char *key);
const char *benc_dget_mem(const char *p, const char *key, size_t *len);
char *benc_dget_mema(const char *p, const char *key, size_t *len);
char *benc_dget_str(const char *p, const char *key, size_t *len);

#endif

+ 24
- 40
misc/metainfo.c Parādīt failu

@@ -69,22 +69,22 @@ check_path(const char *path, size_t len)
int
fill_fileinfo(const char *fdct, struct fileinfo *tfp)
{
int err;
//int err;
size_t npath, plen, len;
const char *plst, *iter, *str;

if ((err = benc_dget_off(fdct, "length", &tfp->length)) != 0)
return err;
if (!benc_dct_chk(fdct, 2, BE_INT, 1, "length", BE_LST, 1, "path"))
return EINVAL;

if ((err = benc_dget_lst(fdct, "path", &plst)) != 0)
return err;
tfp->length = benc_dget_int(fdct, "length");
plst = benc_dget_lst(fdct, "path");

npath = plen = 0;
iter = benc_first(plst);
while (iter != NULL) {
if (!benc_isstr(iter))
return EINVAL;
benc_str(iter, &str, &len, &iter);
str = benc_mem(iter, &len, &iter);
if (!check_path(str, len))
return EINVAL;
npath++;
@@ -97,13 +97,13 @@ fill_fileinfo(const char *fdct, struct fileinfo *tfp)
return ENOMEM;

iter = benc_first(plst);
benc_str(iter, &str, &len, &iter);
str = benc_mem(iter, &len, &iter);
memcpy(tfp->path, str, len);
plen = len;
npath--;
while (npath > 0) {
tfp->path[plen++] = '/';
benc_str(iter, &str, &len, &iter);
str = benc_mem(iter, &len, &iter);
memcpy(tfp->path + plen, str, len);
plen += len;
npath--;
@@ -135,35 +135,25 @@ int
fill_metainfo(const char *bep, struct metainfo *tp, int mem_hashes)
{
size_t len;
int err;
int err = 0;
const char *base_addr = bep;
const char *hash_addr;

if (!benc_isdct(bep))
if (!benc_dct_chk(bep, 5,
BE_STR, 1, "announce",
BE_DCT, 1, "info",
BE_INT, 2, "info", "piece length",
BE_STR, 2, "info", "pieces",
BE_STR, 2, "info", "name"))
return EINVAL;

if ((err = benc_dget_strz(bep, "announce", &tp->announce, NULL)) != 0)
goto out;

if ((err = benc_dget_dct(bep, "info", &bep)) != 0)
goto out;

tp->announce = benc_dget_str(bep, "announce", NULL);
bep = benc_dget_dct(bep, "info");
SHA1(bep, benc_length(bep), tp->info_hash);

if ((err = benc_dget_off(bep, "piece length", &tp->piece_length)) != 0)
goto out;

if ((err = benc_dget_str(bep, "pieces", &hash_addr, &len)) != 0)
goto out;

if (len % 20 != 0) {
err = EINVAL;
goto out;
}
tp->piece_length = benc_dget_int(bep, "piece length");
hash_addr = benc_dget_mem(bep, "pieces", &len);
tp->npieces = len / 20;

tp->pieces_off = hash_addr - base_addr;

if (mem_hashes) {
if ((tp->piece_hash = malloc(len)) == NULL) {
err = ENOMEM;
@@ -171,12 +161,10 @@ fill_metainfo(const char *bep, struct metainfo *tp, int mem_hashes)
}
bcopy(hash_addr, tp->piece_hash, len);
}
tp->name = benc_dget_str(bep, "name", NULL);

if ((err = benc_dget_strz(bep, "name", &tp->name, NULL)) != 0)
goto out;

err = benc_dget_off(bep, "length", &tp->total_length);
if (err == 0) {
if (benc_dct_chk(bep, 1, BE_INT, 1, "length")) {
tp->total_length = benc_dget_int(bep, "length");
tp->nfiles = 1;
tp->files = calloc(1, sizeof(struct fileinfo));
if (tp->files != NULL) {
@@ -190,14 +178,11 @@ fill_metainfo(const char *bep, struct metainfo *tp, int mem_hashes)
err = ENOMEM;
goto out;
}
}
else if (err == ENOENT) {
} else if (benc_dct_chk(bep, 1, BE_LST, 1, "files")) {
int i;
const char *flst, *fdct;

if ((err = benc_dget_lst(bep, "files", &flst)) != 0)
goto out;

flst = benc_dget_lst(bep, "files");
tp->nfiles = benc_nelems(flst);
if (tp->nfiles < 1) {
err = EINVAL;
@@ -258,7 +243,6 @@ load_metainfo(const char *path, off_t size, int mem_hashes,
if (err == 0)
if ((*res = calloc(1, sizeof(**res))) == NULL)
err = ENOMEM;

if (err == 0)
if ((err = fill_metainfo(buf, *res, mem_hashes)) != 0)
free(*res);


Notiek ielāde…
Atcelt
Saglabāt