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