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
@@ -32,9 +32,6 @@ | |||||
static uint8_t m_peer_id[20]; | static uint8_t m_peer_id[20]; | ||||
static struct event m_sigint; | static struct event m_sigint; | ||||
static struct event m_sigterm; | 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; | static int m_shutdown; | ||||
void | void | ||||
@@ -44,92 +41,35 @@ btpd_exit(int code) | |||||
exit(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 | static void | ||||
grace_cb(int fd, short type, void *arg) | grace_cb(int fd, short type, void *arg) | ||||
{ | { | ||||
struct torrent *tp; | struct torrent *tp; | ||||
BTPDQ_FOREACH(tp, &m_torrents, entry) | |||||
torrent_deactivate(tp); | |||||
BTPDQ_FOREACH(tp, torrent_get_all(), entry) | |||||
torrent_stop(tp); | |||||
} | } | ||||
void | void | ||||
btpd_shutdown(struct timeval *grace_tv) | |||||
btpd_shutdown(int grace_seconds) | |||||
{ | { | ||||
if (m_nactive == 0) | |||||
if (torrent_count() == 0) | |||||
btpd_exit(0); | btpd_exit(0); | ||||
else { | else { | ||||
struct torrent *tp; | struct torrent *tp; | ||||
m_shutdown = 1; | 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 * | const uint8_t * | ||||
@@ -138,28 +78,18 @@ btpd_get_peer_id(void) | |||||
return m_peer_id; | 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 | 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 { | struct td_cb { | ||||
@@ -260,14 +190,6 @@ btpd_init(void) | |||||
ul_init(); | ul_init(); | ||||
cm_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(SIGPIPE, SIG_IGN); | ||||
signal_set(&m_sigint, SIGINT, signal_cb, NULL); | signal_set(&m_sigint, SIGINT, signal_cb, NULL); | ||||
@@ -48,14 +48,9 @@ void btpd_err(const char *fmt, ...); | |||||
void *btpd_malloc(size_t size); | void *btpd_malloc(size_t size); | ||||
void *btpd_calloc(size_t nmemb, 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); | const uint8_t *btpd_get_peer_id(void); | ||||
void td_acquire_lock(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(void (*fun)(void *), void *arg); | ||||
void td_post_end(void); | 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 | #endif |
@@ -19,10 +19,39 @@ struct cli { | |||||
struct event read; | struct event read; | ||||
}; | }; | ||||
#define buf_swrite(iob, s) buf_write(iob, s, sizeof(s) - 1) | |||||
static struct event m_cli_incoming; | 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 | static int | ||||
cmd_stat(struct cli *cli, int argc, const char *args) | 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, "d"); | ||||
buf_swrite(&iob, "4:codei0e"); | buf_swrite(&iob, "4:codei0e"); | ||||
buf_print(&iob, "6:npeersi%ue", net_npeers); | 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"); | buf_swrite(&iob, "8:torrentsl"); | ||||
BTPDQ_FOREACH(tp, btpd_get_torrents(), entry) { | |||||
BTPDQ_FOREACH(tp, torrent_get_all(), entry) { | |||||
if (tp->state == T_ACTIVE) { | if (tp->state == T_ACTIVE) { | ||||
uint32_t seen_npieces = 0; | uint32_t seen_npieces = 0; | ||||
for (uint32_t i = 0; i < tp->meta.npieces; i++) | 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, "4:havei%jde", (intmax_t)cm_get_size(tp)); | ||||
buf_print(&iob, "6:npeersi%ue", tp->net->npeers); | buf_print(&iob, "6:npeersi%ue", tp->net->npeers); | ||||
buf_print(&iob, "7:npiecesi%ue", tp->meta.npieces); | 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), | buf_print(&iob, "4:path%d:%s", (int)strlen(tp->meta.name), | ||||
tp->meta.name); | tp->meta.name); | ||||
buf_print(&iob, "2:rdi%lue", tp->net->rate_dwn); | buf_print(&iob, "2:rdi%lue", tp->net->rate_dwn); | ||||
buf_print(&iob, "2:rui%lue", tp->net->rate_up); | buf_print(&iob, "2:rui%lue", tp->net->rate_up); | ||||
buf_print(&iob, "12:seen npiecesi%ue", seen_npieces); | 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, "5:totali%jde", (intmax_t)tp->meta.total_length); | ||||
buf_print(&iob, "2:upi%juee", (intmax_t)tp->net->uploaded); | buf_print(&iob, "2:upi%juee", (intmax_t)tp->net->uploaded); | ||||
} else { | } else { | ||||
buf_swrite(&iob, "d4:hash20:"); | buf_swrite(&iob, "d4:hash20:"); | ||||
buf_write(&iob, tp->meta.info_hash, 20); | 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), | buf_print(&iob, "4:path%d:%s", (int)strlen(tp->meta.name), | ||||
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"); | 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 | 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; | 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 | 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 { | static struct { | ||||
@@ -233,14 +175,10 @@ static struct { | |||||
int nlen; | int nlen; | ||||
int (*fun)(struct cli *cli, int, const char *); | int (*fun)(struct cli *cli, int, const char *); | ||||
} cmd_table[] = { | } cmd_table[] = { | ||||
#if 0 | |||||
{ "add", 3, cmd_add }, | { "add", 3, cmd_add }, | ||||
{ "del", 3, cmd_del }, | { "del", 3, cmd_del }, | ||||
#endif | |||||
{ "die", 3, cmd_die }, | { "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]); | 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 | static int | ||||
cmd_dispatch(struct cli *cli, const char *buf) | cmd_dispatch(struct cli *cli, const char *buf) | ||||
{ | { | ||||
int err = 0; | |||||
size_t cmdlen; | size_t cmdlen; | ||||
const char *cmd; | const char *cmd; | ||||
const char *args; | const char *args; | ||||
cmd = benc_mem(benc_first(buf), &cmdlen, &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++) { | for (int i = 0; i < ncmds; i++) { | ||||
if ((cmdlen == cmd_table[i].nlen && | if ((cmdlen == cmd_table[i].nlen && | ||||
strncmp(cmd_table[i].name, cmd, cmdlen) == 0)) { | 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 | static void | ||||
@@ -98,14 +98,14 @@ static int | |||||
fd_cb_rd(const char *path, int *fd, void *arg) | fd_cb_rd(const char *path, int *fd, void *arg) | ||||
{ | { | ||||
struct torrent *tp = 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 | static int | ||||
fd_cb_wr(const char *path, int *fd, void *arg) | fd_cb_wr(const char *path, int *fd, void *arg) | ||||
{ | { | ||||
struct torrent *tp = 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); | path); | ||||
} | } | ||||
@@ -464,7 +464,7 @@ test_hash(struct torrent *tp, uint8_t *hash, uint32_t piece) | |||||
int fd; | int fd; | ||||
int err; | 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) | if (err != 0) | ||||
btpd_err("test_hash: %s\n", strerror(err)); | 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 (*hashes)[SHA_DIGEST_LENGTH]; | ||||
uint8_t hash[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; | return err; | ||||
hashes = btpd_malloc(tp->meta.npieces * SHA_DIGEST_LENGTH); | 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]; | char path[PATH_MAX]; | ||||
struct stat sb; | struct stat sb; | ||||
for (int i = 0; i < tp->meta.nfiles; i++) { | 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); | tp->meta.files[i].path); | ||||
again: | again: | ||||
if (stat(path, &sb) == -1) { | 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 pfsiz = ceil(tp->meta.npieces / 8.0); | ||||
size_t bfsiz = tp->meta.npieces * tp->cm->bppbf; | 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; | return err; | ||||
if (fscanf(fp, "%d\n", &ver) != 1) | if (fscanf(fp, "%d\n", &ver) != 1) | ||||
@@ -636,7 +636,7 @@ save_resume(struct torrent *tp, struct rstat sbs[]) | |||||
{ | { | ||||
int err; | int err; | ||||
FILE *fp; | 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; | return err; | ||||
fprintf(fp, "%d\n", 1); | fprintf(fp, "%d\n", 1); | ||||
for (int i = 0; i < tp->meta.nfiles; i++) | for (int i = 0; i < tp->meta.nfiles; i++) | ||||
@@ -7,7 +7,6 @@ | |||||
#include <fcntl.h> | #include <fcntl.h> | ||||
#include <getopt.h> | #include <getopt.h> | ||||
#include <locale.h> | #include <locale.h> | ||||
#include <pwd.h> | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
@@ -23,31 +22,17 @@ writepid(int pidfd) | |||||
fclose(fp); | 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 | static void | ||||
setup_daemon(const char *dir) | |||||
setup_daemon(int daemonize, const char *dir, const char *log) | |||||
{ | { | ||||
int pidfd; | int pidfd; | ||||
if (log == NULL) | |||||
log = "log"; | |||||
if (dir == NULL) | if (dir == NULL) | ||||
dir = find_homedir(); | |||||
if ((dir = find_btpd_dir()) == NULL) | |||||
errx(1, "Cannot find the btpd directory"); | |||||
btpd_dir = dir; | btpd_dir = dir; | ||||
@@ -57,8 +42,8 @@ setup_daemon(const char *dir) | |||||
if (chdir(dir) != 0) | if (chdir(dir) != 0) | ||||
err(1, "Couldn't change working directory to '%s'", dir); | 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); | pidfd = open("pid", O_CREAT|O_WRONLY|O_NONBLOCK|O_EXLOCK, 0666); | ||||
if (pidfd == -1) { | if (pidfd == -1) { | ||||
@@ -69,12 +54,12 @@ setup_daemon(const char *dir) | |||||
err(1, "Couldn't open 'pid'"); | err(1, "Couldn't open 'pid'"); | ||||
} | } | ||||
if (btpd_daemon) { | |||||
if (daemonize) { | |||||
if (daemon(1, 1) != 0) | if (daemon(1, 1) != 0) | ||||
err(1, "Failed to daemonize"); | err(1, "Failed to daemonize"); | ||||
freopen("/dev/null", "r", stdin); | 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)); | dup2(fileno(stdout), fileno(stderr)); | ||||
setlinebuf(stdout); | setlinebuf(stdout); | ||||
setlinebuf(stderr); | setlinebuf(stderr); | ||||
@@ -89,11 +74,7 @@ usage(void) | |||||
printf( | printf( | ||||
"The BitTorrent Protocol Daemon.\n" | "The BitTorrent Protocol Daemon.\n" | ||||
"\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" | "\n" | ||||
"Options:\n" | "Options:\n" | ||||
"--bw-in n\n" | "--bw-in n\n" | ||||
@@ -104,9 +85,8 @@ usage(void) | |||||
"\tLimit outgoing BitTorrent traffic to n kB/s.\n" | "\tLimit outgoing BitTorrent traffic to n kB/s.\n" | ||||
"\tDefault is 0 which means unlimited.\n" | "\tDefault is 0 which means unlimited.\n" | ||||
"\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" | "\n" | ||||
"--downloaders n\n" | "--downloaders n\n" | ||||
"\tControls the number of simultaneous uploads.\n" | "\tControls the number of simultaneous uploads.\n" | ||||
@@ -119,9 +99,16 @@ usage(void) | |||||
"--help\n" | "--help\n" | ||||
"\tShow this text.\n" | "\tShow this text.\n" | ||||
"\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" | "--max-peers n\n" | ||||
"\tLimit the amount of peers to n.\n" | "\tLimit the amount of peers to n.\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" | "-p n, --port n\n" | ||||
"\tListen at port n. Default is 6881.\n" | "\tListen at port n. Default is 6881.\n" | ||||
"\n" | "\n" | ||||
@@ -142,6 +129,8 @@ static struct option longopts[] = { | |||||
{ "prealloc", required_argument, &longval, 3 }, | { "prealloc", required_argument, &longval, 3 }, | ||||
{ "downloaders", required_argument, &longval, 4 }, | { "downloaders", required_argument, &longval, 4 }, | ||||
{ "max-peers", required_argument, &longval, 5 }, | { "max-peers", required_argument, &longval, 5 }, | ||||
{ "no-daemon", no_argument, &longval, 6 }, | |||||
{ "logfile", required_argument, &longval, 7 }, | |||||
{ "help", no_argument, &longval, 128 }, | { "help", no_argument, &longval, 128 }, | ||||
{ NULL, 0, NULL, 0 } | { NULL, 0, NULL, 0 } | ||||
}; | }; | ||||
@@ -149,16 +138,17 @@ static struct option longopts[] = { | |||||
int | int | ||||
main(int argc, char **argv) | main(int argc, char **argv) | ||||
{ | { | ||||
char *dir = NULL; | |||||
char *dir = NULL, *log = NULL; | |||||
int daemonize = 1; | |||||
setlocale(LC_ALL, ""); | setlocale(LC_ALL, ""); | ||||
for (;;) { | for (;;) { | ||||
switch (getopt_long(argc, argv, "dp:", longopts, NULL)) { | |||||
switch (getopt_long(argc, argv, "d:p:", longopts, NULL)) { | |||||
case -1: | case -1: | ||||
goto args_done; | goto args_done; | ||||
case 'd': | case 'd': | ||||
btpd_daemon = 0; | |||||
dir = optarg; | |||||
break; | break; | ||||
case 'p': | case 'p': | ||||
net_port = atoi(optarg); | net_port = atoi(optarg); | ||||
@@ -180,6 +170,12 @@ main(int argc, char **argv) | |||||
case 5: | case 5: | ||||
net_max_peers = atoi(optarg); | net_max_peers = atoi(optarg); | ||||
break; | break; | ||||
case 6: | |||||
daemonize = 0; | |||||
break; | |||||
case 7: | |||||
log = optarg; | |||||
break; | |||||
default: | default: | ||||
usage(); | usage(); | ||||
} | } | ||||
@@ -193,12 +189,10 @@ args_done: | |||||
argc -= optind; | argc -= optind; | ||||
argv += optind; | argv += optind; | ||||
if (argc > 1) | |||||
usage(); | |||||
if (argc > 0) | if (argc > 0) | ||||
dir = argv[0]; | |||||
usage(); | |||||
setup_daemon(dir); | |||||
setup_daemon(daemonize, dir, log); | |||||
event_init(); | event_init(); | ||||
@@ -448,9 +448,10 @@ net_connect2(struct sockaddr *sa, socklen_t salen, int *sd) | |||||
set_nonblocking(*sd); | set_nonblocking(*sd); | ||||
if (connect(*sd, sa, salen) == -1 && errno != EINPROGRESS) { | 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); | close(*sd); | ||||
return errno; | |||||
return err; | |||||
} | } | ||||
return 0; | return 0; | ||||
@@ -1,6 +1,5 @@ | |||||
#include <btpd.h> | #include <btpd.h> | ||||
short btpd_daemon = 1; | |||||
const char *btpd_dir; | const char *btpd_dir; | ||||
#ifdef DEBUG | #ifdef DEBUG | ||||
uint32_t btpd_logmask = BTPD_L_ALL; | uint32_t btpd_logmask = BTPD_L_ALL; | ||||
@@ -1,7 +1,6 @@ | |||||
#ifndef BTPD_OPTS_H | #ifndef BTPD_OPTS_H | ||||
#define BTPD_OPTS_H | #define BTPD_OPTS_H | ||||
extern short btpd_daemon; | |||||
extern const char *btpd_dir; | extern const char *btpd_dir; | ||||
extern uint32_t btpd_logmask; | extern uint32_t btpd_logmask; | ||||
extern int net_max_downloaders; | extern int net_max_downloaders; | ||||
@@ -19,14 +19,28 @@ | |||||
#include "tracker_req.h" | #include "tracker_req.h" | ||||
#include "stream.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 | 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 | void | ||||
torrent_deactivate(struct torrent *tp) | |||||
torrent_stop(struct torrent *tp) | |||||
{ | { | ||||
switch (tp->state) { | switch (tp->state) { | ||||
case T_INACTIVE: | |||||
break; | |||||
case T_STARTING: | case T_STARTING: | ||||
case T_ACTIVE: | case T_ACTIVE: | ||||
tp->state = T_STOPPING; | tp->state = T_STOPPING; | ||||
@@ -88,69 +151,43 @@ torrent_deactivate(struct torrent *tp) | |||||
if (tp->tr != NULL) | if (tp->tr != NULL) | ||||
tr_destroy(tp); | tr_destroy(tp); | ||||
break; | 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 | void | ||||
torrent_on_cm_started(struct torrent *tp) | torrent_on_cm_started(struct torrent *tp) | ||||
{ | { | ||||
net_add_torrent(tp); | |||||
tr_start(tp); | |||||
tp->state = T_ACTIVE; | tp->state = T_ACTIVE; | ||||
net_add_torrent(tp); | |||||
if (tr_start(tp) != 0) | |||||
torrent_stop(tp); | |||||
} | } | ||||
void | void | ||||
torrent_on_cm_stopped(struct torrent *tp) | torrent_on_cm_stopped(struct torrent *tp) | ||||
{ | { | ||||
assert(tp->state == T_STOPPING); | 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 | void | ||||
torrent_on_tr_stopped(struct torrent *tp) | torrent_on_tr_stopped(struct torrent *tp) | ||||
{ | { | ||||
assert(tp->state == T_STOPPING); | assert(tp->state == T_STOPPING); | ||||
if (tp->cm == NULL) { | |||||
tp->state = T_INACTIVE; | |||||
btpd_tp_deactivated(tp); | |||||
} | |||||
if (tp->cm == NULL) | |||||
torrent_kill(tp); | |||||
} | } |
@@ -2,17 +2,16 @@ | |||||
#define BTPD_TORRENT_H | #define BTPD_TORRENT_H | ||||
#define PIECE_BLOCKLEN (1 << 14) | #define PIECE_BLOCKLEN (1 << 14) | ||||
#define RELPATH_SIZE 41 | |||||
enum torrent_state { | enum torrent_state { | ||||
T_INACTIVE, | |||||
T_STARTING, | T_STARTING, | ||||
T_ACTIVE, | T_ACTIVE, | ||||
T_STOPPING | T_STOPPING | ||||
}; | }; | ||||
struct torrent { | struct torrent { | ||||
unsigned num; | |||||
const char *relpath; | |||||
char relpath[RELPATH_SIZE]; | |||||
struct metainfo meta; | struct metainfo meta; | ||||
enum torrent_state state; | enum torrent_state state; | ||||
@@ -26,9 +25,14 @@ struct torrent { | |||||
BTPDQ_HEAD(torrent_tq, 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); | off_t torrent_piece_size(struct torrent *tp, uint32_t piece); | ||||
uint32_t torrent_piece_blocks(struct torrent *tp, uint32_t piece); | uint32_t torrent_piece_blocks(struct torrent *tp, uint32_t piece); | ||||
@@ -1,66 +1,179 @@ | |||||
#include <sys/types.h> | |||||
#include <sys/stat.h> | |||||
#include <err.h> | #include <err.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
#include <fcntl.h> | |||||
#include <getopt.h> | #include <getopt.h> | ||||
#include <limits.h> | |||||
#include <stdio.h> | #include <stdio.h> | ||||
#include <stdlib.h> | #include <stdlib.h> | ||||
#include <string.h> | #include <string.h> | ||||
#include <unistd.h> | |||||
#include "btpd_if.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) { | switch (code) { | ||||
case IPC_OK: | case IPC_OK: | ||||
return; | |||||
break; | |||||
case IPC_FAIL: | 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; | 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 | void | ||||
usage_add(void) | usage_add(void) | ||||
{ | { | ||||
printf( | printf( | ||||
"Add a torrent to btpd.\n" | |||||
"Add torrents to btpd.\n" | |||||
"\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" | "\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" | "\n" | ||||
"-f file\n" | |||||
"\tThe torrent to add.\n" | |||||
"Options:\n" | |||||
"-d dir\n" | |||||
"\tUse the dir for content.\n" | |||||
"\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" | "\n" | ||||
); | ); | ||||
exit(1); | 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 | void | ||||
cmd_add(int argc, char **argv) | 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 | void | ||||
@@ -69,11 +182,11 @@ usage_del(void) | |||||
printf( | printf( | ||||
"Remove torrents from btpd.\n" | "Remove torrents from btpd.\n" | ||||
"\n" | "\n" | ||||
"Usage: del num ...\n" | |||||
"Usage: del file ...\n" | |||||
"\n" | "\n" | ||||
"Arguments:\n" | "Arguments:\n" | ||||
"num\n" | |||||
"\tThe number of the torrent to remove.\n" | |||||
"file ...\n" | |||||
"\tThe torrents to remove.\n" | |||||
"\n"); | "\n"); | ||||
exit(1); | exit(1); | ||||
} | } | ||||
@@ -84,16 +197,17 @@ cmd_del(int argc, char **argv) | |||||
if (argc < 2) | if (argc < 2) | ||||
usage_del(); | 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(); | 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 | void | ||||
@@ -118,24 +232,23 @@ cmd_kill(int argc, char **argv) | |||||
{ | { | ||||
int seconds = -1; | int seconds = -1; | ||||
char *endptr; | char *endptr; | ||||
if (argc == 1) | |||||
; | |||||
else if (argc == 2) { | |||||
if (argc == 2) { | |||||
seconds = strtol(argv[1], &endptr, 10); | seconds = strtol(argv[1], &endptr, 10); | ||||
if (strlen(argv[1]) > endptr - argv[1] || seconds < 0) | if (strlen(argv[1]) > endptr - argv[1] || seconds < 0) | ||||
usage_kill(); | usage_kill(); | ||||
} else | |||||
} else if (argc > 2) | |||||
usage_kill(); | usage_kill(); | ||||
btpd_connect(); | btpd_connect(); | ||||
btpd_die(ipc, seconds); | |||||
handle_ipc_res(btpd_die(ipc, seconds), "kill"); | |||||
} | } | ||||
void | void | ||||
usage_list(void) | usage_list(void) | ||||
{ | { | ||||
printf( | printf( | ||||
"List btpd's torrents.\n" | |||||
"List active torrents.\n" | |||||
"\n" | "\n" | ||||
"Usage: list\n" | "Usage: list\n" | ||||
"\n" | "\n" | ||||
@@ -152,12 +265,13 @@ cmd_list(int argc, char **argv) | |||||
usage_list(); | usage_list(); | ||||
btpd_connect(); | 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"); | st->ntorrents == 1 ? "" : "s"); | ||||
} | } | ||||
@@ -166,9 +280,9 @@ usage_stat(void) | |||||
{ | { | ||||
printf( | printf( | ||||
"Display stats for active torrents.\n" | "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" | "%% 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" | "\n" | ||||
"Usage: stat [-i] [-w seconds]\n" | "Usage: stat [-i] [-w seconds]\n" | ||||
"\n" | "\n" | ||||
@@ -182,23 +296,6 @@ usage_stat(void) | |||||
exit(1); | 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 | void | ||||
do_stat(int individual, int seconds) | do_stat(int individual, int seconds) | ||||
{ | { | ||||
@@ -206,13 +303,11 @@ do_stat(int individual, int seconds) | |||||
struct tpstat tot; | struct tpstat tot; | ||||
again: | again: | ||||
bzero(&tot, sizeof(tot)); | 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++) { | for (int i = 0; i < st->ntorrents; i++) { | ||||
struct tpstat *cur = &st->torrents[i]; | struct tpstat *cur = &st->torrents[i]; | ||||
if (cur->state != 'A') | |||||
continue; | |||||
tot.uploaded += cur->uploaded; | tot.uploaded += cur->uploaded; | ||||
tot.downloaded += cur->downloaded; | tot.downloaded += cur->downloaded; | ||||
tot.rate_up += cur->rate_up; | tot.rate_up += cur->rate_up; | ||||
@@ -223,7 +318,8 @@ again: | |||||
tot.have += cur->have; | tot.have += cur->have; | ||||
tot.total += cur->total; | tot.total += cur->total; | ||||
if (individual) { | if (individual) { | ||||
printf("%u. %s:\n", cur->num, cur->name); | |||||
print_state_name(cur); | |||||
printf(":\n"); | |||||
print_stat(cur); | 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} | {NULL, 0, NULL, 0} | ||||
}; | }; | ||||
@@ -272,73 +368,7 @@ cmd_stat(int argc, char **argv) | |||||
do_stat(iflag, seconds); | 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; | const char *name; | ||||
void (*fun)(int, char **); | void (*fun)(int, char **); | ||||
void (*help)(void); | void (*help)(void); | ||||
@@ -347,19 +377,16 @@ static struct { | |||||
{ "del", cmd_del, usage_del }, | { "del", cmd_del, usage_del }, | ||||
{ "kill", cmd_kill, usage_kill }, | { "kill", cmd_kill, usage_kill }, | ||||
{ "list", cmd_list, usage_list }, | { "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 | void | ||||
usage(void) | usage(void) | ||||
{ | { | ||||
printf( | 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" | "\n" | ||||
"Usage: btcli [main options] command [command options]\n" | "Usage: btcli [main options] command [command options]\n" | ||||
"\n" | "\n" | ||||
@@ -375,15 +402,13 @@ usage(void) | |||||
"del\n" | "del\n" | ||||
"kill\n" | "kill\n" | ||||
"list\n" | "list\n" | ||||
"start\n" | |||||
"stat\n" | "stat\n" | ||||
"stop\n" | |||||
"\n"); | "\n"); | ||||
exit(1); | 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} | {NULL, 0, NULL, 0} | ||||
}; | }; | ||||
@@ -400,7 +425,7 @@ main(int argc, char **argv) | |||||
case 'd': | case 'd': | ||||
btpd_dir = optarg; | btpd_dir = optarg; | ||||
break; | break; | ||||
case 1: | |||||
case 'H': | |||||
help = 1; | help = 1; | ||||
break; | break; | ||||
default: | default: | ||||
@@ -413,6 +438,10 @@ main(int argc, char **argv) | |||||
if (argc == 0) | if (argc == 0) | ||||
usage(); | usage(); | ||||
if (btpd_dir == NULL) | |||||
if ((btpd_dir = find_btpd_dir()) == NULL) | |||||
errx(1, "cannot find the btpd directory"); | |||||
optind = 0; | optind = 0; | ||||
int found = 0; | int found = 0; | ||||
for (int i = 0; !found && i < ncmds; i++) { | for (int i = 0; !found && i < ncmds; i++) { | ||||
@@ -424,7 +453,7 @@ main(int argc, char **argv) | |||||
cmd_table[i].fun(argc, argv); | cmd_table[i].fun(argc, argv); | ||||
} | } | ||||
} | } | ||||
if (!found) | if (!found) | ||||
usage(); | usage(); | ||||
@@ -1,3 +1,7 @@ | |||||
#include <sys/types.h> | |||||
#include <sys/socket.h> | |||||
#include <sys/un.h> | |||||
#include <ctype.h> | #include <ctype.h> | ||||
#include <err.h> | #include <err.h> | ||||
#include <errno.h> | #include <errno.h> | ||||
@@ -81,7 +85,7 @@ ipc_response(struct ipc *ipc, char **out, uint32_t *len) | |||||
*len = size; | *len = size; | ||||
return 0; | return 0; | ||||
} | } | ||||
static int | static int | ||||
ipc_req_res(struct ipc *ipc, const char *req, uint32_t qlen, char **res, | ipc_req_res(struct ipc *ipc, const char *req, uint32_t qlen, char **res, | ||||
uint32_t *rlen) | uint32_t *rlen) | ||||
@@ -125,7 +129,7 @@ btpd_die(struct ipc *ipc, int seconds) | |||||
if (seconds >= 0) | if (seconds >= 0) | ||||
buf_print(&iob, "l3:diei%dee", seconds); | buf_print(&iob, "l3:diei%dee", seconds); | ||||
else | else | ||||
buf_print(&iob, "l3:diee"); | |||||
buf_swrite(&iob, "l3:diee"); | |||||
return ipc_buf_req(ipc, &iob); | return ipc_buf_req(ipc, &iob); | ||||
} | } | ||||
@@ -150,21 +154,18 @@ parse_btstat(const uint8_t *res, struct btstat **out) | |||||
int i = 0; | int i = 0; | ||||
for (const char *tp = benc_first(tlst); tp != NULL; tp = benc_next(tp)) { | for (const char *tp = benc_first(tlst); tp != NULL; tp = benc_next(tp)) { | ||||
struct tpstat *ts = &st->torrents[i]; | struct tpstat *ts = &st->torrents[i]; | ||||
ts->num = benc_dget_int(tp, "num"); | |||||
ts->name = benc_dget_str(tp, "path", NULL); | 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++; | i++; | ||||
} | } | ||||
*out = st; | *out = st; | ||||
@@ -197,29 +198,26 @@ btpd_stat(struct ipc *ipc, struct btstat **out) | |||||
return err; | 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 | 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 | 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); | |||||
} | } |
@@ -1,15 +1,18 @@ | |||||
#ifndef BTPD_IF_H | #ifndef BTPD_IF_H | ||||
#define BTPD_IF_H | #define BTPD_IF_H | ||||
#include <sys/types.h> | |||||
#include <sys/socket.h> | |||||
#include <sys/un.h> | |||||
struct ipc; | struct ipc; | ||||
enum torrent_state { //XXX: Same as in btpd/torrent.h | |||||
T_STARTING, | |||||
T_ACTIVE, | |||||
T_STOPPING | |||||
}; | |||||
enum ipc_code { | enum ipc_code { | ||||
IPC_OK, | IPC_OK, | ||||
IPC_FAIL, | IPC_FAIL, | ||||
IPC_ERROR, | |||||
IPC_COMMERR | IPC_COMMERR | ||||
}; | }; | ||||
@@ -17,8 +20,7 @@ struct btstat { | |||||
unsigned ntorrents; | unsigned ntorrents; | ||||
struct tpstat { | struct tpstat { | ||||
char *name; | char *name; | ||||
unsigned num; | |||||
char state; | |||||
enum torrent_state state; | |||||
unsigned errors; | unsigned errors; | ||||
unsigned npeers; | unsigned npeers; | ||||
@@ -32,13 +34,11 @@ struct btstat { | |||||
int ipc_open(const char *dir, struct ipc **out); | int ipc_open(const char *dir, struct ipc **out); | ||||
int ipc_close(struct ipc *ipc); | 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_die(struct ipc *ipc, int seconds); | ||||
enum ipc_code btpd_stat(struct ipc *ipc, struct btstat **out); | 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); | void free_btstat(struct btstat *stat); | ||||
#endif | #endif |