Merge branch 't/opusdec_busy_loop_fix'
[paraslash.git] / sideband.c
1 /*
2 * Copyright (C) 2012-2014 Andre Noll <maan@systemlinux.org>
3 *
4 * Licensed under the GPL v2. For licencing details see COPYING.
5 */
6
7 /** \file sideband.c Implementation of the sideband API. */
8
9 #include <regex.h>
10 #include <sys/uio.h>
11
12 #include "para.h"
13 #include "error.h"
14 #include "portable_io.h"
15 #include "string.h"
16 #include "sideband.h"
17
18 /** Each sideband packet consists of a header and a data part. */
19 #define SIDEBAND_HEADER_SIZE 5
20
21 struct sb_context {
22 char header[SIDEBAND_HEADER_SIZE];
23 size_t bytes_dispatched; /* including header */
24 sb_transformation trafo;
25 void *trafo_context;
26 struct sb_buffer sbb;
27 size_t max_size;
28 bool dont_free;
29 };
30
31 /**
32 * Prepare to receive a sideband packet.
33 *
34 * \param max_size Do not allocate more than this many bytes.
35 * \param t Optional sideband transformation.
36 * \param trafo_context Passed verbatim to \a t.
37 *
38 * \a trafo_context is ignored if \a t is \p NULL.
39 *
40 * \return An opaque sideband handle.
41 */
42 struct sb_context *sb_new_recv(size_t max_size, sb_transformation t,
43 void *trafo_context)
44 {
45 struct sb_context *c = para_calloc(sizeof(*c));
46
47 c->max_size = max_size;
48 c->trafo = t;
49 c->trafo_context = trafo_context;
50 return c;
51 }
52
53 /**
54 * Prepare to write a sideband packet.
55 *
56 * \param sbb Data and meta data to send.
57 * \param dont_free Do not try to deallocate the sideband buffer.
58 * \param t See \ref sb_new_recv().
59 * \param trafo_context See \ref sb_new_recv().
60 *
61 * It's OK to supply a zero-sized buffer in \a sbb. In this case only the band
62 * designator is sent through the sideband channel. Otherwise, if \a dont_free
63 * is false, the buffer of \a sbb is freed after the data has been sent.
64 *
65 * \return See \ref sb_new_recv().
66 */
67 struct sb_context *sb_new_send(struct sb_buffer *sbb, bool dont_free,
68 sb_transformation t, void *trafo_context)
69 {
70 struct sb_context *c = para_calloc(sizeof(*c));
71 struct iovec src, dst, *srcp, *dstp;
72
73 assert(sbb);
74 c->trafo = t;
75 c->trafo_context = trafo_context;
76 c->dont_free = dont_free;
77 c->sbb = *sbb;
78 write_u32(c->header, sbb->iov.iov_len);
79 write_u8(c->header + 4, sbb->band);
80 if (!t)
81 goto out;
82 src = (typeof(src)){.iov_base = c->header, .iov_len = SIDEBAND_HEADER_SIZE};
83 t(&src, &dst, trafo_context);
84 if (src.iov_base != dst.iov_base) {
85 memcpy(c->header, dst.iov_base, SIDEBAND_HEADER_SIZE);
86 free(dst.iov_base);
87 }
88 if (!iov_valid(&sbb->iov))
89 goto out;
90 srcp = &sbb->iov;
91 dstp = &c->sbb.iov;
92 t(srcp, dstp, trafo_context);
93 if (srcp->iov_base != dstp->iov_base) {
94 if (!c->dont_free)
95 free(srcp->iov_base);
96 c->dont_free = false;
97 }
98 out:
99 return c;
100 }
101
102 /**
103 * Deallocate all memory associated with a sideband handle.
104 *
105 * \param c The sideband handle.
106 *
107 * \a c must point to a handle previously returned by \ref sb_new_recv() or
108 * \ref sb_new_send(). It \a c is \p NULL, the function does nothing.
109 */
110 void sb_free(struct sb_context *c)
111 {
112 if (!c)
113 return;
114 if (!c->dont_free)
115 free(c->sbb.iov.iov_base);
116 free(c);
117 }
118
119 /**
120 * Obtain pointer(s) to the sideband send buffer(s).
121 *
122 * \param c The sideband handle.
123 * \param iov Array of two I/O vectors.
124 *
125 * \return The number of buffers that need to be sent, either 1 or 2.
126 *
127 * This function fills out the buffers described by \a iov. The result can be
128 * passed to \ref xwritev() or similar.
129 *
130 * \sa \ref sb_get_recv_buffer().
131 */
132 int sb_get_send_buffers(struct sb_context *c, struct iovec iov[2])
133 {
134 struct sb_buffer *sbb = &c->sbb;
135 size_t n = c->bytes_dispatched;
136
137 if (n < SIDEBAND_HEADER_SIZE) {
138 iov[0].iov_base = c->header + n;
139 iov[0].iov_len = SIDEBAND_HEADER_SIZE - n;
140 if (!iov_valid(&sbb->iov))
141 goto out;
142 iov[1] = sbb->iov;
143 return 2;
144 }
145 n -= SIDEBAND_HEADER_SIZE;
146 assert(n < sbb->iov.iov_len);
147 iov[0].iov_base = sbb->iov.iov_base + n;
148 iov[0].iov_len = sbb->iov.iov_len - n;
149 out:
150 iov[1].iov_base = NULL;
151 iov[1].iov_len = 0;
152 return 1;
153 }
154
155 /**
156 * Update the sideband context after data has been sent.
157 *
158 * \param c The sideband handle.
159 * \param nbytes The number of sent bytes.
160 *
161 * \return False if more data must be sent to complete the sideband transfer,
162 * true if the transfer is complete. In this case all resources are freed and
163 * the sideband handle must not be used any more.
164 */
165 bool sb_sent(struct sb_context *c, size_t nbytes)
166 {
167 struct sb_buffer *sbb = &c->sbb;
168 size_t sz = SIDEBAND_HEADER_SIZE + sbb->iov.iov_len;
169
170 assert(c->bytes_dispatched + nbytes <= sz);
171 c->bytes_dispatched += nbytes;
172 if (c->bytes_dispatched < sz)
173 return false;
174 sb_free(c);
175 return true;
176 }
177
178 /**
179 * Obtain a pointer to the next sideband read buffer.
180 *
181 * \param c The sideband handle.
182 * \param iov Result IO vector.
183 *
184 * This fills in \a iov to point to the buffer to which the next chunk of
185 * received data should be written.
186 */
187 void sb_get_recv_buffer(struct sb_context *c, struct iovec *iov)
188 {
189 struct sb_buffer *sbb = &c->sbb;
190 size_t n = c->bytes_dispatched;
191
192 if (n < SIDEBAND_HEADER_SIZE) {
193 iov->iov_base = c->header + n;
194 iov->iov_len = SIDEBAND_HEADER_SIZE - n;
195 return;
196 }
197 n -= SIDEBAND_HEADER_SIZE;
198 assert(sbb->iov.iov_base);
199 assert(sbb->iov.iov_len > n);
200 iov->iov_base = sbb->iov.iov_base + n;
201 iov->iov_len = sbb->iov.iov_len - n;
202 }
203
204 /**
205 * Update the sideband context after data has been received.
206 *
207 * \param c The sideband handle.
208 * \param nbytes The number of bytes that have been received.
209 * \param result The received sideband packet.
210 *
211 * \return Negative on errors, zero if more data needs to be read to complete
212 * this sideband packet, one if the sideband packet has been received
213 * completely.
214 *
215 * Only if the function returns one, the sideband buffer pointed to by \a
216 * result is set to point to the received data.
217 */
218 int sb_received(struct sb_context *c, size_t nbytes, struct sb_buffer *result)
219 {
220 struct sb_buffer *sbb = &c->sbb;
221 size_t n = c->bytes_dispatched,
222 sz = SIDEBAND_HEADER_SIZE + sbb->iov.iov_len;
223
224 assert(n + nbytes <= sz);
225 c->bytes_dispatched += nbytes;
226 if (c->bytes_dispatched < SIDEBAND_HEADER_SIZE)
227 return 0;
228 if (n >= SIDEBAND_HEADER_SIZE) { /* header has already been received */
229 if (c->bytes_dispatched < sz) /* need to recv more body data */
230 return 0;
231 /* received everything, decrypt and return sbb */
232 if (c->trafo) {
233 struct iovec dst;
234 c->trafo(&sbb->iov, &dst, c->trafo_context);
235 if (sbb->iov.iov_base != dst.iov_base) {
236 free(sbb->iov.iov_base);
237 sbb->iov.iov_base = dst.iov_base;
238 }
239 }
240 ((char *)(sbb->iov.iov_base))[sbb->iov.iov_len] = '\0';
241 goto success;
242 }
243 /* header has been received, decrypt and decode it */
244 if (c->trafo) { /* decrypt */
245 struct iovec dst, src = (typeof(src)) {
246 .iov_base = c->header,
247 .iov_len = SIDEBAND_HEADER_SIZE
248 };
249 c->trafo(&src, &dst, c->trafo_context);
250 if (src.iov_base != dst.iov_base) {
251 memcpy(c->header, dst.iov_base,
252 SIDEBAND_HEADER_SIZE);
253 free(dst.iov_base);
254 }
255 }
256 /* Decode header, setup sbb */
257 sbb->iov.iov_len = read_u32(c->header);
258 sbb->band = read_u8(c->header + 4);
259 sbb->iov.iov_base = NULL;
260 if (sbb->iov.iov_len == 0) /* zero-sized msg */
261 goto success;
262 if (c->max_size > 0 && sbb->iov.iov_len > c->max_size) {
263 PARA_ERROR_LOG("packet too big (is %zu, max %zu)\n",
264 sbb->iov.iov_len, c->max_size);
265 return -E_SB_PACKET_SIZE;
266 }
267 /*
268 * We like to reserve one extra byte for the terminating NULL
269 * character. However, we must make sure the +1 below does not
270 * overflow iov_len.
271 */
272 if (sbb->iov.iov_len == (size_t)-1)
273 return -E_SB_PACKET_SIZE;
274 sbb->iov.iov_base = para_malloc(sbb->iov.iov_len + 1);
275 return 0; /* ready to read body */
276 success:
277 *result = c->sbb;
278 free(c);
279 return 1;
280 }