Torrents are now considered stopped and may be restarted even if the stop event haven't been sent the trackers yet. The same holds for the del and add commands. A btpd process in shutdown mode that only have stopped torrents, but is still sending the stop event to trackers, will release resources that would block a new btpd to start. It will the silently exit when it's finished with the trackers. This also makes the timeout parameter for shutdown unnecessary.master
@@ -5,9 +5,9 @@ | |||||
static uint8_t m_peer_id[20]; | static uint8_t m_peer_id[20]; | ||||
static struct timeout m_heartbeat; | static struct timeout m_heartbeat; | ||||
static struct timeout m_grace_timer; | |||||
static int m_signal; | static int m_signal; | ||||
static int m_shutdown; | static int m_shutdown; | ||||
static int m_ghost; | |||||
long btpd_seconds; | long btpd_seconds; | ||||
@@ -18,29 +18,35 @@ btpd_exit(int code) | |||||
exit(code); | exit(code); | ||||
} | } | ||||
extern int pidfd; | |||||
void ipc_shutdown(void); | |||||
void net_shutdown(void); | |||||
static void | static void | ||||
grace_cb(int fd, short type, void *arg) | |||||
death_procedure(void) | |||||
{ | { | ||||
struct torrent *tp; | |||||
BTPDQ_FOREACH(tp, torrent_get_all(), entry) | |||||
torrent_stop(tp, 0); | |||||
assert(m_shutdown); | |||||
if (torrent_count() == 0) | |||||
btpd_exit(0); | |||||
if (!m_ghost && torrent_count() == torrent_ghosts()) { | |||||
btpd_log(BTPD_L_BTPD, "Entering pre exit mode. Bye!\n"); | |||||
fclose(stderr); | |||||
fclose(stdout); | |||||
net_shutdown(); | |||||
ipc_shutdown(); | |||||
close(pidfd); | |||||
m_ghost = 1; | |||||
} | |||||
} | } | ||||
void | void | ||||
btpd_shutdown(int grace_seconds) | |||||
btpd_shutdown(void) | |||||
{ | { | ||||
if (torrent_count() == 0) | |||||
btpd_exit(0); | |||||
else { | |||||
struct torrent *tp; | |||||
m_shutdown = 1; | |||||
BTPDQ_FOREACH(tp, torrent_get_all(), entry) | |||||
if (tp->state != T_STOPPING) | |||||
torrent_stop(tp, 0); | |||||
if (grace_seconds >= 0) | |||||
btpd_timer_add(&m_grace_timer, | |||||
(& (struct timespec){ grace_seconds, 0 })); | |||||
} | |||||
m_shutdown = 1; | |||||
struct torrent *tp, *next; | |||||
BTPDQ_FOREACH_MUTABLE(tp, torrent_get_all(), entry, next) | |||||
torrent_stop(tp, 0); | |||||
death_procedure(); | |||||
} | } | ||||
int btpd_is_stopping(void) | int btpd_is_stopping(void) | ||||
@@ -69,11 +75,12 @@ heartbeat_cb(int fd, short type, void *arg) | |||||
torrent_on_tick_all(); | torrent_on_tick_all(); | ||||
if (m_signal) { | if (m_signal) { | ||||
btpd_log(BTPD_L_BTPD, "Got signal %d.\n", m_signal); | btpd_log(BTPD_L_BTPD, "Got signal %d.\n", m_signal); | ||||
btpd_shutdown(30); | |||||
m_signal = 0; | m_signal = 0; | ||||
if (!m_shutdown) | |||||
btpd_shutdown(); | |||||
} | } | ||||
if (m_shutdown && torrent_count() == 0) | |||||
btpd_exit(0); | |||||
if (m_shutdown) | |||||
death_procedure(); | |||||
} | } | ||||
void tr_init(void); | void tr_init(void); | ||||
@@ -123,7 +130,6 @@ btpd_init(void) | |||||
tr_init(); | tr_init(); | ||||
tlib_init(); | tlib_init(); | ||||
timer_init(&m_grace_timer, grace_cb, NULL); | |||||
timer_init(&m_heartbeat, heartbeat_cb, NULL); | timer_init(&m_heartbeat, heartbeat_cb, NULL); | ||||
btpd_timer_add(&m_heartbeat, (& (struct timespec) { 1, 0 })); | btpd_timer_add(&m_heartbeat, (& (struct timespec) { 1, 0 })); | ||||
} | } |
@@ -82,7 +82,7 @@ void btpd_ev_disable(struct fdev *ev, uint16_t flags); | |||||
void btpd_timer_add(struct timeout *to, struct timespec *ts); | void btpd_timer_add(struct timeout *to, struct timespec *ts); | ||||
void btpd_timer_del(struct timeout *to); | void btpd_timer_del(struct timeout *to); | ||||
void btpd_shutdown(int grace_seconds); | |||||
void btpd_shutdown(void); | |||||
int btpd_is_stopping(void); | int btpd_is_stopping(void); | ||||
const uint8_t *btpd_get_peer_id(void); | const uint8_t *btpd_get_peer_id(void); | ||||
@@ -8,6 +8,7 @@ struct cli { | |||||
struct fdev read; | struct fdev read; | ||||
}; | }; | ||||
static int m_listen_sd; | |||||
static struct fdev m_cli_incoming; | static struct fdev m_cli_incoming; | ||||
static int | static int | ||||
@@ -135,6 +136,8 @@ write_ans(struct iobuf *iob, struct tlib *tl, enum ipc_tval val) | |||||
case T_LEECH: | case T_LEECH: | ||||
ts = IPC_TSTATE_LEECH; | ts = IPC_TSTATE_LEECH; | ||||
break; | break; | ||||
case T_GHOST: | |||||
break; | |||||
} | } | ||||
} | } | ||||
iobuf_print(iob, "i%de", ts); | iobuf_print(iob, "i%de", ts); | ||||
@@ -185,9 +188,10 @@ cmd_tget(struct cli *cli, int argc, const char *args) | |||||
struct tlib *tlv[tlib_count()]; | struct tlib *tlv[tlib_count()]; | ||||
tlib_put_all(tlv); | tlib_put_all(tlv); | ||||
for (int i = 0; i < sizeof(tlv) / sizeof(tlv[0]); i++) { | for (int i = 0; i < sizeof(tlv) / sizeof(tlv[0]); i++) { | ||||
if ((from == IPC_TWC_ALL || | |||||
(tlv[i]->tp == NULL && from == IPC_TWC_INACTIVE) || | |||||
(tlv[i]->tp != NULL && from == IPC_TWC_ACTIVE))) { | |||||
if (!torrent_haunting(tlv[i]) && ( | |||||
from == IPC_TWC_ALL || | |||||
(!torrent_active(tlv[i]) && from == IPC_TWC_INACTIVE) || | |||||
(torrent_active(tlv[i]) && from == IPC_TWC_ACTIVE))) { | |||||
iobuf_swrite(&iob, "l"); | iobuf_swrite(&iob, "l"); | ||||
for (int k = 0; k < nkeys; k++) | for (int k = 0; k < nkeys; k++) | ||||
write_ans(&iob, tlv[i], opts[k]); | write_ans(&iob, tlv[i], opts[k]); | ||||
@@ -206,7 +210,7 @@ cmd_tget(struct cli *cli, int argc, const char *args) | |||||
free(opts); | free(opts); | ||||
return IPC_COMMERR; | return IPC_COMMERR; | ||||
} | } | ||||
if (tl != NULL) { | |||||
if (tl != NULL && !torrent_haunting(tl)) { | |||||
iobuf_swrite(&iob, "l"); | iobuf_swrite(&iob, "l"); | ||||
for (int i = 0; i < nkeys; i++) | for (int i = 0; i < nkeys; i++) | ||||
write_ans(&iob, tl, opts[i]); | write_ans(&iob, tl, opts[i]); | ||||
@@ -248,10 +252,15 @@ cmd_add(struct cli *cli, int argc, const char *args) | |||||
content[csize] = '\0'; | content[csize] = '\0'; | ||||
tl = tlib_by_hash(mi_info_hash(mi, hash)); | tl = tlib_by_hash(mi_info_hash(mi, hash)); | ||||
if (tl != NULL) | |||||
if (tl != NULL && !torrent_haunting(tl)) | |||||
return write_code_buffer(cli, IPC_ETENTEXIST); | return write_code_buffer(cli, IPC_ETENTEXIST); | ||||
tl = tlib_add(hash, mi, mi_size, content, | |||||
benc_dget_str(args, "name", NULL)); | |||||
if (tl != NULL) { | |||||
tl = tlib_readd(tl, hash, mi, mi_size, content, | |||||
benc_dget_str(args, "name", NULL)); | |||||
} else { | |||||
tl = tlib_add(hash, mi, mi_size, content, | |||||
benc_dget_str(args, "name", NULL)); | |||||
} | |||||
return write_add_buffer(cli, tl->num); | return write_add_buffer(cli, tl->num); | ||||
} | } | ||||
@@ -270,7 +279,7 @@ cmd_del(struct cli *cli, int argc, const char *args) | |||||
else | else | ||||
return IPC_COMMERR; | return IPC_COMMERR; | ||||
if (tl == NULL) | |||||
if (tl == NULL || torrent_haunting(tl)) | |||||
ret = write_code_buffer(cli, IPC_ENOTENT); | ret = write_code_buffer(cli, IPC_ENOTENT); | ||||
else { | else { | ||||
ret = write_code_buffer(cli, IPC_OK); | ret = write_code_buffer(cli, IPC_OK); | ||||
@@ -300,9 +309,9 @@ cmd_start(struct cli *cli, int argc, const char *args) | |||||
else | else | ||||
return IPC_COMMERR; | return IPC_COMMERR; | ||||
if (tl == NULL) | |||||
if (tl == NULL || torrent_haunting(tl)) | |||||
code = IPC_ENOTENT; | code = IPC_ENOTENT; | ||||
else if (tl->tp != NULL) | |||||
else if (!torrent_startable(tl)) | |||||
code = IPC_ETACTIVE; | code = IPC_ETACTIVE; | ||||
else | else | ||||
if ((code = torrent_start(tl)) == IPC_OK) | if ((code = torrent_start(tl)) == IPC_OK) | ||||
@@ -324,9 +333,9 @@ cmd_stop(struct cli *cli, int argc, const char *args) | |||||
else | else | ||||
return IPC_COMMERR; | return IPC_COMMERR; | ||||
if (tl == NULL) | |||||
if (tl == NULL || torrent_haunting(tl)) | |||||
return write_code_buffer(cli, IPC_ENOTENT); | return write_code_buffer(cli, IPC_ENOTENT); | ||||
else if (tl->tp == NULL) | |||||
else if (!torrent_active(tl)) | |||||
return write_code_buffer(cli, IPC_ETINACTIVE); | return write_code_buffer(cli, IPC_ETINACTIVE); | ||||
else { | else { | ||||
// Stopping a torrent may trigger exit so we need to reply before. | // Stopping a torrent may trigger exit so we need to reply before. | ||||
@@ -340,12 +349,11 @@ cmd_stop(struct cli *cli, int argc, const char *args) | |||||
static int | static int | ||||
cmd_stop_all(struct cli *cli, int argc, const char *args) | cmd_stop_all(struct cli *cli, int argc, const char *args) | ||||
{ | { | ||||
struct torrent *tp; | |||||
struct torrent *tp, *next; | |||||
int ret = write_code_buffer(cli, IPC_OK); | int ret = write_code_buffer(cli, IPC_OK); | ||||
active_clear(); | active_clear(); | ||||
BTPDQ_FOREACH(tp, torrent_get_all(), entry) | |||||
if (tp->state != T_STOPPING) | |||||
torrent_stop(tp, 0); | |||||
BTPDQ_FOREACH_MUTABLE(tp, torrent_get_all(), entry, next) | |||||
torrent_stop(tp, 0); | |||||
return ret; | return ret; | ||||
} | } | ||||
@@ -354,11 +362,8 @@ cmd_die(struct cli *cli, int argc, const char *args) | |||||
{ | { | ||||
int err = write_code_buffer(cli, IPC_OK); | int err = write_code_buffer(cli, IPC_OK); | ||||
if (!btpd_is_stopping()) { | 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_log(BTPD_L_BTPD, "Someone wants me dead.\n"); | ||||
btpd_shutdown(grace_seconds); | |||||
btpd_shutdown(); | |||||
} | } | ||||
return err; | return err; | ||||
} | } | ||||
@@ -449,6 +454,13 @@ client_connection_cb(int sd, short type, void *arg) | |||||
btpd_ev_new(&cli->read, cli->sd, EV_READ, cli_read_cb, cli); | btpd_ev_new(&cli->read, cli->sd, EV_READ, cli_read_cb, cli); | ||||
} | } | ||||
void | |||||
ipc_shutdown(void) | |||||
{ | |||||
btpd_ev_del(&m_cli_incoming); | |||||
close(m_listen_sd); | |||||
} | |||||
void | void | ||||
ipc_init(void) | ipc_init(void) | ||||
{ | { | ||||
@@ -477,4 +489,5 @@ ipc_init(void) | |||||
set_nonblocking(sd); | set_nonblocking(sd); | ||||
btpd_ev_new(&m_cli_incoming, sd, EV_READ, client_connection_cb, NULL); | btpd_ev_new(&m_cli_incoming, sd, EV_READ, client_connection_cb, NULL); | ||||
m_listen_sd = sd; | |||||
} | } |
@@ -5,6 +5,7 @@ | |||||
int btpd_daemon_phase = 2; | int btpd_daemon_phase = 2; | ||||
int first_btpd_comm[2]; | int first_btpd_comm[2]; | ||||
int pidfd; | |||||
void | void | ||||
first_btpd_exit(char code) | first_btpd_exit(char code) | ||||
@@ -15,7 +16,7 @@ first_btpd_exit(char code) | |||||
} | } | ||||
static void | static void | ||||
writepid(int pidfd) | |||||
writepid(void) | |||||
{ | { | ||||
int nw; | int nw; | ||||
char pidtxt[100]; | char pidtxt[100]; | ||||
@@ -28,7 +29,6 @@ static void | |||||
setup_daemon(int daemonize, const char *dir) | setup_daemon(int daemonize, const char *dir) | ||||
{ | { | ||||
char c; | char c; | ||||
int pidfd; | |||||
pid_t pid; | pid_t pid; | ||||
struct timespec ts; | struct timespec ts; | ||||
@@ -91,7 +91,7 @@ setup_daemon(int daemonize, const char *dir) | |||||
if (lockf(pidfd, F_TLOCK, 0) == -1) | if (lockf(pidfd, F_TLOCK, 0) == -1) | ||||
btpd_err("Another instance of btpd is probably running in %s.\n", dir); | btpd_err("Another instance of btpd is probably running in %s.\n", dir); | ||||
writepid(pidfd); | |||||
writepid(); | |||||
} | } | ||||
static void | static void | ||||
@@ -14,6 +14,7 @@ struct net_listener { | |||||
struct fdev ev; | struct fdev ev; | ||||
}; | }; | ||||
static int m_nlisteners; | |||||
static struct net_listener *m_net_listeners; | static struct net_listener *m_net_listeners; | ||||
unsigned net_npeers; | unsigned net_npeers; | ||||
@@ -668,6 +669,15 @@ net_af_spec(void) | |||||
return AF_INET6; | return AF_INET6; | ||||
} | } | ||||
void | |||||
net_shutdown(void) | |||||
{ | |||||
for (int i = 0; i < m_nlisteners; i++) { | |||||
btpd_ev_del(&m_net_listeners[i].ev); | |||||
close(m_net_listeners[i].sd); | |||||
} | |||||
} | |||||
void | void | ||||
net_init(void) | net_init(void) | ||||
{ | { | ||||
@@ -699,6 +709,7 @@ net_init(void) | |||||
net_ipv6 = found_ipv6; | net_ipv6 = found_ipv6; | ||||
if (!net_ipv4 && !net_ipv6) | if (!net_ipv4 && !net_ipv6) | ||||
btpd_err("no usable address found. wrong use of -4/-6 perhaps.\n"); | btpd_err("no usable address found. wrong use of -4/-6 perhaps.\n"); | ||||
m_nlisteners = count; | |||||
m_net_listeners = btpd_calloc(count, sizeof(*m_net_listeners)); | m_net_listeners = btpd_calloc(count, sizeof(*m_net_listeners)); | ||||
for (ai = res; ai != NULL; ai = ai->ai_next) { | for (ai = res; ai != NULL; ai = ai->ai_next) { | ||||
count--; | count--; | ||||
@@ -65,7 +65,7 @@ tlib_del(struct tlib *tl) | |||||
char path[PATH_MAX]; | char path[PATH_MAX]; | ||||
DIR *dir; | DIR *dir; | ||||
struct dirent *de; | struct dirent *de; | ||||
assert(tl->tp == NULL); | |||||
assert(tl->tp == NULL || tl->tp->state == T_GHOST); | |||||
snprintf(path, PATH_MAX, "torrents/%s", bin2hex(tl->hash, relpath, 20)); | snprintf(path, PATH_MAX, "torrents/%s", bin2hex(tl->hash, relpath, 20)); | ||||
if ((dir = opendir(path)) != NULL) { | if ((dir = opendir(path)) != NULL) { | ||||
while ((de = readdir(dir)) != NULL) { | while ((de = readdir(dir)) != NULL) { | ||||
@@ -78,7 +78,8 @@ tlib_del(struct tlib *tl) | |||||
} | } | ||||
snprintf(path, PATH_MAX, "torrents/%s", relpath); | snprintf(path, PATH_MAX, "torrents/%s", relpath); | ||||
remove(path); | remove(path); | ||||
tlib_kill(tl); | |||||
if (tl->tp == NULL) | |||||
tlib_kill(tl); | |||||
return 0; | return 0; | ||||
} | } | ||||
@@ -257,6 +258,19 @@ tlib_add(const uint8_t *hash, const char *mi, size_t mi_size, | |||||
return tl; | return tl; | ||||
} | } | ||||
struct tlib * | |||||
tlib_readd(struct tlib *tl, const uint8_t *hash, const char *mi, | |||||
size_t mi_size, const char *content, char *name) | |||||
{ | |||||
struct tlib *tln; | |||||
struct torrent *tp = tl->tp; | |||||
tp->delete = 0; | |||||
tlib_kill(tl); | |||||
tln = tlib_add(hash, mi, mi_size, content, name); | |||||
tln->tp = tp; | |||||
return tln; | |||||
} | |||||
static int | static int | ||||
num_test(const void *k1, const void *k2) | num_test(const void *k1, const void *k2) | ||||
{ | { | ||||
@@ -26,7 +26,10 @@ void tlib_put_all(struct tlib **v); | |||||
struct tlib *tlib_add(const uint8_t *hash, const char *mi, size_t mi_size, | struct tlib *tlib_add(const uint8_t *hash, const char *mi, size_t mi_size, | ||||
const char *content, char *name); | const char *content, char *name); | ||||
struct tlib *tlib_readd(struct tlib *tl, const uint8_t *hash, const char *mi, | |||||
size_t mi_size, const char *content, char *name); | |||||
int tlib_del(struct tlib *tl); | int tlib_del(struct tlib *tl); | ||||
void tlib_kill(struct tlib *tl); | |||||
void tlib_update_info(struct tlib *tl, int only_file); | void tlib_update_info(struct tlib *tl, int only_file); | ||||
@@ -4,6 +4,7 @@ | |||||
#define SAVE_INTERVAL 300 | #define SAVE_INTERVAL 300 | ||||
static unsigned m_nghosts; | |||||
static unsigned m_ntorrents; | static unsigned m_ntorrents; | ||||
static struct torrent_tq m_torrents = BTPDQ_HEAD_INITIALIZER(m_torrents); | static struct torrent_tq m_torrents = BTPDQ_HEAD_INITIALIZER(m_torrents); | ||||
@@ -22,6 +23,12 @@ torrent_count(void) | |||||
return m_ntorrents; | return m_ntorrents; | ||||
} | } | ||||
unsigned | |||||
torrent_ghosts(void) | |||||
{ | |||||
return m_nghosts; | |||||
} | |||||
struct torrent * | struct torrent * | ||||
torrent_by_num(unsigned num) | torrent_by_num(unsigned num) | ||||
{ | { | ||||
@@ -71,12 +78,41 @@ torrent_block_size(struct torrent *tp, uint32_t piece, uint32_t nblocks, | |||||
} | } | ||||
} | } | ||||
static void | |||||
torrent_kill(struct torrent *tp) | |||||
{ | |||||
assert(m_ntorrents > 0); | |||||
assert(!(net_active(tp) || cm_active(tp))); | |||||
if (tp->state == T_GHOST) | |||||
m_nghosts--; | |||||
m_ntorrents--; | |||||
BTPDQ_REMOVE(&m_torrents, tp, entry); | |||||
if (tp->delete) | |||||
tlib_kill(tp->tl); | |||||
else | |||||
tp->tl->tp = NULL; | |||||
tr_kill(tp); | |||||
net_kill(tp); | |||||
cm_kill(tp); | |||||
mi_free_files(tp->nfiles, tp->files); | |||||
if (m_savetp == tp) | |||||
if ((m_savetp = BTPDQ_NEXT(tp, entry)) == NULL) | |||||
m_savetp = BTPDQ_FIRST(&m_torrents); | |||||
free(tp); | |||||
} | |||||
enum ipc_err | enum ipc_err | ||||
torrent_start(struct tlib *tl) | torrent_start(struct tlib *tl) | ||||
{ | { | ||||
struct torrent *tp; | struct torrent *tp; | ||||
char *mi; | char *mi; | ||||
if (tl->tp != NULL) { | |||||
assert(torrent_startable(tl)); | |||||
torrent_kill(tl->tp); | |||||
tl->tp = NULL; | |||||
} | |||||
if (tl->dir == NULL) | if (tl->dir == NULL) | ||||
return IPC_EBADTENT; | return IPC_EBADTENT; | ||||
@@ -111,25 +147,16 @@ torrent_start(struct tlib *tl) | |||||
return IPC_OK; | return IPC_OK; | ||||
} | } | ||||
static void | |||||
torrent_kill(struct torrent *tp) | |||||
static | |||||
void become_ghost(struct torrent *tp) | |||||
{ | { | ||||
btpd_log(BTPD_L_BTPD, "Stopped torrent '%s'.\n", torrent_name(tp)); | btpd_log(BTPD_L_BTPD, "Stopped torrent '%s'.\n", torrent_name(tp)); | ||||
assert(m_ntorrents > 0); | |||||
assert(!(tr_active(tp) || net_active(tp) || cm_active(tp))); | |||||
m_ntorrents--; | |||||
BTPDQ_REMOVE(&m_torrents, tp, entry); | |||||
tp->tl->tp = NULL; | |||||
tp->state = T_GHOST; | |||||
if (tp->delete) | if (tp->delete) | ||||
tlib_del(tp->tl); | tlib_del(tp->tl); | ||||
tr_kill(tp); | |||||
net_kill(tp); | |||||
cm_kill(tp); | |||||
mi_free_files(tp->nfiles, tp->files); | |||||
if (m_savetp == tp) | |||||
if ((m_savetp = BTPDQ_NEXT(tp, entry)) == NULL) | |||||
m_savetp = BTPDQ_FIRST(&m_torrents); | |||||
free(tp); | |||||
else | |||||
tlib_update_info(tp->tl, 0); | |||||
m_nghosts++; | |||||
} | } | ||||
void | void | ||||
@@ -148,10 +175,13 @@ torrent_stop(struct torrent *tp, int delete) | |||||
tr_stop(tp); | tr_stop(tp); | ||||
if (cm_active(tp)) | if (cm_active(tp)) | ||||
cm_stop(tp); | cm_stop(tp); | ||||
if (!delete) | |||||
tlib_update_info(tp->tl, 0); | |||||
if (!cm_active(tp)) { | |||||
become_ghost(tp); | |||||
if (!tr_active(tp)) | |||||
torrent_kill(tp); | |||||
} | |||||
break; | break; | ||||
case T_STOPPING: | |||||
default: | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
@@ -187,7 +217,11 @@ torrent_on_tick(struct torrent *tp) | |||||
} | } | ||||
break; | break; | ||||
case T_STOPPING: | case T_STOPPING: | ||||
if (!(cm_active(tp) || tr_active(tp))) | |||||
if (cm_active(tp)) | |||||
break; | |||||
become_ghost(tp); | |||||
case T_GHOST: | |||||
if (!tr_active(tp)) | |||||
torrent_kill(tp); | torrent_kill(tp); | ||||
break; | break; | ||||
default: | default: | ||||
@@ -213,3 +247,21 @@ torrent_on_tick_all(void) | |||||
} | } | ||||
} | } | ||||
} | } | ||||
int | |||||
torrent_active(struct tlib *tl) | |||||
{ | |||||
return tl->tp != NULL && tl->tp->state != T_GHOST; | |||||
} | |||||
int | |||||
torrent_startable(struct tlib *tl) | |||||
{ | |||||
return tl->tp == NULL || (tl->tp->state == T_GHOST && !tl->tp->delete); | |||||
} | |||||
int | |||||
torrent_haunting(struct tlib *tl) | |||||
{ | |||||
return tl->tp != NULL && tl->tp->delete && tl->tp->state == T_GHOST; | |||||
} |
@@ -8,7 +8,8 @@ enum torrent_state { | |||||
T_STARTING, | T_STARTING, | ||||
T_LEECH, | T_LEECH, | ||||
T_SEED, | T_SEED, | ||||
T_STOPPING | |||||
T_STOPPING, | |||||
T_GHOST | |||||
}; | }; | ||||
struct torrent { | struct torrent { | ||||
@@ -34,6 +35,7 @@ struct torrent { | |||||
BTPDQ_HEAD(torrent_tq, torrent); | BTPDQ_HEAD(torrent_tq, torrent); | ||||
unsigned torrent_count(void); | unsigned torrent_count(void); | ||||
unsigned torrent_ghosts(void); | |||||
const struct torrent_tq *torrent_get_all(void); | const struct torrent_tq *torrent_get_all(void); | ||||
struct torrent *torrent_by_num(unsigned num); | struct torrent *torrent_by_num(unsigned num); | ||||
struct torrent *torrent_by_hash(const uint8_t *hash); | struct torrent *torrent_by_hash(const uint8_t *hash); | ||||
@@ -41,6 +43,10 @@ struct torrent *torrent_by_hash(const uint8_t *hash); | |||||
enum ipc_err torrent_start(struct tlib *tl); | enum ipc_err torrent_start(struct tlib *tl); | ||||
void torrent_stop(struct torrent *tp, int delete); | void torrent_stop(struct torrent *tp, int delete); | ||||
int torrent_active(struct tlib *tl); | |||||
int torrent_haunting(struct tlib *tl); | |||||
int torrent_startable(struct tlib *tl); | |||||
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); | ||||
uint32_t torrent_block_size(struct torrent *tp, uint32_t piece, | uint32_t torrent_block_size(struct torrent *tp, uint32_t piece, | ||||
@@ -6,12 +6,7 @@ usage_kill(void) | |||||
printf( | printf( | ||||
"Shutdown btpd.\n" | "Shutdown btpd.\n" | ||||
"\n" | "\n" | ||||
"Usage: kill [seconds]\n" | |||||
"\n" | |||||
"Arguments:\n" | |||||
"seconds\n" | |||||
"\tThe number of seconds btpd waits before giving up on unresponsive\n" | |||||
"\ttrackers.\n" | |||||
"Usage: kill\n" | |||||
"\n" | "\n" | ||||
); | ); | ||||
exit(1); | exit(1); | ||||
@@ -20,18 +15,12 @@ usage_kill(void) | |||||
void | void | ||||
cmd_kill(int argc, char **argv) | cmd_kill(int argc, char **argv) | ||||
{ | { | ||||
int seconds = -1; | |||||
enum ipc_err code; | enum ipc_err code; | ||||
char *endptr; | |||||
if (argc == 2) { | |||||
seconds = strtol(argv[1], &endptr, 10); | |||||
if (strlen(argv[1]) > endptr - argv[1] || seconds < 0) | |||||
usage_kill(); | |||||
} else if (argc > 2) | |||||
if (argc > 1) | |||||
usage_kill(); | usage_kill(); | ||||
btpd_connect(); | btpd_connect(); | ||||
if ((code = btpd_die(ipc, seconds)) != 0) | |||||
if ((code = btpd_die(ipc)) != 0) | |||||
diemsg("command failed (%s).\n", ipc_strerror(code)); | diemsg("command failed (%s).\n", ipc_strerror(code)); | ||||
} | } |
@@ -155,13 +155,10 @@ ipc_buf_req_code(struct ipc *ipc, struct iobuf *iob) | |||||
} | } | ||||
enum ipc_err | enum ipc_err | ||||
btpd_die(struct ipc *ipc, int seconds) | |||||
btpd_die(struct ipc *ipc) | |||||
{ | { | ||||
struct iobuf iob = iobuf_init(16); | struct iobuf iob = iobuf_init(16); | ||||
if (seconds >= 0) | |||||
iobuf_print(&iob, "l3:diei%dee", seconds); | |||||
else | |||||
iobuf_swrite(&iob, "l3:diee"); | |||||
iobuf_swrite(&iob, "l3:diee"); | |||||
return ipc_buf_req_code(ipc, &iob); | return ipc_buf_req_code(ipc, &iob); | ||||
} | } | ||||
@@ -80,7 +80,7 @@ enum ipc_err btpd_del(struct ipc *ipc, struct ipc_torrent *tp); | |||||
enum ipc_err btpd_start(struct ipc *ipc, struct ipc_torrent *tp); | enum ipc_err btpd_start(struct ipc *ipc, struct ipc_torrent *tp); | ||||
enum ipc_err btpd_stop(struct ipc *ipc, struct ipc_torrent *tp); | enum ipc_err btpd_stop(struct ipc *ipc, struct ipc_torrent *tp); | ||||
enum ipc_err btpd_stop_all(struct ipc *ipc); | enum ipc_err btpd_stop_all(struct ipc *ipc); | ||||
enum ipc_err btpd_die(struct ipc *ipc, int seconds); | |||||
enum ipc_err btpd_die(struct ipc *ipc); | |||||
enum ipc_err btpd_get(struct ipc *ipc, enum ipc_dval *keys, size_t nkeys, | enum ipc_err btpd_get(struct ipc *ipc, enum ipc_dval *keys, size_t nkeys, | ||||
tget_cb_t cb, void *arg); | tget_cb_t cb, void *arg); | ||||
enum ipc_err btpd_tget(struct ipc *ipc, struct ipc_torrent *tps, size_t ntps, | enum ipc_err btpd_tget(struct ipc *ipc, struct ipc_torrent *tps, size_t ntps, | ||||