From 01c92051d5eda1c5fe44c7a84766afbd6774ff24 Mon Sep 17 00:00:00 2001 From: Richard Nyberg Date: Sun, 5 Feb 2006 17:08:39 +0000 Subject: [PATCH] o Changed the benc_ api to make it easier to use. o Lot of work on the cli and its communication with btpd. --- btpd/cli_if.c | 260 ++++++++++----- btpd/tracker_req.c | 80 ++--- cli/btcli.c | 797 ++++++++++++++++++--------------------------- cli/btpd_if.c | 284 ++++++++-------- cli/btpd_if.h | 39 ++- misc/benc.c | 323 +++++++++++------- misc/benc.h | 50 +-- misc/metainfo.c | 64 ++-- 8 files changed, 949 insertions(+), 948 deletions(-) diff --git a/btpd/cli_if.c b/btpd/cli_if.c index 9b699f3..f2cdc0b 100644 --- a/btpd/cli_if.c +++ b/btpd/cli_if.c @@ -12,58 +12,80 @@ #include #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 diff --git a/btpd/tracker_req.c b/btpd/tracker_req.c index 74b16ee..dfb5ed6 100644 --- a/btpd/tracker_req.c +++ b/btpd/tracker_req.c @@ -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 diff --git a/cli/btcli.c b/cli/btcli.c index 11329b6..660b9ad 100644 --- a/cli/btcli.c +++ b/cli/btcli.c @@ -1,587 +1,422 @@ -#include -#include - #include #include -#include #include -#include -#include #include #include #include -#include - -#include -#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_n]\n" - "\tAdd the given torrents to btpd.\n" - "\n" - "del ... [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(); diff --git a/cli/btpd_if.c b/cli/btpd_if.c index 65502ac..a5fa40d 100644 --- a/cli/btpd_if.c +++ b/cli/btpd_if.c @@ -8,34 +8,42 @@ #include #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); } diff --git a/cli/btpd_if.h b/cli/btpd_if.h index e67770f..64cdb4b 100644 --- a/cli/btpd_if.h +++ b/cli/btpd_if.h @@ -5,17 +5,40 @@ #include #include -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 diff --git a/misc/benc.c b/misc/benc.c index 68f7f95..6a57e2e 100644 --- a/misc/benc.c +++ b/misc/benc.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -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 diff --git a/misc/benc.h b/misc/benc.h index 8088190..69c5f57 100644 --- a/misc/benc.h +++ b/misc/benc.h @@ -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 diff --git a/misc/metainfo.c b/misc/metainfo.c index 1b90247..d8f3a7c 100644 --- a/misc/metainfo.c +++ b/misc/metainfo.c @@ -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);