2 * Copyright (C) 2012 Andre Noll <maan@systemlinux.org>
4 * Licensed under the GPL v2. For licencing details see COPYING.
7 /** \file sideband.c Implementation of the sideband API. */
14 #include "portable_io.h"
18 /** Each sideband packet consists of a header and a data part. */
19 #define SIDEBAND_HEADER_SIZE 5
22 char header
[SIDEBAND_HEADER_SIZE
];
23 size_t bytes_dispatched
; /* including header */
24 sb_transformation trafo
;
32 * Prepare to receive a sideband packet.
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.
38 * \a trafo_context is ignored if \a t is \p NULL.
40 * \return An opaque sideband handle.
42 struct sb_context
*sb_new_recv(size_t max_size
, sb_transformation t
,
45 struct sb_context
*c
= para_calloc(sizeof(*c
));
47 c
->max_size
= max_size
;
49 c
->trafo_context
= trafo_context
;
54 * Prepare to write a sideband packet.
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().
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.
65 * \return See \ref sb_new_recv().
67 struct sb_context
*sb_new_send(struct sb_buffer
*sbb
, bool dont_free
,
68 sb_transformation t
, void *trafo_context
)
70 struct sb_context
*c
= para_calloc(sizeof(*c
));
71 struct iovec src
, dst
, *srcp
, *dstp
;
75 c
->trafo_context
= trafo_context
;
76 c
->dont_free
= dont_free
;
78 write_u32(c
->header
, sbb
->iov
.iov_len
);
79 write_u8(c
->header
+ 4, sbb
->band
);
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
);
88 if (!iov_valid(&sbb
->iov
))
92 t(srcp
, dstp
, trafo_context
);
93 if (srcp
->iov_base
!= dstp
->iov_base
) {
103 * Deallocate all memory associated with a sideband handle.
105 * \param c The sideband handle.
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.
110 void sb_free(struct sb_context
*c
)
115 free(c
->sbb
.iov
.iov_base
);
120 * Obtain pointer(s) to the sideband send buffer(s).
122 * \param c The sideband handle.
123 * \param iov Array of two I/O vectors.
125 * \return The number of buffers that need to be sent, either 1 or 2.
127 * This function fills out the buffers described by \a iov. The result can be
128 * passed to \ref xwritev() or similar.
130 * \sa \ref sb_get_recv_buffer().
132 int sb_get_send_buffers(struct sb_context
*c
, struct iovec iov
[2])
134 struct sb_buffer
*sbb
= &c
->sbb
;
135 size_t n
= c
->bytes_dispatched
;
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
))
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
;
150 iov
[1].iov_base
= NULL
;
156 * Update the sideband context after data has been sent.
158 * \param c The sideband handle.
159 * \param nbytes The number of sent bytes.
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.
165 bool sb_sent(struct sb_context
*c
, size_t nbytes
)
167 struct sb_buffer
*sbb
= &c
->sbb
;
168 size_t sz
= SIDEBAND_HEADER_SIZE
+ sbb
->iov
.iov_len
;
170 assert(c
->bytes_dispatched
+ nbytes
<= sz
);
171 c
->bytes_dispatched
+= nbytes
;
172 if (c
->bytes_dispatched
< sz
)
179 * Obtain a pointer to the next sideband read buffer.
181 * \param c The sideband handle.
182 * \param iov Result IO vector.
184 * This fills in \a iov to point to the buffer to which the next chunk of
185 * received data should be written.
187 void sb_get_recv_buffer(struct sb_context
*c
, struct iovec
*iov
)
189 struct sb_buffer
*sbb
= &c
->sbb
;
190 size_t n
= c
->bytes_dispatched
;
192 if (n
< SIDEBAND_HEADER_SIZE
) {
193 iov
->iov_base
= c
->header
+ n
;
194 iov
->iov_len
= SIDEBAND_HEADER_SIZE
- n
;
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
;
205 * Update the sideband context after data has been received.
207 * \param c The sideband handle.
208 * \param nbytes The number of bytes that have been received.
209 * \param result The received sideband packet.
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
215 * Only if the function returns one, the sideband buffer pointed to by \a
216 * result is set to point to the received data.
218 int sb_received(struct sb_context
*c
, size_t nbytes
, struct sb_buffer
*result
)
220 struct sb_buffer
*sbb
= &c
->sbb
;
221 size_t n
= c
->bytes_dispatched
,
222 sz
= SIDEBAND_HEADER_SIZE
+ sbb
->iov
.iov_len
;
224 assert(n
+ nbytes
<= sz
);
225 c
->bytes_dispatched
+= nbytes
;
226 if (c
->bytes_dispatched
< SIDEBAND_HEADER_SIZE
)
228 if (n
>= SIDEBAND_HEADER_SIZE
) { /* header has already been received */
229 if (c
->bytes_dispatched
< sz
) /* need to recv more body data */
231 /* received everything, decrypt and return sbb */
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
;
240 ((char *)(sbb
->iov
.iov_base
))[sbb
->iov
.iov_len
] = '\0';
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
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
);
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 */
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
;
268 * We like to reserve one extra byte for the terminating NULL
269 * character. However, we must make sure the +1 below does not
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 */