diff options
Diffstat (limited to 'src/ot_batch.c')
| -rw-r--r-- | src/ot_batch.c | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/src/ot_batch.c b/src/ot_batch.c new file mode 100644 index 0000000..e4dda0b --- /dev/null +++ b/src/ot_batch.c | |||
| @@ -0,0 +1,156 @@ | |||
| 1 | /* This software was written by Dirk Engling <erdgeist@erdgeist.org> | ||
| 2 | It is considered beerware. Prost. Skol. Cheers or whatever. */ | ||
| 3 | |||
| 4 | /* System */ | ||
| 5 | #include <errno.h> | ||
| 6 | #include <limits.h> | ||
| 7 | #include <stdlib.h> | ||
| 8 | #include <string.h> | ||
| 9 | #include <sys/socket.h> | ||
| 10 | #include <sys/uio.h> | ||
| 11 | #include <unistd.h> | ||
| 12 | |||
| 13 | /* Opentracker */ | ||
| 14 | #include "ot_batch.h" | ||
| 15 | |||
| 16 | #ifndef IOV_MAX | ||
| 17 | #define IOV_MAX 1024 | ||
| 18 | #endif | ||
| 19 | |||
| 20 | #define OT_BATCH_MIN_ENTRIES 16 | ||
| 21 | |||
| 22 | static int batch_room_for(ot_batch *batch, size_t wanted) { | ||
| 23 | size_t space = batch->space ? batch->space : OT_BATCH_MIN_ENTRIES; | ||
| 24 | struct iovec *grown; | ||
| 25 | |||
| 26 | if (batch->count + wanted <= batch->space) | ||
| 27 | return 0; | ||
| 28 | |||
| 29 | /* During a chunked transfer entries are appended while earlier ones are | ||
| 30 | still going out, so the front of the array frees up as we go. Slide the | ||
| 31 | rest down before asking for more. */ | ||
| 32 | if (batch->first && batch->count - batch->first + wanted <= batch->space) { | ||
| 33 | memmove(batch->entries, batch->entries + batch->first, (batch->count - batch->first) * sizeof(struct iovec)); | ||
| 34 | batch->count -= batch->first; | ||
| 35 | batch->first = 0; | ||
| 36 | return 0; | ||
| 37 | } | ||
| 38 | |||
| 39 | while (space < batch->count + wanted) | ||
| 40 | space *= 2; | ||
| 41 | |||
| 42 | if (!(grown = realloc(batch->entries, space * sizeof(struct iovec)))) | ||
| 43 | return -1; | ||
| 44 | |||
| 45 | batch->entries = grown; | ||
| 46 | batch->space = space; | ||
| 47 | return 0; | ||
| 48 | } | ||
| 49 | |||
| 50 | int batch_add(ot_batch *batch, void *buf, size_t len) { | ||
| 51 | if (batch_room_for(batch, 1)) { | ||
| 52 | free(buf); | ||
| 53 | return -1; | ||
| 54 | } | ||
| 55 | |||
| 56 | batch->entries[batch->count].iov_base = buf; | ||
| 57 | batch->entries[batch->count].iov_len = len; | ||
| 58 | ++batch->count; | ||
| 59 | return 0; | ||
| 60 | } | ||
| 61 | |||
| 62 | int batch_add_vector(ot_batch *batch, const struct iovec *entries, size_t count) { | ||
| 63 | if (batch_room_for(batch, count)) { | ||
| 64 | while (count--) | ||
| 65 | free(entries[count].iov_base); | ||
| 66 | return -1; | ||
| 67 | } | ||
| 68 | |||
| 69 | memcpy(batch->entries + batch->count, entries, count * sizeof(struct iovec)); | ||
| 70 | batch->count += count; | ||
| 71 | return 0; | ||
| 72 | } | ||
| 73 | |||
| 74 | /* Drop everything the kernel accepted, freeing each buffer as it goes */ | ||
| 75 | static void batch_consume(ot_batch *batch, size_t written) { | ||
| 76 | while (written) { | ||
| 77 | struct iovec *entry = batch->entries + batch->first; | ||
| 78 | size_t left = entry->iov_len - batch->offset; | ||
| 79 | |||
| 80 | if (written < left) { | ||
| 81 | batch->offset += written; | ||
| 82 | return; | ||
| 83 | } | ||
| 84 | |||
| 85 | free(entry->iov_base); | ||
| 86 | written -= left; | ||
| 87 | batch->offset = 0; | ||
| 88 | ++batch->first; | ||
| 89 | } | ||
| 90 | |||
| 91 | /* Nothing is outstanding, so the array can start over from the front */ | ||
| 92 | if (batch->first == batch->count) | ||
| 93 | batch->first = batch->count = 0; | ||
| 94 | } | ||
| 95 | |||
| 96 | /* A peer that has gone away must come back as an error rather than as a | ||
| 97 | signal that kills the tracker, so ask the kernel not to raise one where it | ||
| 98 | can be asked. macOS cannot, and relies on SIGPIPE being blocked. */ | ||
| 99 | static ssize_t batch_writev(int s, struct iovec *entries, int count) { | ||
| 100 | #ifdef MSG_NOSIGNAL | ||
| 101 | struct msghdr msg; | ||
| 102 | |||
| 103 | memset(&msg, 0, sizeof(msg)); | ||
| 104 | msg.msg_iov = entries; | ||
| 105 | msg.msg_iovlen = count; | ||
| 106 | return sendmsg(s, &msg, MSG_NOSIGNAL); | ||
| 107 | #else | ||
| 108 | return writev(s, entries, count); | ||
| 109 | #endif | ||
| 110 | } | ||
| 111 | |||
| 112 | ssize_t batch_send(int s, ot_batch *batch) { | ||
| 113 | size_t entries = batch->count - batch->first; | ||
| 114 | ssize_t written; | ||
| 115 | |||
| 116 | if (!batch_pending(batch)) | ||
| 117 | return BATCH_DONE; | ||
| 118 | |||
| 119 | if (entries > IOV_MAX) | ||
| 120 | entries = IOV_MAX; | ||
| 121 | |||
| 122 | /* The first entry may be half way out already */ | ||
| 123 | batch->entries[batch->first].iov_base = (char *)batch->entries[batch->first].iov_base + batch->offset; | ||
| 124 | batch->entries[batch->first].iov_len -= batch->offset; | ||
| 125 | written = batch_writev(s, batch->entries + batch->first, (int)entries); | ||
| 126 | batch->entries[batch->first].iov_base = (char *)batch->entries[batch->first].iov_base - batch->offset; | ||
| 127 | batch->entries[batch->first].iov_len += batch->offset; | ||
| 128 | |||
| 129 | if (written < 0) | ||
| 130 | return (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) ? BATCH_AGAIN : BATCH_FAILED; | ||
| 131 | if (!written) | ||
| 132 | return BATCH_FAILED; | ||
| 133 | |||
| 134 | batch_consume(batch, (size_t)written); | ||
| 135 | return written; | ||
| 136 | } | ||
| 137 | |||
| 138 | void batch_reset(ot_batch *batch) { | ||
| 139 | while (batch->first < batch->count) | ||
| 140 | free(batch->entries[batch->first++].iov_base); | ||
| 141 | |||
| 142 | free(batch->entries); | ||
| 143 | batch->entries = NULL; | ||
| 144 | batch->count = batch->first = batch->offset = batch->space = 0; | ||
| 145 | } | ||
| 146 | |||
| 147 | /* For the few constants the chunked encoding needs, which the batch frees | ||
| 148 | along with everything else */ | ||
| 149 | int batch_add_copy(ot_batch *batch, const void *buf, size_t len) { | ||
| 150 | void *copy = malloc(len); | ||
| 151 | |||
| 152 | if (!copy) | ||
| 153 | return -1; | ||
| 154 | memcpy(copy, buf, len); | ||
| 155 | return batch_add(batch, copy, len); | ||
| 156 | } | ||
