ratox

FIFO based tox client
git clone git://git.2f30.org/ratox
Log | Files | Refs | README | LICENSE

commit a0737ec5a4e6bddef86a7317e06740ed445a00c0
parent 4f4f6e1320ff996e09a5b78d6431b5ccf80a94bc
Author: FRIGN <dev@frign.de>
Date:   Fri, 17 Oct 2014 14:31:08 +0200

Refactor call-state management

Checking call_out only and stopping the call as soon as call_out
is gone is not desirable behaviour.
It now keeps up the running call until both call_out and call_in
have been "closed", no matter how or by whom it was started.

This is done by introducing av.state, which is a bit field.
While at it, I also incorporated the transmission-state into it.

toxav_hangup() will now only be called from the one centralized
flag-checking-loop before select, not in the callbacks themselves.
This will also get rid of some race-conditions (more or less).

Previously, call_out was constantly polled as long as the user
was online. Change this to only attempt to open call_out when the
call is initialized. This lowers CPU-usage dramatically.

Diffstat:
Mratox.c | 222++++++++++++++++++++++++++++++++++++++++++-------------------------------------
1 file changed, 118 insertions(+), 104 deletions(-)

diff --git a/ratox.c b/ratox.c @@ -161,13 +161,19 @@ struct transfer { int cooldown; }; +enum { + OUTGOING = 1 << 0, + INCOMING = 1 << 1, + TRANSMITTING = 1 << 2, + INCOMPLETE = 1 << 3, +}; + struct call { int num; - int transmission; + int state; uint8_t *frame; uint8_t payload[RTP_PAYLOAD_SIZE]; ssize_t n; - int incompleteframe; struct timespec lastsent; }; @@ -210,6 +216,7 @@ static int proxyflag; static struct timespec timediff(struct timespec, struct timespec); static void printrat(void); static void logmsg(const char *, ...); +static int fifoopen(int, struct file); static void fiforeset(int, int *, struct file); static ssize_t fiforead(int, int *, struct file, void *, size_t); static void cbcallinvite(void *, int32_t, void *); @@ -249,6 +256,9 @@ static void initshutdown(int); static void shutdown(void); static void usage(void); +#define FD_APPEND(fd) FD_SET((fd), &rfds); \ + if ((fd) > fdmax) fdmax = (fd); + static struct timespec timediff(struct timespec t1, struct timespec t2) { @@ -302,6 +312,17 @@ logmsg(const char *fmt, ...) va_end(ap); } +int +fifoopen(int dirfd, struct file f) +{ + int fd; + + fd = openat(dirfd, f.name, f.flags, 0666); + if (fd < 0 && errno != ENXIO) + eprintf("openat %s:", f.name); + return fd; +} + static void fiforeset(int dirfd, int *fd, struct file f) { @@ -315,10 +336,7 @@ fiforeset(int dirfd, int *fd, struct file f) r = mkfifoat(dirfd, f.name, 0666); if (r < 0 && errno != EEXIST) eprintf("mkfifoat %s:", f.name); - r = openat(dirfd, f.name, f.flags); - if (r < 0 && errno != ENXIO) - eprintf("openat %s:", f.name); - *fd = r; + *fd = fifoopen(dirfd, f); } static ssize_t @@ -396,8 +414,8 @@ cbcallstart(void *av, int32_t cnum, void *udata) f->av.frame = malloc(sizeof(int16_t) * framesize); if (!f->av.frame) eprintf("malloc:"); + f->av.n = 0; - f->av.incompleteframe = 0; f->av.lastsent.tv_sec = 0; f->av.lastsent.tv_nsec = 0; @@ -409,7 +427,7 @@ cbcallstart(void *av, int32_t cnum, void *udata) weprintf("Failed to hang up\n"); return; } - f->av.transmission = 1; + f->av.state |= TRANSMITTING; ftruncate(f->fd[FCALL_STATE], 0); lseek(f->fd[FCALL_STATE], 0, SEEK_SET); @@ -449,7 +467,7 @@ cbcalldata(ToxAv *av, int32_t cnum, int16_t *data, int len, void *udata) { struct friend *f; uint8_t *buf; - int wrote = 0; + int fd, wrote = 0; ssize_t n; TAILQ_FOREACH(f, &friendhead, entry) @@ -457,6 +475,18 @@ cbcalldata(ToxAv *av, int32_t cnum, int16_t *data, int len, void *udata) break; if (!f) return; + if (!(f->av.state & INCOMING)) { + /* try to open call_out for writing */ + fd = fifoopen(f->dirfd, ffiles[FCALL_OUT]); + if (fd < 0) { + close (fd); + return; + } + if (f->fd[FCALL_OUT] < 0) { + f->fd[FCALL_OUT] = fd; + f->av.state |= INCOMING; + } + } buf = (uint8_t *)data; len *= 2; @@ -464,7 +494,7 @@ cbcalldata(ToxAv *av, int32_t cnum, int16_t *data, int len, void *udata) n = write(f->fd[FCALL_OUT], &buf[wrote], len); if (n < 0) { if (errno == EPIPE) { - toxav_hangup(toxav, f->av.num); + f->av.state &= ~INCOMING; break; } else if (errno == EWOULDBLOCK) { continue; @@ -486,13 +516,13 @@ cancelcall(struct friend *f, char *action) logmsg(": %s : Audio > %s\n", f->name, action); if (f->av.num != -1) { - if (f->av.transmission) { + if (f->av.state & TRANSMITTING) { r = toxav_kill_transmission(toxav, f->av.num); if (r < 0) weprintf("Failed to kill transmission\n"); } } - f->av.transmission = 0; + f->av.state = 0; f->av.num = -1; /* Cancel Rx side of the call */ @@ -518,19 +548,18 @@ sendfriendcalldata(struct friend *f) int r; n = fiforead(f->dirfd, &f->fd[FCALL_IN], ffiles[FCALL_IN], - f->av.frame + f->av.incompleteframe * f->av.n, - framesize * sizeof(int16_t) - f->av.incompleteframe * f->av.n); + f->av.frame + ((f->av.state & INCOMPLETE) != 0) * f->av.n, + framesize * sizeof(int16_t) - ((f->av.state & INCOMPLETE) != 0) * f->av.n); if (n == 0) { - r = toxav_hangup(toxav, f->av.num); - if (r < 0) - weprintf("Failed to hang up\n"); + f->av.state &= ~OUTGOING; return; } else if (n < 0) { return; - } else if (n == (framesize * sizeof(int16_t) - f->av.incompleteframe * f->av.n)) { - f->av.incompleteframe = 0; + } else if (n == (framesize * sizeof(int16_t) - ((f->av.state & INCOMPLETE) != 0) * f->av.n)) { + f->av.state &= ~INCOMPLETE; f->av.n = 0; } else { + f->av.state |= INCOMPLETE; f->av.n += n; return; } @@ -1361,7 +1390,7 @@ friendcreate(int32_t frnum) ftruncate(f->fd[FCALL_STATE], 0); dprintf(f->fd[FCALL_STATE], "0\n"); - f->av.transmission = 0; + f->av.state = 0; f->av.num = -1; TAILQ_INSERT_TAIL(&friendhead, f, entry); @@ -1592,7 +1621,7 @@ loop(void) char tstamp[64]; struct friend *f, *ftmp; struct request *req, *rtmp; - time_t t0, t1, now; + time_t t0, t1; int connected = 0; int i, n, r; int fd, fdmax; @@ -1606,10 +1635,10 @@ loop(void) logmsg("DHT > Connecting\n"); toxconnect(); while (running) { - if (tox_isconnected(tox) == 1) { - if (connected == 0) { + /* Handle connection states */ + if (tox_isconnected(tox)) { + if (!connected) { logmsg("DHT > Connected\n"); - /* Cancel any pending transfers */ TAILQ_FOREACH(f, &friendhead, entry) { canceltxtransfer(f); cancelrxtransfer(f); @@ -1617,7 +1646,7 @@ loop(void) connected = 1; } } else { - if (connected == 1) { + if (connected) { logmsg("DHT > Disconnected\n"); connected = 0; } @@ -1630,23 +1659,20 @@ loop(void) } tox_do(tox); + /* Prepare select-fd-set */ FD_ZERO(&rfds); - fdmax = -1; + for (i = 0; i < LEN(gslots); i++) { - FD_SET(gslots[i].fd[IN], &rfds); - if (gslots[i].fd[IN] > fdmax) - fdmax = gslots[i].fd[IN]; + FD_APPEND(gslots[i].fd[IN]); } TAILQ_FOREACH(req, &reqhead, entry) { - FD_SET(req->fd, &rfds); - if(req->fd > fdmax) - fdmax = req->fd; + FD_APPEND(req->fd); } TAILQ_FOREACH(f, &friendhead, entry) { - /* Check cooldown state of file transfers */ + /* File transfer cooldown */ if (f->tx.cooldown) { clock_gettime(CLOCK_MONOTONIC, &curtime); diff = timediff(f->tx.lastblock, curtime); @@ -1660,25 +1686,19 @@ loop(void) /* Only monitor friends that are online */ if (tox_get_friend_connection_status(tox, f->num) == 1) { - FD_SET(f->fd[FTEXT_IN], &rfds); - if (f->fd[FTEXT_IN] > fdmax) - fdmax = f->fd[FTEXT_IN]; + FD_APPEND(f->fd[FTEXT_IN]); + if (f->tx.state == TRANSFER_NONE || (f->tx.state == TRANSFER_INPROGRESS && !f->tx.cooldown)) { - FD_SET(f->fd[FFILE_IN], &rfds); - if (f->fd[FFILE_IN] > fdmax) - fdmax = f->fd[FFILE_IN]; + FD_APPEND(f->fd[FFILE_IN]); } if (f->av.num < 0 || - (toxav_get_call_state(toxav, f->av.num) == av_CallActive && f->av.transmission)) { - FD_SET(f->fd[FCALL_IN], &rfds); - if (f->fd[FCALL_IN] > fdmax) - fdmax = f->fd[FCALL_IN]; + (toxav_get_call_state(toxav, f->av.num) == av_CallActive && + f->av.state & TRANSMITTING)) { + FD_APPEND(f->fd[FCALL_IN]); } } - FD_SET(f->fd[FREMOVE], &rfds); - if (f->fd[FREMOVE] > fdmax) - fdmax = f->fd[FREMOVE]; + FD_APPEND(f->fd[FREMOVE]); } tv.tv_sec = 0; @@ -1690,9 +1710,7 @@ loop(void) eprintf("select:"); } - /* Check for broken transfers, i.e. the friend went offline - * in the middle of a transfer or you close file_out. - */ + /* Check for broken transfers (friend went offline, file_out was closed) */ TAILQ_FOREACH(f, &friendhead, entry) { if (tox_get_friend_connection_status(tox, f->num) == 0) { canceltxtransfer(f); @@ -1734,22 +1752,20 @@ loop(void) continue; if (f->rxstate == TRANSFER_NONE) continue; - if (f->fd[FFILE_OUT] < 0) { - r = openat(f->dirfd, ffiles[FFILE_OUT].name, - ffiles[FFILE_OUT].flags, 0666); - if (r < 0) { - if (errno != ENXIO) - eprintf("openat %s:", ffiles[FFILE_OUT].name); + if (f->fd[FFILE_OUT] >= 0) + continue; + r = openat(f->dirfd, ffiles[FFILE_OUT].name, ffiles[FFILE_OUT].flags); + if (r < 0) { + if (errno != ENXIO) + eprintf("openat %s:", ffiles[FFILE_OUT].name); + } else { + f->fd[FFILE_OUT] = r; + if (tox_file_send_control(tox, f->num, 1, 0, TOX_FILECONTROL_ACCEPT, NULL, 0) < 0) { + weprintf("Failed to accept transfer from receiver\n"); + cancelrxtransfer(f); } else { - f->fd[FFILE_OUT] = r; - if (tox_file_send_control(tox, f->num, 1, 0, - TOX_FILECONTROL_ACCEPT, NULL, 0) < 0) { - weprintf("Failed to accept transfer from receiver\n"); - cancelrxtransfer(f); - } else { - logmsg(": %s : Rx > Accepted\n", f->name); - f->rxstate = TRANSFER_INPROGRESS; - } + logmsg(": %s : Rx > Accepted\n", f->name); + f->rxstate = TRANSFER_INPROGRESS; } } } @@ -1758,43 +1774,40 @@ loop(void) TAILQ_FOREACH(f, &friendhead, entry) { if (tox_get_friend_connection_status(tox, f->num) == 0) continue; - if (f->fd[FCALL_OUT] < 0) { - r = openat(f->dirfd, ffiles[FCALL_OUT].name, - ffiles[FCALL_OUT].flags, 0666); + if (f->av.num < 0) + continue; + + switch (toxav_get_call_state(toxav, f->av.num)) { + case av_CallStarting: + r = toxav_answer(toxav, f->av.num, &toxavconfig); if (r < 0) { - if (errno != ENXIO) - eprintf("openat %s:", ffiles[FCALL_OUT].name); - continue; - } else { - f->fd[FCALL_OUT] = r; + weprintf("Failed to answer call\n"); + r = toxav_reject(toxav, f->av.num, NULL); + if (r < 0) + weprintf("Failed to reject call\n"); } - } - if (f->av.num != -1) { - switch (toxav_get_call_state(toxav, f->av.num)) { - case av_CallStarting: - r = toxav_answer(toxav, f->av.num, &toxavconfig); - if (r < 0) { - weprintf("Failed to answer call\n"); - r = toxav_reject(toxav, f->av.num, NULL); - if (r < 0) - weprintf("Failed to reject call\n"); - } - break; - case av_CallActive: - fd = openat(f->dirfd, ffiles[FCALL_OUT].name, ffiles[FCALL_OUT].flags); - if (fd < 0) { - if (errno == ENXIO) { - r = toxav_hangup(toxav, f->av.num); - if (r < 0) - weprintf("Failed to hang up\n"); - } - } else { + break; + case av_CallActive: + fd = fifoopen(f->dirfd, ffiles[FCALL_OUT]); + if (fd < 0) { + f->av.state &= ~INCOMING; + close(fd); + } else { + f->av.state |= INCOMING; + if (f->fd[FCALL_OUT] >= 0) close(fd); - } - break; - default: - break; + else + f->fd[FCALL_OUT] = fd; + } + + if (!(f->av.state & INCOMING) && !(f->av.state & OUTGOING)) { + r = toxav_hangup(toxav, f->av.num); + if (r < 0) + weprintf("Failed to hang up\n"); } + break; + default: + break; } } @@ -1847,8 +1860,7 @@ loop(void) switch (f->tx.state) { case TRANSFER_NONE: /* Prepare a new transfer */ - now = time(NULL); - snprintf(tstamp, sizeof(tstamp), "%lu", (unsigned long)now); + snprintf(tstamp, sizeof(tstamp), "%lu", (unsigned long)time(NULL)); if (tox_new_file_sender(tox, f->num, 0, (uint8_t *)tstamp, strlen(tstamp)) < 0) { weprintf("Failed to initiate new transfer\n"); @@ -1872,9 +1884,11 @@ loop(void) fiforeset(f->dirfd, &f->fd[FCALL_IN], ffiles[FCALL_IN]); break; } + f->av.state |= OUTGOING; logmsg(": %s : Audio : Tx > Inviting\n", f->name); break; case av_CallActive: + f->av.state |= OUTGOING; sendfriendcalldata(f); break; default: @@ -1946,11 +1960,11 @@ shutdown(void) static void usage(void) { - fprintf(stderr, "usage: %s [-4|-6] [-t] [-p]\n", argv0); - fprintf(stderr, " -4\tIPv4 only\n"); - fprintf(stderr, " -6\tIPv6 only\n"); - fprintf(stderr, " -t\tEnable TCP mode (UDP by default)\n"); - fprintf(stderr, " -p\tEnable TCP socks5 proxy\n"); + fprintf(stderr, "usage: %s [-4|-6] [-t] [-p]\n" + " -4\tIPv4 only\n" + " -6\tIPv6 only\n" + " -t\tEnable TCP mode (UDP by default)\n" + " -p\tEnable TCP socks5 proxy\n", argv0); exit(1); }