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