From 2550d6cb8c7f263f63a68122b69285f8f849dc18 Mon Sep 17 00:00:00 2001 From: Richard Nyberg Date: Wed, 8 Feb 2006 22:56:35 +0000 Subject: [PATCH] Interaction with btpd is now much more like I want it. Previous work has moved toward btpd having a library of torrent to wich one may add or remove torrents, and where interaction on torrents are done by their assigned number. This commit is a step back from that and it makes life simpler and better for all :) * Some options to btpd has changed: --no-daemon is the old -d. -d is now used to specify the btpd directory. --logfile option is reintroduced. * The ipc code has been improved on both btpd and cli sides. * All commands have been implemented. * Various improvements in btpd. With this commit we're very close to 0.8 :) --- btpd/btpd.c | 118 +++-------------- btpd/btpd.h | 14 +- btpd/cli_if.c | 251 ++++++++++++++---------------------- btpd/content.c | 14 +- btpd/main.c | 76 +++++------ btpd/net.c | 5 +- btpd/opts.c | 1 - btpd/opts.h | 1 - btpd/torrent.c | 153 +++++++++++++--------- btpd/torrent.h | 16 ++- cli/btcli.c | 341 +++++++++++++++++++++++++++---------------------- cli/btpd_if.c | 68 +++++----- cli/btpd_if.h | 22 ++-- 13 files changed, 496 insertions(+), 584 deletions(-) diff --git a/btpd/btpd.c b/btpd/btpd.c index c73d5bd..ccc2e20 100644 --- a/btpd/btpd.c +++ b/btpd/btpd.c @@ -32,9 +32,6 @@ static uint8_t m_peer_id[20]; static struct event m_sigint; static struct event m_sigterm; -static unsigned m_ntorrents; -static struct torrent_tq m_torrents = BTPDQ_HEAD_INITIALIZER(m_torrents); -static unsigned m_nactive; static int m_shutdown; void @@ -44,92 +41,35 @@ btpd_exit(int code) exit(code); } -void -btpd_tp_activated(struct torrent *tp) -{ - m_nactive++; -} - -void -btpd_tp_deactivated(struct torrent *tp) -{ - m_nactive--; - if (m_nactive == 0 && m_shutdown) - btpd_exit(0); -} - static void grace_cb(int fd, short type, void *arg) { struct torrent *tp; - BTPDQ_FOREACH(tp, &m_torrents, entry) - torrent_deactivate(tp); + BTPDQ_FOREACH(tp, torrent_get_all(), entry) + torrent_stop(tp); } void -btpd_shutdown(struct timeval *grace_tv) +btpd_shutdown(int grace_seconds) { - if (m_nactive == 0) + if (torrent_count() == 0) btpd_exit(0); else { struct torrent *tp; m_shutdown = 1; - BTPDQ_FOREACH(tp, &m_torrents, entry) - torrent_deactivate(tp); - if (grace_tv != NULL) - event_once(-1, EV_TIMEOUT, grace_cb, NULL, grace_tv); + BTPDQ_FOREACH(tp, torrent_get_all(), entry) + if (tp->state != T_STOPPING) + torrent_stop(tp); + if (grace_seconds >= 0) { + event_once(-1, EV_TIMEOUT, grace_cb, NULL, + (& (struct timeval) { grace_seconds, 0 })); + } } } -static void -signal_cb(int signal, short type, void *arg) -{ - btpd_log(BTPD_L_BTPD, "Got signal %d.\n", signal); - btpd_shutdown((& (struct timeval) { 30, 0 })); -} - -void -btpd_add_torrent(struct torrent *tp) -{ - BTPDQ_INSERT_TAIL(&m_torrents, tp, entry); - m_ntorrents++; -} - -void -btpd_del_torrent(struct torrent *tp) -{ - BTPDQ_REMOVE(&m_torrents, tp, entry); - m_ntorrents--; -} - -const struct torrent_tq * -btpd_get_torrents(void) -{ - return &m_torrents; -} - -unsigned -btpd_get_ntorrents(void) -{ - return m_ntorrents; -} - -struct torrent * -btpd_get_torrent(const uint8_t *hash) -{ - struct torrent *tp = BTPDQ_FIRST(&m_torrents); - while (tp != NULL && bcmp(hash, tp->meta.info_hash, 20) != 0) - tp = BTPDQ_NEXT(tp, entry); - return tp; -} - -struct torrent * -btpd_get_torrent_num(unsigned num) +int btpd_is_stopping(void) { - struct torrent *tp = BTPDQ_FIRST(&m_torrents); - while (tp != NULL && tp->num != num) - tp = BTPDQ_NEXT(tp, entry); - return tp; + return m_shutdown; } const uint8_t * @@ -138,28 +78,18 @@ btpd_get_peer_id(void) return m_peer_id; } -static int -nodot(struct dirent *dp) +void +btpd_on_no_torrents(void) { - return !(strcmp(".", dp->d_name) == 0 || strcmp("..", dp->d_name) == 0); + if (m_shutdown) + btpd_exit(0); } static void -load_library(void) +signal_cb(int signal, short type, void *arg) { - int ne; - struct dirent **entries; - if ((ne = scandir("library", &entries, nodot, NULL)) < 0) - btpd_err("Couldn't open the library.\n"); - - for (int i = 0; i < ne; i++) { - struct torrent *tp; - struct dirent *e = entries[i]; - if (torrent_load(&tp, e->d_name) == 0) - btpd_add_torrent(tp); - free(e); - } - free(entries); + btpd_log(BTPD_L_BTPD, "Got signal %d.\n", signal); + btpd_shutdown(30); } struct td_cb { @@ -260,14 +190,6 @@ btpd_init(void) ul_init(); cm_init(); - load_library(); - -#if 0 - struct torrent *tp; - BTPDQ_FOREACH(tp, &m_torrents, entry) - torrent_activate(tp); -#endif - signal(SIGPIPE, SIG_IGN); signal_set(&m_sigint, SIGINT, signal_cb, NULL); diff --git a/btpd/btpd.h b/btpd/btpd.h index 1700248..c567c4e 100644 --- a/btpd/btpd.h +++ b/btpd/btpd.h @@ -48,14 +48,9 @@ void btpd_err(const char *fmt, ...); void *btpd_malloc(size_t size); void *btpd_calloc(size_t nmemb, size_t size); -void btpd_shutdown(struct timeval *grace_tv); - -struct torrent *btpd_get_torrent(const uint8_t *hash); -struct torrent *btpd_get_torrent_num(unsigned num); -const struct torrent_tq *btpd_get_torrents(void); -void btpd_add_torrent(struct torrent *tp); -void btpd_del_torrent(struct torrent *tp); -unsigned btpd_get_ntorrents(void); +void btpd_shutdown(int grace_seconds); +int btpd_is_stopping(void); + const uint8_t *btpd_get_peer_id(void); void td_acquire_lock(void); @@ -65,7 +60,6 @@ void td_release_lock(void); void td_post(void (*fun)(void *), void *arg); void td_post_end(void); -void btpd_tp_activated(struct torrent *tp); -void btpd_tp_deactivated(struct torrent *tp); +void btpd_on_no_torrents(void); #endif diff --git a/btpd/cli_if.c b/btpd/cli_if.c index f2cdc0b..a5111c0 100644 --- a/btpd/cli_if.c +++ b/btpd/cli_if.c @@ -19,10 +19,39 @@ struct cli { struct event read; }; -#define buf_swrite(iob, s) buf_write(iob, s, sizeof(s) - 1) - static struct event m_cli_incoming; +enum ipc_code { // XXX: Same as in cli/btpd_if.h + IPC_OK, + IPC_FAIL, + IPC_ERROR, + IPC_COMMERR +}; + +static int +write_buffer(struct cli *cli, struct io_buffer *iob) +{ + int err = 0; + if (!iob->error) { + uint32_t len = iob->buf_off; + write_fully(cli->sd, &len, sizeof(len)); + err = write_fully(cli->sd, iob->buf, iob->buf_off); + } else + btpd_err("Out of memory.\n"); + if (iob->buf != NULL) + free(iob->buf); + return err; +} + +static int +write_code_buffer(struct cli *cli, enum ipc_code code) +{ + struct io_buffer iob; + buf_init(&iob, 16); + buf_print(&iob, "d4:codei%uee", code); + return write_buffer(cli, &iob); +} + static int cmd_stat(struct cli *cli, int argc, const char *args) { @@ -33,9 +62,9 @@ cmd_stat(struct cli *cli, int argc, const char *args) 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_print(&iob, "9:ntorrentsi%ue", torrent_count()); buf_swrite(&iob, "8:torrentsl"); - BTPDQ_FOREACH(tp, btpd_get_torrents(), entry) { + BTPDQ_FOREACH(tp, torrent_get_all(), entry) { if (tp->state == T_ACTIVE) { uint32_t seen_npieces = 0; for (uint32_t i = 0; i < tp->meta.npieces; i++) @@ -49,183 +78,96 @@ cmd_stat(struct cli *cli, int argc, const char *args) 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:statei%ue", tp->state); 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(); - } + buf_print(&iob, "5:statei%uee", tp->state); } } buf_swrite(&iob, "ee"); - - uint32_t len = iob.buf_off; - write_fully(cli->sd, &len, sizeof(len)); - write_fully(cli->sd, iob.buf, iob.buf_off); - free(iob.buf); - return 0; + return write_buffer(cli, &iob); } -#if 0 -static void -cmd_add(int argc, const char *args, FILE *fp) +static int +cmd_add(struct cli *cli, int argc, const char *args) { - struct io_buffer iob; - buf_init(&iob, (1 << 10)); - - buf_write(&iob, "l", 1); - while (args != NULL) { - size_t plen; - char path[PATH_MAX]; - const char *pathp; - - if (!benc_isstr(args)) { - free(iob.buf); - return; - } - - benc_str(args, &pathp, &plen, &args); - - if (plen >= PATH_MAX) { - buf_print(&iob, "d4:codei%dee", ENAMETOOLONG); - continue; - } + if (argc != 1) + return EINVAL; + if (btpd_is_stopping()) + return write_code_buffer(cli, IPC_FAIL); - bcopy(pathp, path, plen); - path[plen] = '\0'; - btpd_log(BTPD_L_BTPD, "add request for %s.\n", path); - buf_print(&iob, "d4:codei%dee", torrent_load(path)); + size_t hlen; + struct torrent *tp; + enum ipc_code code = IPC_OK; + const uint8_t *hash = benc_dget_mem(args, "hash", &hlen); + char *content = benc_dget_str(args, "content", NULL); + char *torrent = benc_dget_str(args, "torrent", NULL); + + if (!(hlen == 20 && content != NULL && torrent != NULL)) { + code = IPC_COMMERR; + goto out; } - buf_write(&iob, "e", 1); - - uint32_t len = iob.buf_off; - fwrite(&len, sizeof(len), 1, fp); - fwrite(iob.buf, 1, iob.buf_off, fp); - free(iob.buf); -} - -static void -cmd_del(int argc, const char *args, FILE *fp) -{ - struct io_buffer iob; - buf_init(&iob, (1 << 10)); - - buf_swrite(&iob, "l"); - - while (args != NULL) { - size_t len; - const char *hash; - struct torrent *tp; - - if (!benc_isstr(args) || - benc_str(args, &hash, &len, &args) != 0 || len != 20) { - free(iob.buf); - return; - } - - tp = btpd_get_torrent(hash); - if (tp != NULL) { - btpd_log(BTPD_L_BTPD, "del request for %s.\n", tp->relpath); - torrent_unload(tp); - buf_swrite(&iob, "d4:codei0ee"); - } else { - btpd_log(BTPD_L_BTPD, "del request didn't match.\n"); - buf_print(&iob, "d4:codei%dee", ENOENT); - } + if ((tp = torrent_get(hash)) != NULL) { + code = tp->state == T_STOPPING ? IPC_FAIL : IPC_OK; + goto out; } - buf_swrite(&iob, "e"); - - uint32_t len = iob.buf_off; - fwrite(&len, sizeof(len), 1, fp); - fwrite(iob.buf, 1, iob.buf_off, fp); - free(iob.buf); -} + if (torrent_set_links(hash, torrent, content) != 0) { + code = IPC_ERROR; + goto out; + } + if (torrent_start(hash) != 0) + code = IPC_ERROR; -#endif +out: + if (content != NULL) + free(content); + if (torrent != NULL) + free(torrent); -static int -cmd_die(struct cli *cli, int argc, const char *args) -{ - char res[] = "d4:codei0ee"; - uint32_t len = sizeof(res) - 1; - 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((& (struct timeval) { 0, 0 })); - return 0; + if (code == IPC_COMMERR) + return EINVAL; + else + return write_code_buffer(cli, code); } static int -cmd_start(struct cli *cli, int argc, const char *args) +cmd_del(struct cli *cli, int argc, const char *args) { - if (argc != 1 || !benc_isint(args)) + if (argc != 1 || !benc_isstr(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; + size_t hlen; + uint8_t *hash = (uint8_t *)benc_mem(args, &hlen, NULL); + if (hlen != 20) + return EINVAL; + struct torrent *tp = torrent_get(hash); + if (tp != NULL) + torrent_stop(tp); + return write_code_buffer(cli, IPC_OK); } static int -cmd_stop(struct cli *cli, int argc, const char *args) +cmd_die(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; + int err = write_code_buffer(cli, IPC_OK); + if (!btpd_is_stopping()) { + int grace_seconds = -1; + if (argc == 1 && benc_isint(args)) + grace_seconds = benc_int(args, NULL); + btpd_log(BTPD_L_BTPD, "Someone wants me dead.\n"); + btpd_shutdown(grace_seconds); + } + return err; } static struct { @@ -233,14 +175,10 @@ static struct { int nlen; int (*fun)(struct cli *cli, int, const char *); } cmd_table[] = { -#if 0 { "add", 3, cmd_add }, { "del", 3, cmd_del }, -#endif { "die", 3, cmd_die }, - { "start", 5, cmd_start }, - { "stat", 4, cmd_stat }, - { "stop", 4, cmd_stop } + { "stat", 4, cmd_stat } }; static int ncmds = sizeof(cmd_table) / sizeof(cmd_table[0]); @@ -248,22 +186,19 @@ static int ncmds = sizeof(cmd_table) / sizeof(cmd_table[0]); static int cmd_dispatch(struct cli *cli, const char *buf) { - int err = 0; size_t cmdlen; const char *cmd; const char *args; cmd = benc_mem(benc_first(buf), &cmdlen, &args); - 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 cmd_table[i].fun(cli, benc_nelems(buf) - 1, args); } } - return err; + return ENOENT; } static void diff --git a/btpd/content.c b/btpd/content.c index f132392..df19849 100644 --- a/btpd/content.c +++ b/btpd/content.c @@ -98,14 +98,14 @@ static int fd_cb_rd(const char *path, int *fd, void *arg) { struct torrent *tp = arg; - return vopen(fd, O_RDONLY, "library/%s/content/%s", tp->relpath, path); + return vopen(fd, O_RDONLY, "torrents/%s/content/%s", tp->relpath, path); } static int fd_cb_wr(const char *path, int *fd, void *arg) { struct torrent *tp = arg; - return vopen(fd, O_RDWR|O_CREAT, "library/%s/content/%s", tp->relpath, + return vopen(fd, O_RDWR|O_CREAT, "torrents/%s/content/%s", tp->relpath, path); } @@ -464,7 +464,7 @@ test_hash(struct torrent *tp, uint8_t *hash, uint32_t piece) int fd; int err; - err = vopen(&fd, O_RDONLY, "library/%s/torrent", tp->relpath); + err = vopen(&fd, O_RDONLY, "torrents/%s/torrent", tp->relpath); if (err != 0) btpd_err("test_hash: %s\n", strerror(err)); @@ -531,7 +531,7 @@ test_torrent(struct torrent *tp, volatile sig_atomic_t *cancel) uint8_t (*hashes)[SHA_DIGEST_LENGTH]; uint8_t hash[SHA_DIGEST_LENGTH]; - if ((err = vfopen(&fp, "r", "library/%s/torrent", tp->relpath)) != 0) + if ((err = vfopen(&fp, "r", "torrents/%s/torrent", tp->relpath)) != 0) return err; hashes = btpd_malloc(tp->meta.npieces * SHA_DIGEST_LENGTH); @@ -573,7 +573,7 @@ stat_and_adjust(struct torrent *tp, struct rstat ret[]) char path[PATH_MAX]; struct stat sb; for (int i = 0; i < tp->meta.nfiles; i++) { - snprintf(path, PATH_MAX, "library/%s/content/%s", tp->relpath, + snprintf(path, PATH_MAX, "torrents/%s/content/%s", tp->relpath, tp->meta.files[i].path); again: if (stat(path, &sb) == -1) { @@ -603,7 +603,7 @@ load_resume(struct torrent *tp, struct rstat sbs[]) size_t pfsiz = ceil(tp->meta.npieces / 8.0); size_t bfsiz = tp->meta.npieces * tp->cm->bppbf; - if ((err = vfopen(&fp, "r" , "library/%s/resume", tp->relpath)) != 0) + if ((err = vfopen(&fp, "r" , "torrents/%s/resume", tp->relpath)) != 0) return err; if (fscanf(fp, "%d\n", &ver) != 1) @@ -636,7 +636,7 @@ save_resume(struct torrent *tp, struct rstat sbs[]) { int err; FILE *fp; - if ((err = vfopen(&fp, "wb", "library/%s/resume", tp->relpath)) != 0) + if ((err = vfopen(&fp, "wb", "torrents/%s/resume", tp->relpath)) != 0) return err; fprintf(fp, "%d\n", 1); for (int i = 0; i < tp->meta.nfiles; i++) diff --git a/btpd/main.c b/btpd/main.c index 2e3e238..e397eee 100644 --- a/btpd/main.c +++ b/btpd/main.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -23,31 +22,17 @@ writepid(int pidfd) fclose(fp); } -static char * -find_homedir(void) -{ - char *res = getenv("BTPD_HOME"); - if (res == NULL) { - char *home = getenv("HOME"); - if (home == NULL) { - struct passwd *pwent = getpwuid(getuid()); - if (pwent == NULL) - errx(1, "Can't find my home directory.\n"); - home = pwent->pw_dir; - endpwent(); - } - asprintf(&res, "%s/.btpd", home); - } - return res; -} - static void -setup_daemon(const char *dir) +setup_daemon(int daemonize, const char *dir, const char *log) { int pidfd; + if (log == NULL) + log = "log"; + if (dir == NULL) - dir = find_homedir(); + if ((dir = find_btpd_dir()) == NULL) + errx(1, "Cannot find the btpd directory"); btpd_dir = dir; @@ -57,8 +42,8 @@ setup_daemon(const char *dir) if (chdir(dir) != 0) err(1, "Couldn't change working directory to '%s'", dir); - if (mkdir("library", 0777) == -1 && errno != EEXIST) - err(1, "Couldn't create library"); + if (mkdir("torrents", 0777) == -1 && errno != EEXIST) + err(1, "Couldn't create torrents subdir"); pidfd = open("pid", O_CREAT|O_WRONLY|O_NONBLOCK|O_EXLOCK, 0666); if (pidfd == -1) { @@ -69,12 +54,12 @@ setup_daemon(const char *dir) err(1, "Couldn't open 'pid'"); } - if (btpd_daemon) { + if (daemonize) { if (daemon(1, 1) != 0) err(1, "Failed to daemonize"); freopen("/dev/null", "r", stdin); - if (freopen("log", "a", stdout) == NULL) - err(1, "Couldn't open 'log'"); + if (freopen(log, "a", stdout) == NULL) + err(1, "Couldn't open '%s'", log); dup2(fileno(stdout), fileno(stderr)); setlinebuf(stdout); setlinebuf(stderr); @@ -89,11 +74,7 @@ usage(void) printf( "The BitTorrent Protocol Daemon.\n" "\n" - "Usage: btpd [options] [dir]\n" - "\n" - "Arguments:\n" - "dir\n" - "\tThe directory in which to run btpd. Default is '$HOME/.btpd'.\n" + "Usage: btpd [-d dir] [-p port] [more options...]\n" "\n" "Options:\n" "--bw-in n\n" @@ -104,9 +85,8 @@ usage(void) "\tLimit outgoing BitTorrent traffic to n kB/s.\n" "\tDefault is 0 which means unlimited.\n" "\n" - "-d\n" - "\tKeep the btpd process in the foregorund and log to std{out,err}.\n" - "\tThis option is intended for debugging purposes.\n" + "-d dir\n" + "\tThe directory in which to run btpd. Default is '$HOME/.btpd'.\n" "\n" "--downloaders n\n" "\tControls the number of simultaneous uploads.\n" @@ -119,9 +99,16 @@ usage(void) "--help\n" "\tShow this text.\n" "\n" + "--logfile file\n" + "\tWhere to put the logfile. By default it's put in the btpd dir.\n" + "\n" "--max-peers n\n" "\tLimit the amount of peers to n.\n" "\n" + "--no-daemon\n" + "\tKeep the btpd process in the foregorund and log to std{out,err}.\n" + "\tThis option is intended for debugging purposes.\n" + "\n" "-p n, --port n\n" "\tListen at port n. Default is 6881.\n" "\n" @@ -142,6 +129,8 @@ static struct option longopts[] = { { "prealloc", required_argument, &longval, 3 }, { "downloaders", required_argument, &longval, 4 }, { "max-peers", required_argument, &longval, 5 }, + { "no-daemon", no_argument, &longval, 6 }, + { "logfile", required_argument, &longval, 7 }, { "help", no_argument, &longval, 128 }, { NULL, 0, NULL, 0 } }; @@ -149,16 +138,17 @@ static struct option longopts[] = { int main(int argc, char **argv) { - char *dir = NULL; + char *dir = NULL, *log = NULL; + int daemonize = 1; setlocale(LC_ALL, ""); for (;;) { - switch (getopt_long(argc, argv, "dp:", longopts, NULL)) { + switch (getopt_long(argc, argv, "d:p:", longopts, NULL)) { case -1: goto args_done; case 'd': - btpd_daemon = 0; + dir = optarg; break; case 'p': net_port = atoi(optarg); @@ -180,6 +170,12 @@ main(int argc, char **argv) case 5: net_max_peers = atoi(optarg); break; + case 6: + daemonize = 0; + break; + case 7: + log = optarg; + break; default: usage(); } @@ -193,12 +189,10 @@ args_done: argc -= optind; argv += optind; - if (argc > 1) - usage(); if (argc > 0) - dir = argv[0]; + usage(); - setup_daemon(dir); + setup_daemon(daemonize, dir, log); event_init(); diff --git a/btpd/net.c b/btpd/net.c index 43b43f8..1fc25dc 100644 --- a/btpd/net.c +++ b/btpd/net.c @@ -448,9 +448,10 @@ net_connect2(struct sockaddr *sa, socklen_t salen, int *sd) set_nonblocking(*sd); if (connect(*sd, sa, salen) == -1 && errno != EINPROGRESS) { - btpd_log(BTPD_L_CONN, "Botched connection %s.", strerror(errno)); + int err = errno; + btpd_log(BTPD_L_CONN, "Botched connection %s.\n", strerror(errno)); close(*sd); - return errno; + return err; } return 0; diff --git a/btpd/opts.c b/btpd/opts.c index 7321727..2fc3d1c 100644 --- a/btpd/opts.c +++ b/btpd/opts.c @@ -1,6 +1,5 @@ #include -short btpd_daemon = 1; const char *btpd_dir; #ifdef DEBUG uint32_t btpd_logmask = BTPD_L_ALL; diff --git a/btpd/opts.h b/btpd/opts.h index c47ea63..4f48fa2 100644 --- a/btpd/opts.h +++ b/btpd/opts.h @@ -1,7 +1,6 @@ #ifndef BTPD_OPTS_H #define BTPD_OPTS_H -extern short btpd_daemon; extern const char *btpd_dir; extern uint32_t btpd_logmask; extern int net_max_downloaders; diff --git a/btpd/torrent.c b/btpd/torrent.c index b65b481..1202039 100644 --- a/btpd/torrent.c +++ b/btpd/torrent.c @@ -19,14 +19,28 @@ #include "tracker_req.h" #include "stream.h" -static unsigned m_next_num; +static unsigned m_ntorrents; +static struct torrent_tq m_torrents = BTPDQ_HEAD_INITIALIZER(m_torrents); -static unsigned -num_get_next(void) +const struct torrent_tq * +torrent_get_all(void) { - if (m_next_num == UINT_MAX) - btpd_err("Reached maximum torrent number.\n"); - return m_next_num++; + return &m_torrents; +} + +unsigned +torrent_count(void) +{ + return m_ntorrents; +} + +struct torrent * +torrent_get(const uint8_t *hash) +{ + struct torrent *tp = BTPDQ_FIRST(&m_torrents); + while (tp != NULL && bcmp(hash, tp->meta.info_hash, 20) != 0) + tp = BTPDQ_NEXT(tp, entry); + return tp; } off_t @@ -58,22 +72,71 @@ torrent_block_size(struct torrent *tp, uint32_t piece, uint32_t nblocks, } } -void -torrent_activate(struct torrent *tp) +static void +torrent_relpath(const uint8_t *hash, char *buf) +{ + for (int i = 0; i < 20; i++) + snprintf(buf + i * 2, 3, "%.2x", hash[i]); +} + +int +torrent_set_links(const uint8_t *hash, const char *torrent, + const char *content) { - if (tp->state == T_INACTIVE) { - tp->state = T_STARTING; - cm_start(tp); - btpd_tp_activated(tp); + char relpath[RELPATH_SIZE]; + char file[PATH_MAX]; + torrent_relpath(hash, relpath); + snprintf(file, PATH_MAX, "torrents/%s", relpath); + if (mkdir(file, 0777) == -1 && errno != EEXIST) + return errno; + snprintf(file, PATH_MAX, "torrents/%s/torrent", relpath); + if (unlink(file) == -1 && errno != ENOENT) + return errno; + if (symlink(torrent, file) == -1) + return errno; + snprintf(file, PATH_MAX, "torrents/%s/content", relpath); + if (unlink(file) == -1 && errno != ENOENT) + return errno; + if (symlink(content, file) == -1) + return errno; + return 0; +} + +int +torrent_start(const uint8_t *hash) +{ + struct torrent *tp; + struct metainfo *mi; + int error; + char relpath[RELPATH_SIZE]; + char file[PATH_MAX]; + + torrent_relpath(hash, relpath); + snprintf(file, PATH_MAX, "torrents/%s/torrent", relpath); + + if ((error = load_metainfo(file, -1, 0, &mi)) != 0) { + btpd_log(BTPD_L_ERROR, "Couldn't load torrent file %s: %s.\n", + file, strerror(error)); + return error; } + + btpd_log(BTPD_L_BTPD, "Starting torrent '%s'.\n", mi->name); + + tp = btpd_calloc(1, sizeof(*tp)); + bcopy(relpath, tp->relpath, RELPATH_SIZE); + tp->meta = *mi; + free(mi); + BTPDQ_INSERT_TAIL(&m_torrents, tp, entry); + m_ntorrents++; + cm_start(tp); + + return 0; } void -torrent_deactivate(struct torrent *tp) +torrent_stop(struct torrent *tp) { switch (tp->state) { - case T_INACTIVE: - break; case T_STARTING: case T_ACTIVE: tp->state = T_STOPPING; @@ -88,69 +151,43 @@ torrent_deactivate(struct torrent *tp) if (tp->tr != NULL) tr_destroy(tp); break; - default: - abort(); } } -int -torrent_load(struct torrent **res, const char *path) +static void +torrent_kill(struct torrent *tp) { - struct metainfo *mi; - int error; - char file[PATH_MAX]; - snprintf(file, PATH_MAX, "library/%s/torrent", path); - - if ((error = load_metainfo(file, -1, 0, &mi)) != 0) { - btpd_log(BTPD_L_ERROR, "Couldn't load metainfo file %s: %s.\n", - file, strerror(error)); - return error; - } - - if (btpd_get_torrent(mi->info_hash) != NULL) { - btpd_log(BTPD_L_BTPD, - "%s has same hash as an already loaded torrent.\n", path); - error = EEXIST; - } - - if (error == 0) { - *res = btpd_calloc(1, sizeof(**res)); - (*res)->relpath = strdup(path); - (*res)->meta = *mi; - (*res)->num = num_get_next(); - free(mi); - } else { - clear_metainfo(mi); - free(mi); - } - - return error; + btpd_log(BTPD_L_BTPD, "Removed torrent '%s'.\n", tp->meta.name); + assert(m_ntorrents > 0); + m_ntorrents--; + BTPDQ_REMOVE(&m_torrents, tp, entry); + clear_metainfo(&tp->meta); + free(tp); + if (m_ntorrents == 0) + btpd_on_no_torrents(); } void torrent_on_cm_started(struct torrent *tp) { - net_add_torrent(tp); - tr_start(tp); tp->state = T_ACTIVE; + net_add_torrent(tp); + if (tr_start(tp) != 0) + torrent_stop(tp); } void torrent_on_cm_stopped(struct torrent *tp) { assert(tp->state == T_STOPPING); - if (tp->tr == NULL) { - tp->state = T_INACTIVE; - btpd_tp_deactivated(tp); - } + if (tp->tr == NULL) + torrent_kill(tp); } void torrent_on_tr_stopped(struct torrent *tp) { assert(tp->state == T_STOPPING); - if (tp->cm == NULL) { - tp->state = T_INACTIVE; - btpd_tp_deactivated(tp); - } + if (tp->cm == NULL) + torrent_kill(tp); } diff --git a/btpd/torrent.h b/btpd/torrent.h index 4a1542e..c9a942b 100644 --- a/btpd/torrent.h +++ b/btpd/torrent.h @@ -2,17 +2,16 @@ #define BTPD_TORRENT_H #define PIECE_BLOCKLEN (1 << 14) +#define RELPATH_SIZE 41 enum torrent_state { - T_INACTIVE, T_STARTING, T_ACTIVE, T_STOPPING }; struct torrent { - unsigned num; - const char *relpath; + char relpath[RELPATH_SIZE]; struct metainfo meta; enum torrent_state state; @@ -26,9 +25,14 @@ struct torrent { BTPDQ_HEAD(torrent_tq, torrent); -int torrent_load(struct torrent **res, const char *path); -void torrent_activate(struct torrent *tp); -void torrent_deactivate(struct torrent *tp); +unsigned torrent_count(void); +const struct torrent_tq *torrent_get_all(void); +struct torrent *torrent_get(const uint8_t *hash); + +int torrent_start(const uint8_t *hash); +void torrent_stop(struct torrent *tp); +int torrent_set_links(const uint8_t *hash, const char *torrent, + const char *content); off_t torrent_piece_size(struct torrent *tp, uint32_t piece); uint32_t torrent_piece_blocks(struct torrent *tp, uint32_t piece); diff --git a/cli/btcli.c b/cli/btcli.c index d5e20b6..3971d77 100644 --- a/cli/btcli.c +++ b/cli/btcli.c @@ -1,66 +1,179 @@ +#include +#include + #include #include +#include #include +#include #include #include #include +#include #include "btpd_if.h" +#include "metainfo.h" +#include "subr.h" -static const char *btpd_dir = "/usr/btpd"; -static struct ipc *ipc; +const char *btpd_dir; +struct ipc *ipc; -static void -handle_ipc_res(enum ipc_code code) +void +btpd_connect(void) +{ + if ((errno = ipc_open(btpd_dir, &ipc)) != 0) + err(1, "cannot open connection to btpd in %s", btpd_dir); +} + +enum ipc_code +handle_ipc_res(enum ipc_code code, const char *target) { switch (code) { case IPC_OK: - return; + break; case IPC_FAIL: - warnx("Ipc failed.\n"); + warnx("btpd couldn't execute the requested operation for %s", target); + break; + case IPC_ERROR: + warnx("btpd encountered an error for %s", target); break; - case IPC_COMMERR: - errx(1, "Communication error.\n"); + default: + errx(1, "fatal error in communication with btpd"); } + return code; } -static void -btpd_connect(void) +void +print_state_name(struct tpstat *ts) { - if ((errno = ipc_open(btpd_dir, &ipc)) != 0) - errx(1, "Couldn't connect to btpd in %s (%s).\n", - btpd_dir, strerror(errno)); + char statec[] = ">*state, 3); + printf("%c. %s", statec[state], ts->name); +} + +void +print_stat(struct tpstat *cur) +{ + printf("%5.1f%% %6.1fM %7.2fkB/s %6.1fM %7.2fkB/s %4u %5.1f%%", + 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"); } void usage_add(void) { printf( - "Add a torrent to btpd.\n" + "Add torrents 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" + "Usage: add [--topdir] -d dir file\n" + " add file ...\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" + "Arguments:\n" + "file ...\n" + "\tOne or more torrents to add.\n" "\n" - "-f file\n" - "\tThe torrent to add.\n" + "Options:\n" + "-d dir\n" + "\tUse the dir for content.\n" "\n" - "-s\n" - "\tStart the torrent.\n" + "--topdir\n" + "\tAppend the torrent top directory (if any) to the content path.\n" + "\tThis option cannot be used without the '-d' option.\n" "\n" ); exit(1); } +struct option add_opts [] = { + { "help", no_argument, NULL, 'H' }, + { "topdir", no_argument, NULL, 'T'}, + {NULL, 0, NULL, 0} +}; + +int +content_link(uint8_t *hash, char *buf) +{ + int n; + char relpath[41]; + char path[PATH_MAX]; + for (int i = 0; i < 20; i++) + snprintf(relpath + i * 2, 3, "%.2x", hash[i]); + snprintf(path, PATH_MAX, "%s/torrents/%s/content", btpd_dir, relpath); + if ((n = readlink(path, buf, PATH_MAX)) == -1) + return errno; + buf[min(n, PATH_MAX)] = '\0'; + return 0; +} + void cmd_add(int argc, char **argv) { + int ch, topdir = 0; + char *dir = NULL, bdir[PATH_MAX]; + + while ((ch = getopt_long(argc, argv, "d:", add_opts, NULL)) != -1) { + switch (ch) { + case 'T': + topdir = 1; + break; + case 'd': + dir = optarg; + break; + default: + usage_add(); + } + } + argc -= optind; + argv += optind; + + if (argc < 1 || (topdir == 1 && dir == NULL) || (dir != NULL && argc > 1)) + usage_add(); + + if (dir != NULL) + if (realpath(dir, bdir) == NULL) + err(1, "path error on %s", bdir); + + btpd_connect(); + for (int i = 0; i < argc; i++) { + struct metainfo *mi; + char dpath[PATH_MAX], fpath[PATH_MAX]; + + if ((errno = load_metainfo(argv[i], -1, 0, &mi)) != 0) { + warn("error loading torrent %s", argv[i]); + continue; + } + + if ((topdir && + !(mi->nfiles == 1 + && strcmp(mi->name, mi->files[0].path) == 0))) + snprintf(dpath, PATH_MAX, "%s/%s", bdir, mi->name); + else if (dir != NULL) + strlcpy(dpath, bdir, PATH_MAX); + else { + if (content_link(mi->info_hash, dpath) != 0) { + warnx("unknown content dir for %s", argv[i]); + errx(1, "use the '-d' option"); + } + } + + if (mkdir(dpath, 0777) != 0 && errno != EEXIST) + err(1, "couldn't create directory %s", dpath); + + if (realpath(argv[i], fpath) == NULL) + err(1, "path error on %s", fpath); + + handle_ipc_res(btpd_add(ipc, mi->info_hash, fpath, dpath), argv[1]); + clear_metainfo(mi); + free(mi); + } } void @@ -69,11 +182,11 @@ usage_del(void) printf( "Remove torrents from btpd.\n" "\n" - "Usage: del num ...\n" + "Usage: del file ...\n" "\n" "Arguments:\n" - "num\n" - "\tThe number of the torrent to remove.\n" + "file ...\n" + "\tThe torrents to remove.\n" "\n"); exit(1); } @@ -84,16 +197,17 @@ cmd_del(int argc, char **argv) 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(); - } btpd_connect(); - for (int i = 0; i < argc -1; i++) - handle_ipc_res(btpd_del_num(ipc, nums[i])); + for (int i = 1; i < argc; i++) { + struct metainfo *mi; + if ((errno = load_metainfo(argv[i], -1, 0, &mi)) != 0) { + warn("error loading torrent %s", argv[i]); + continue; + } + handle_ipc_res(btpd_del(ipc, mi->info_hash), argv[i]); + clear_metainfo(mi); + free(mi); + } } void @@ -118,24 +232,23 @@ cmd_kill(int argc, char **argv) { int seconds = -1; char *endptr; - if (argc == 1) - ; - else if (argc == 2) { + + if (argc == 2) { seconds = strtol(argv[1], &endptr, 10); if (strlen(argv[1]) > endptr - argv[1] || seconds < 0) usage_kill(); - } else + } else if (argc > 2) usage_kill(); btpd_connect(); - btpd_die(ipc, seconds); + handle_ipc_res(btpd_die(ipc, seconds), "kill"); } void usage_list(void) { printf( - "List btpd's torrents.\n" + "List active torrents.\n" "\n" "Usage: list\n" "\n" @@ -152,12 +265,13 @@ cmd_list(int argc, char **argv) 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, + if (handle_ipc_res(btpd_stat(ipc, &st), "list") != IPC_OK) + exit(1); + for (int i = 0; i < st->ntorrents; i++) { + print_state_name(&st->torrents[i]); + putchar('\n'); + } + printf("%u torrent%s.\n", st->ntorrents, st->ntorrents == 1 ? "" : "s"); } @@ -166,9 +280,9 @@ usage_stat(void) { printf( "Display stats for active torrents.\n" - "The stats displayed are:\n" + "The displayed stats are:\n" "%% got, MB down, rate down. MB up, rate up\n" - "peers, %% of pieces seen, tracker errors\n" + "peer count, %% of pieces seen, tracker errors\n" "\n" "Usage: stat [-i] [-w seconds]\n" "\n" @@ -182,23 +296,6 @@ usage_stat(void) exit(1); } -void -print_stat(struct tpstat *cur) -{ - printf("%5.1f%% %6.1fM %7.2fkB/s %6.1fM %7.2fkB/s %4u %5.1f%%", - 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"); -} - void do_stat(int individual, int seconds) { @@ -206,13 +303,11 @@ do_stat(int individual, int seconds) struct tpstat tot; again: bzero(&tot, sizeof(tot)); - tot.num = -1; - if ((errno = btpd_stat(ipc, &st)) != 0) - err(1, "btpd_stat"); + tot.state = T_ACTIVE; + if (handle_ipc_res(btpd_stat(ipc, &st), "stat") != IPC_OK) + exit(1); for (int i = 0; i < st->ntorrents; i++) { struct tpstat *cur = &st->torrents[i]; - if (cur->state != 'A') - continue; tot.uploaded += cur->uploaded; tot.downloaded += cur->downloaded; tot.rate_up += cur->rate_up; @@ -223,7 +318,8 @@ again: tot.have += cur->have; tot.total += cur->total; if (individual) { - printf("%u. %s:\n", cur->num, cur->name); + print_state_name(cur); + printf(":\n"); print_stat(cur); } } @@ -237,8 +333,8 @@ again: } } -static struct option stat_opts [] = { - { "help", no_argument, NULL, 1 }, +struct option stat_opts [] = { + { "help", no_argument, NULL, 'H' }, {NULL, 0, NULL, 0} }; @@ -272,73 +368,7 @@ cmd_stat(int argc, char **argv) do_stat(iflag, seconds); } -void -usage_start(void) -{ - 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); -} - -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(); - } - 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(); - } - btpd_connect(); - for (int i = 0; i < argc -1; i++) - handle_ipc_res(btpd_stop_num(ipc, nums[i])); -} - -static struct { +struct { const char *name; void (*fun)(int, char **); void (*help)(void); @@ -347,19 +377,16 @@ static struct { { "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 } + { "stat", cmd_stat, usage_stat } }; -static int ncmds = sizeof(cmd_table) / sizeof(cmd_table[0]); +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" + "btcli is the btpd command line interface.\n" "\n" "Usage: btcli [main options] command [command options]\n" "\n" @@ -375,15 +402,13 @@ usage(void) "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 }, +struct option base_opts [] = { + { "help", no_argument, NULL, 'H' }, {NULL, 0, NULL, 0} }; @@ -400,7 +425,7 @@ main(int argc, char **argv) case 'd': btpd_dir = optarg; break; - case 1: + case 'H': help = 1; break; default: @@ -413,6 +438,10 @@ main(int argc, char **argv) if (argc == 0) usage(); + if (btpd_dir == NULL) + if ((btpd_dir = find_btpd_dir()) == NULL) + errx(1, "cannot find the btpd directory"); + optind = 0; int found = 0; for (int i = 0; !found && i < ncmds; i++) { @@ -424,7 +453,7 @@ main(int argc, char **argv) cmd_table[i].fun(argc, argv); } } - + if (!found) usage(); diff --git a/cli/btpd_if.c b/cli/btpd_if.c index a5fa40d..c328fb6 100644 --- a/cli/btpd_if.c +++ b/cli/btpd_if.c @@ -1,3 +1,7 @@ +#include +#include +#include + #include #include #include @@ -81,7 +85,7 @@ ipc_response(struct ipc *ipc, char **out, uint32_t *len) *len = size; return 0; } - + static int ipc_req_res(struct ipc *ipc, const char *req, uint32_t qlen, char **res, uint32_t *rlen) @@ -125,7 +129,7 @@ btpd_die(struct ipc *ipc, int seconds) if (seconds >= 0) buf_print(&iob, "l3:diei%dee", seconds); else - buf_print(&iob, "l3:diee"); + buf_swrite(&iob, "l3:diee"); return ipc_buf_req(ipc, &iob); } @@ -150,21 +154,18 @@ parse_btstat(const uint8_t *res, struct btstat **out) 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"); - } + ts->state = benc_dget_int(tp, "state"); + 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, "down"); + ts->uploaded = benc_dget_int(tp, "up"); + 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++; } *out = st; @@ -197,29 +198,26 @@ btpd_stat(struct ipc *ipc, struct btstat **out) return err; } -static enum ipc_code -btpd_common_num(struct ipc *ipc, const char *cmd, unsigned num) -{ - struct io_buffer iob; - buf_init(&iob, 16); - buf_print(&iob, "l%d:%si%uee", (int)strlen(cmd), cmd, num); - return ipc_buf_req(ipc, &iob); -} - enum ipc_code -btpd_del_num(struct ipc *ipc, unsigned num) +btpd_add(struct ipc *ipc, const uint8_t *hash, const char *torrent, + const char *content) { - return btpd_common_num(ipc, "del", num); -} - -enum ipc_code -btpd_start_num(struct ipc *ipc, unsigned num) -{ - return btpd_common_num(ipc, "start", num); + struct io_buffer iob; + buf_init(&iob, (1 << 10)); + buf_print(&iob, "l3:addd7:content%d:%s4:hash20:", (int)strlen(content), + content); + buf_write(&iob, hash, 20); + buf_print(&iob, "7:torrent%d:%see", (int)strlen(torrent), torrent); + return ipc_buf_req(ipc, &iob); } enum ipc_code -btpd_stop_num(struct ipc *ipc, unsigned num) +btpd_del(struct ipc *ipc, const uint8_t *hash) { - return btpd_common_num(ipc, "stop", num); + struct io_buffer iob; + buf_init(&iob, 32); + buf_swrite(&iob, "l3:del20:"); + buf_write(&iob, hash, 20); + buf_write(&iob, "e", 1); + return ipc_buf_req(ipc, &iob); } diff --git a/cli/btpd_if.h b/cli/btpd_if.h index 64cdb4b..8c35cc7 100644 --- a/cli/btpd_if.h +++ b/cli/btpd_if.h @@ -1,15 +1,18 @@ #ifndef BTPD_IF_H #define BTPD_IF_H -#include -#include -#include - struct ipc; +enum torrent_state { //XXX: Same as in btpd/torrent.h + T_STARTING, + T_ACTIVE, + T_STOPPING +}; + enum ipc_code { IPC_OK, IPC_FAIL, + IPC_ERROR, IPC_COMMERR }; @@ -17,8 +20,7 @@ struct btstat { unsigned ntorrents; struct tpstat { char *name; - unsigned num; - char state; + enum torrent_state state; unsigned errors; unsigned npeers; @@ -32,13 +34,11 @@ struct btstat { int ipc_open(const char *dir, struct ipc **out); int ipc_close(struct ipc *ipc); +enum ipc_code btpd_add(struct ipc *ipc, const uint8_t *hash, + const char *torrent, const char *content); +enum ipc_code btpd_del(struct ipc *ipc, const uint8_t *hash); enum ipc_code btpd_die(struct ipc *ipc, int seconds); enum ipc_code btpd_stat(struct ipc *ipc, struct btstat **out); - -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