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