Переглянути джерело

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 :)
master
Richard Nyberg 19 роки тому
джерело
коміт
2550d6cb8c
13 змінених файлів з 496 додано та 584 видалено
  1. +20
    -98
      btpd/btpd.c
  2. +4
    -10
      btpd/btpd.h
  3. +93
    -158
      btpd/cli_if.c
  4. +7
    -7
      btpd/content.c
  5. +35
    -41
      btpd/main.c
  6. +3
    -2
      btpd/net.c
  7. +0
    -1
      btpd/opts.c
  8. +0
    -1
      btpd/opts.h
  9. +95
    -58
      btpd/torrent.c
  10. +10
    -6
      btpd/torrent.h
  11. +185
    -156
      cli/btcli.c
  12. +33
    -35
      cli/btpd_if.c
  13. +11
    -11
      cli/btpd_if.h

+ 20
- 98
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);


+ 4
- 10
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

+ 93
- 158
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


+ 7
- 7
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++)


+ 35
- 41
btpd/main.c Переглянути файл

@@ -7,7 +7,6 @@
#include <fcntl.h>
#include <getopt.h>
#include <locale.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -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();



+ 3
- 2
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;


+ 0
- 1
btpd/opts.c Переглянути файл

@@ -1,6 +1,5 @@
#include <btpd.h>

short btpd_daemon = 1;
const char *btpd_dir;
#ifdef DEBUG
uint32_t btpd_logmask = BTPD_L_ALL;


+ 0
- 1
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;


+ 95
- 58
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);
}

+ 10
- 6
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);


+ 185
- 156
cli/btcli.c Переглянути файл

@@ -1,66 +1,179 @@
#include <sys/types.h>
#include <sys/stat.h>

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

#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[] = ">*<U";
int state = min(ts->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();



+ 33
- 35
cli/btpd_if.c Переглянути файл

@@ -1,3 +1,7 @@
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

#include <ctype.h>
#include <err.h>
#include <errno.h>
@@ -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);
}

+ 11
- 11
cli/btpd_if.h Переглянути файл

@@ -1,15 +1,18 @@
#ifndef BTPD_IF_H
#define BTPD_IF_H

#include <sys/types.h>
#include <sys/socket.h>
#include <sys/un.h>

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

Завантаження…
Відмінити
Зберегти