Branch data Line data Source code
1 : : // SPDX-License-Identifier: GPL-3.0-or-later
2 : : // SPDX-FileCopyrightText: Andy Holmes <andrew.g.r.holmes@gmail.com>
3 : :
4 : : #define G_LOG_DOMAIN "valent-bluez-muxer"
5 : :
6 : : #include "config.h"
7 : :
8 : : #ifndef _GNU_SOURCE
9 : : # define _GNU_SOURCE
10 : : #endif /* _GNU_SOURCE */
11 : :
12 : : #include <unistd.h>
13 : : #include <sys/eventfd.h>
14 : :
15 : : #include <glib/gprintf.h>
16 : : #include <glib-unix.h>
17 : : #include <gio/gio.h>
18 : : #include <valent.h>
19 : :
20 : : #include "valent-bluez-channel.h"
21 : : #include "valent-mux-io-stream.h"
22 : :
23 : : #include "valent-bluez-muxer.h"
24 : :
25 : : #define IDENTITY_BUFFER_MAX (8192)
26 : :
27 : : #define CERTIFICATE_HEADER "-----BEGIN CERTIFICATE-----\n"
28 : : #define CERTIFICATE_FOOTER "-----END CERTIFICATE-----\n"
29 : :
30 : : #define DEFAULT_BUFFER_SIZE (4096)
31 : : #define HEADER_SIZE (19)
32 : : #define PRIMARY_UUID "a0d0aaf4-1072-4d81-aa35-902a954b1266"
33 : : #define PROTOCOL_MIN (1)
34 : : #define PROTOCOL_MAX (1)
35 : :
36 : :
37 : : struct _ValentBluezMuxer
38 : : {
39 : : ValentObject parent_instance;
40 : :
41 : : GIOStream *base_stream;
42 : : uint16_t buffer_size;
43 : :
44 : : GHashTable *states;
45 : : GCancellable *cancellable;
46 : : unsigned int protocol_version;
47 : :
48 : : GThread *input_thread;
49 : : GInputStream *input_stream;
50 : : GOutputStream *output_stream;
51 : : };
52 : :
53 [ + + + - ]: 3949 : G_DEFINE_FINAL_TYPE (ValentBluezMuxer, valent_bluez_muxer, VALENT_TYPE_OBJECT)
54 : :
55 : : typedef enum {
56 : : PROP_BASE_STREAM = 1,
57 : : PROP_BUFFER_SIZE,
58 : : } ValentBluezMuxerProperty;
59 : :
60 : : static GParamSpec *properties[PROP_BUFFER_SIZE + 1] = { NULL, };
61 : :
62 : : /**
63 : : * MessageType:
64 : : * @MESSAGE_PROTOCOL: The protocol version
65 : : * @MESSAGE_OPEN: A request to open a new multiplexed channel
66 : : * @MESSAGE_CLOSE: A request to close a multiplexed channel
67 : : * @MESSAGE_READ: A request for more bytes
68 : : * @MESSAGE_WRITE: A packet of bytes
69 : : *
70 : : * Enumeration of multiplex message types.
71 : : */
72 : : typedef enum
73 : : {
74 : : MESSAGE_PROTOCOL_VERSION,
75 : : MESSAGE_OPEN_CHANNEL,
76 : : MESSAGE_CLOSE_CHANNEL,
77 : : MESSAGE_READ,
78 : : MESSAGE_WRITE
79 : : } MessageType;
80 : :
81 : : /**
82 : : * ChannelState:
83 : : * @uuid: the channel UUID
84 : : * @mutex: a lock for changes to the state
85 : : * @cond: a `GCond` for blocking threads
86 : : * @eventfd: a file descriptor for pollable sources
87 : : * @condition: the `GIOCondition`
88 : : * @stream: a `GIOStream`
89 : : * @buffer: an input buffer
90 : : * @size: size of the input buffer
91 : : * @head: data start
92 : : * @tail: data end
93 : : * @count: bytes in the buffer
94 : : * @read_free: free space in the input buffer
95 : : * @write_free: amount of bytes that can be written
96 : : *
97 : : * A thread-safe struct, with a ring buffer, for tracking a multiplex channel.
98 : : *
99 : : * The @head and @tail offsets refer to the read and write positions,
100 : : * respectively, while @count indicates bytes in the buffer waiting to be read.
101 : : *
102 : : * @read_free is the amount of free space in the buffer for which a
103 : : * %MESSAGE_READ request has not been sent (i.e. @read_free <= @size - @count),
104 : : * while @write_free is the amount of bytes that can be written until another
105 : : * %MESSAGE_READ request is received.
106 : : */
107 : : typedef struct
108 : : {
109 : : char *uuid;
110 : : GMutex mutex;
111 : : GCond cond;
112 : : int eventfd;
113 : : GIOCondition condition;
114 : : GIOStream *stream;
115 : :
116 : : /* Input Buffer */
117 : : uint8_t *buffer;
118 : : size_t size;
119 : : size_t head;
120 : : size_t tail;
121 : : size_t count;
122 : :
123 : : /* Muxer State */
124 : : uint16_t read_free;
125 : : uint16_t write_free;
126 : : } ChannelState;
127 : :
128 : : static ChannelState *
129 : 6 : channel_state_new (ValentBluezMuxer *muxer,
130 : : const char *uuid)
131 : : {
132 : 6 : ChannelState *state = NULL;
133 : :
134 : 6 : state = g_atomic_rc_box_new0 (ChannelState);
135 : 6 : g_mutex_init (&state->mutex);
136 : 6 : g_mutex_lock (&state->mutex);
137 : 6 : g_cond_init (&state->cond);
138 [ - + ]: 6 : state->uuid = g_strdup (uuid);
139 : 6 : state->size = muxer->buffer_size;
140 : 6 : state->buffer = g_malloc0 (state->size);
141 : 6 : state->stream = g_object_new (VALENT_TYPE_MUX_IO_STREAM,
142 : : "muxer", muxer,
143 : : "uuid", uuid,
144 : : NULL);
145 : 6 : state->eventfd = eventfd (0, EFD_NONBLOCK | EFD_CLOEXEC);
146 [ - + ]: 6 : if (state->eventfd == -1)
147 : 0 : g_critical ("%s(): %s", G_STRFUNC, g_strerror (errno));
148 : 6 : g_mutex_unlock (&state->mutex);
149 : :
150 : 6 : return state;
151 : : }
152 : :
153 : : static void
154 : 0 : channel_state_free (gpointer data)
155 : : {
156 : 0 : ChannelState *state = (ChannelState *)data;
157 : :
158 : 0 : g_mutex_lock (&state->mutex);
159 [ # # ]: 0 : g_clear_object (&state->stream);
160 [ # # ]: 0 : g_clear_pointer (&state->buffer, g_free);
161 [ # # ]: 0 : g_clear_pointer (&state->uuid, g_free);
162 : 0 : g_clear_fd (&state->eventfd, NULL);
163 : 0 : g_cond_clear (&state->cond);
164 : 0 : g_mutex_unlock (&state->mutex);
165 : 0 : g_mutex_clear (&state->mutex);
166 : 0 : }
167 : :
168 : : static void
169 : 3967 : channel_state_unref (gpointer data)
170 : : {
171 : 4 : g_atomic_rc_box_release_full (data, channel_state_free);
172 : 3963 : }
173 : :
174 : : static inline gboolean
175 : 31 : channel_state_flush_unlocked (ChannelState *state,
176 : : GError **error)
177 : : {
178 : 31 : int64_t byte = 1;
179 : :
180 [ - + ]: 31 : if (write (state->eventfd, &byte, sizeof (uint64_t)) == -1)
181 : : {
182 : 0 : g_set_error_literal (error,
183 : : G_IO_ERROR,
184 : 0 : g_io_error_from_errno (errno),
185 : 0 : g_strerror (errno));
186 : 0 : return FALSE;
187 : : }
188 : 31 : g_cond_broadcast (&state->cond);
189 : :
190 : 31 : return TRUE;
191 : : }
192 : :
193 : : static inline gboolean
194 : 13 : channel_state_close_unlocked (ChannelState *state,
195 : : GError **error)
196 : : {
197 : 13 : state->condition &= ~(G_IO_IN | G_IO_OUT);
198 : 13 : state->condition |= G_IO_HUP;
199 : :
200 : 0 : return channel_state_flush_unlocked (state, error);
201 : : }
202 : :
203 : : static inline gssize
204 : 3882 : channel_state_read_unlocked (ChannelState *state,
205 : : uint8_t *buffer,
206 : : size_t count,
207 : : GError **error)
208 : : {
209 : 3882 : size_t tail_chunk;
210 : :
211 [ - + ]: 3882 : if ((state->condition & G_IO_ERR) != 0)
212 : : {
213 : 0 : g_set_error_literal (error,
214 : : G_IO_ERROR,
215 : : G_IO_ERROR_CLOSED,
216 : : g_strerror (EPIPE));
217 : 0 : return -1;
218 : : }
219 : :
220 : 3882 : count = MIN (count, state->count);
221 [ + + ]: 3882 : if (count == 0)
222 : : {
223 : : /* If the buffer has been emptied and the channel is marked closed,
224 : : * simulate EOF and set the error condition on the channel state
225 : : */
226 [ + - ]: 1 : if ((state->condition & G_IO_HUP) != 0)
227 : : {
228 : 1 : state->condition |= G_IO_ERR;
229 : 1 : return 0;
230 : : }
231 : :
232 : 0 : g_set_error_literal (error,
233 : : G_IO_ERROR,
234 : : G_IO_ERROR_WOULD_BLOCK,
235 : : g_strerror (EAGAIN));
236 : 0 : return -1;
237 : : }
238 : :
239 : 3881 : tail_chunk = MIN (state->size - state->head, count);
240 : 3881 : memcpy (buffer, state->buffer + state->head, tail_chunk);
241 [ - + ]: 3881 : if (count > tail_chunk)
242 : 0 : memcpy (buffer + tail_chunk, state->buffer, count - tail_chunk);
243 : :
244 : 3881 : state->head = (state->head + count) % state->size;
245 : 3881 : state->count -= count;
246 : :
247 [ + + ]: 3881 : if (state->count == 0)
248 : 7 : state->condition &= ~G_IO_IN;
249 : :
250 : 3881 : return count;
251 : : }
252 : :
253 : : static inline ChannelState *
254 : 3950 : channel_state_lookup (ValentBluezMuxer *self,
255 : : const char *uuid,
256 : : GError **error)
257 : : {
258 : 3950 : ChannelState *state = NULL;
259 : 3950 : ChannelState *ret = NULL;
260 : :
261 : 3950 : valent_object_lock (VALENT_OBJECT (self));
262 : 3950 : state = g_hash_table_lookup (self->states, uuid);
263 [ - + ]: 3950 : if (state == NULL)
264 : : {
265 : 0 : g_set_error_literal (error,
266 : : G_IO_ERROR,
267 : : G_IO_ERROR_CLOSED,
268 : : g_strerror (EPIPE));
269 : : }
270 : : else
271 : : {
272 : 3950 : ret = g_atomic_rc_box_acquire (state);
273 : : }
274 : 3950 : valent_object_unlock (VALENT_OBJECT (self));
275 : :
276 : 3950 : return ret;
277 : : }
278 : :
279 [ + + ]: 3956 : G_DEFINE_AUTOPTR_CLEANUP_FUNC (ChannelState, channel_state_unref)
280 : :
281 : : /**
282 : : * pack_header:
283 : : * @hdr: (out): a 19-byte buffer
284 : : * @type: a `MessageType` type
285 : : * @size: size of the message data
286 : : * @uuid: channel UUID
287 : : *
288 : : * Pack a multiplex header into @hdr.
289 : : */
290 : : static inline void
291 : 28 : pack_header (uint8_t *hdr,
292 : : MessageType type,
293 : : uint16_t size,
294 : : const char *uuid)
295 : : {
296 : 28 : static const uint8_t indices[16] = {
297 : : 0, 2, 4, 6, 9, 11, 14, 16, 19, 21, 24, 26, 28, 30, 32, 34,
298 : : };
299 : :
300 : 28 : hdr[0] = type;
301 : 28 : hdr[1] = (size >> 8) & 0xff;
302 : 28 : hdr[2] = size & 0xff;
303 : :
304 [ + + ]: 476 : for (size_t i = 0; i < G_N_ELEMENTS (indices); i++)
305 : : {
306 : 448 : int hi = g_ascii_xdigit_value (uuid[indices[i] + 0]);
307 : 448 : int lo = g_ascii_xdigit_value (uuid[indices[i] + 1]);
308 : :
309 : 448 : hdr[3 + i] = (hi << 4) | lo;
310 : : }
311 : 28 : }
312 : :
313 : : /**
314 : : * unpack_header:
315 : : * @hdr: a 19-byte buffer
316 : : * @type: (out): a `MessageType` type
317 : : * @size: (out): size of the message data
318 : : * @uuid: (out): a 37-byte buffer
319 : : *
320 : : * Unpack the multiplex header @hdr into @type, @size and @uuid.
321 : : */
322 : : static inline void
323 : 28 : unpack_header (const uint8_t *hdr,
324 : : MessageType *type,
325 : : uint16_t *size,
326 : : char *uuid)
327 : : {
328 [ - + ]: 28 : g_assert (type != NULL);
329 [ + - ]: 28 : g_assert (size != NULL);
330 : :
331 : 28 : *type = hdr[0];
332 : 28 : *size = (uint16_t)(hdr[1] << 8 | hdr[2]);
333 : 28 : g_snprintf (uuid, 37,
334 : : "%02x%02x%02x%02x-"
335 : : "%02x%02x-%02x%02x-%02x%02x-"
336 : : "%02x%02x%02x%02x%02x%02x",
337 : 28 : hdr[3], hdr[4], hdr[5], hdr[6],
338 : 28 : hdr[7], hdr[8], hdr[9], hdr[10], hdr[11], hdr[12],
339 : 28 : hdr[13], hdr[14], hdr[15], hdr[16], hdr[17], hdr[18]);
340 : 28 : }
341 : :
342 : : /*
343 : : * Receive Helpers
344 : : */
345 : : static inline gboolean
346 : 32 : recv_header (ValentBluezMuxer *self,
347 : : MessageType *type,
348 : : uint16_t *size,
349 : : char *uuid,
350 : : GCancellable *cancellable,
351 : : GError **error)
352 : : {
353 : 32 : uint8_t hdr[HEADER_SIZE] = { 0, };
354 : 32 : gboolean ret;
355 : :
356 : 32 : ret = g_input_stream_read_all (self->input_stream,
357 : : hdr,
358 : : sizeof (hdr),
359 : : NULL,
360 : : cancellable,
361 : : error);
362 [ + - ]: 28 : if (ret)
363 : 28 : unpack_header (hdr, type, size, uuid);
364 : :
365 : 28 : return ret;
366 : : }
367 : :
368 : : static inline gboolean
369 : 4 : recv_protocol_version (ValentBluezMuxer *self,
370 : : GCancellable *cancellable,
371 : : GError **error)
372 : : {
373 : 4 : gboolean ret;
374 : 4 : uint16_t supported_versions[2] = { 0, };
375 : 4 : uint16_t min_version, max_version;
376 : :
377 : 4 : ret = g_input_stream_read_all (self->input_stream,
378 : : supported_versions,
379 : : sizeof (supported_versions),
380 : : NULL,
381 : : cancellable,
382 : : error);
383 [ + - ]: 4 : if (ret)
384 : : {
385 : 4 : min_version = GUINT16_FROM_BE (supported_versions[0]);
386 : 4 : max_version = GUINT16_FROM_BE (supported_versions[1]);
387 [ - + ]: 4 : if (min_version > PROTOCOL_MAX)
388 : : {
389 : 0 : g_set_error (error,
390 : : G_IO_ERROR,
391 : : G_IO_ERROR_NOT_SUPPORTED,
392 : : "Protocol version too high (v%u)",
393 : : min_version);
394 : 0 : return FALSE;
395 : : }
396 : :
397 : 4 : self->protocol_version = MIN (max_version, PROTOCOL_MAX);
398 : : VALENT_NOTE ("Using multiplexer protocol v%u", self->protocol_version);
399 : : }
400 : :
401 : : return ret;
402 : : }
403 : :
404 : : static inline gboolean
405 : 1 : recv_open_channel (ValentBluezMuxer *self,
406 : : const char *uuid,
407 : : GCancellable *cancellable,
408 : : GError **error)
409 : : {
410 : 1 : gboolean ret = TRUE;
411 : :
412 : 1 : valent_object_lock (VALENT_OBJECT (self));
413 [ - + ]: 1 : if (g_hash_table_contains (self->states, uuid))
414 : : {
415 : 0 : g_set_error (error,
416 : : G_IO_ERROR,
417 : : G_IO_ERROR_ADDRESS_IN_USE,
418 : : "Channel already open (%s)",
419 : : uuid);
420 : 0 : ret = FALSE;
421 : : }
422 : : else
423 : : {
424 : 1 : g_autoptr (ChannelState) state = NULL;
425 : :
426 : : /* NOTE: the initial MESSAGE_READ request will be sent by
427 : : * valent_bluez_muxer_channel_accept()
428 : : */
429 : 1 : state = channel_state_new (self, uuid);
430 : 1 : g_hash_table_replace (self->states,
431 : 1 : state->uuid,
432 : : g_atomic_rc_box_acquire (state));
433 : : }
434 : 1 : valent_object_unlock (VALENT_OBJECT (self));
435 : :
436 : 1 : return ret;
437 : : }
438 : :
439 : : static inline gboolean
440 : 10 : recv_close_channel (ValentBluezMuxer *self,
441 : : const char *uuid,
442 : : GCancellable *cancellable,
443 : : GError **error)
444 : : {
445 : 10 : g_autoptr (ChannelState) state = NULL;
446 : 10 : gboolean ret;
447 : :
448 : 10 : state = channel_state_lookup (self, uuid, NULL);
449 [ + - ]: 10 : if (state == NULL)
450 : : return TRUE;
451 : :
452 : 10 : g_mutex_lock (&state->mutex);
453 : 10 : ret = channel_state_close_unlocked (state, error);
454 : 10 : g_mutex_unlock (&state->mutex);
455 : :
456 : 10 : return ret;
457 : : }
458 : :
459 : : static inline gboolean
460 : 6 : recv_read (ValentBluezMuxer *self,
461 : : const char *uuid,
462 : : GCancellable *cancellable,
463 : : GError **error)
464 : : {
465 : 6 : g_autoptr (ChannelState) state = NULL;
466 : 6 : uint16_t size_request;
467 : 6 : gboolean ret;
468 : :
469 : 6 : state = channel_state_lookup (self, uuid, error);
470 [ - + ]: 6 : if (state == NULL)
471 : : return FALSE;
472 : :
473 : 6 : ret = g_input_stream_read_all (self->input_stream,
474 : : &size_request,
475 : : sizeof (size_request),
476 : : NULL,
477 : : cancellable,
478 : : error);
479 [ - + ]: 6 : if (ret)
480 : : {
481 : 6 : g_mutex_lock (&state->mutex);
482 : 6 : state->write_free += GUINT16_FROM_BE (size_request);
483 : 6 : state->condition |= G_IO_OUT;
484 : 6 : ret = channel_state_flush_unlocked (state, error);
485 : 6 : g_mutex_unlock (&state->mutex);
486 : : }
487 : :
488 : : return ret;
489 : : }
490 : :
491 : : static inline gboolean
492 : 7 : recv_write (ValentBluezMuxer *self,
493 : : const char *uuid,
494 : : uint16_t size,
495 : : GCancellable *cancellable,
496 : : GError **error)
497 : : {
498 : 7 : g_autoptr (ChannelState) state = NULL;
499 : 7 : size_t tail_free;
500 : 7 : gboolean ret;
501 : :
502 : 7 : state = channel_state_lookup (self, uuid, error);
503 [ - + ]: 7 : if (state == NULL)
504 : : return FALSE;
505 : :
506 : 7 : g_mutex_lock (&state->mutex);
507 [ + - ]: 7 : if G_UNLIKELY (size > state->read_free)
508 : : {
509 : 0 : g_set_error (error,
510 : : G_IO_ERROR,
511 : : G_IO_ERROR_MESSAGE_TOO_LARGE,
512 : : "Write size (%u) exceeds requested (%u)",
513 : : size, state->read_free);
514 : 0 : g_mutex_unlock (&state->mutex);
515 : 0 : return FALSE;
516 : : }
517 : :
518 : 7 : tail_free = MIN (state->size - state->tail, size);
519 : 14 : ret = g_input_stream_read_all (self->input_stream,
520 : 7 : &state->buffer[state->tail],
521 : : tail_free,
522 : : NULL,
523 : : cancellable,
524 : : error);
525 [ + - - + ]: 7 : if (ret && size > tail_free)
526 : : {
527 : 0 : ret = g_input_stream_read_all (self->input_stream,
528 : 0 : &state->buffer[0],
529 : 0 : size - tail_free,
530 : : NULL,
531 : : cancellable,
532 : : error);
533 : : }
534 : :
535 [ - + ]: 7 : if (ret)
536 : : {
537 : 7 : state->tail = (state->tail + size) % state->size;
538 : 7 : state->count += size;
539 : 7 : state->read_free -= size;
540 : 7 : state->condition |= G_IO_IN;
541 : 7 : ret = channel_state_flush_unlocked (state, error);
542 : : }
543 : 7 : g_mutex_unlock (&state->mutex);
544 : :
545 : 7 : return ret;
546 : : }
547 : :
548 : : static gpointer
549 : 4 : valent_bluez_muxer_receive_loop (gpointer data)
550 : : {
551 : 4 : g_autoptr (ValentBluezMuxer) self = VALENT_BLUEZ_MUXER (data);
552 : 4 : MessageType type;
553 : 4 : uint16_t size;
554 : 4 : char uuid[37] = { 0, };
555 : 0 : g_autoptr (GError) error = NULL;
556 : :
557 [ + - ]: 32 : while (recv_header (self, &type, &size, uuid, self->cancellable, &error))
558 : : {
559 [ + + + + : 28 : switch ((MessageType)type)
+ - ]
560 : : {
561 : 4 : case MESSAGE_PROTOCOL_VERSION:
562 [ - + ]: 4 : if (!recv_protocol_version (self, self->cancellable, &error))
563 : 0 : VALENT_GOTO (out);
564 : : break;
565 : :
566 : 1 : case MESSAGE_OPEN_CHANNEL:
567 [ - + ]: 1 : if (!recv_open_channel (self, uuid, self->cancellable, &error))
568 : 0 : VALENT_GOTO (out);
569 : : break;
570 : :
571 : 10 : case MESSAGE_CLOSE_CHANNEL:
572 [ - + ]: 10 : if (!recv_close_channel (self, uuid, self->cancellable, &error))
573 : 0 : VALENT_GOTO (out);
574 : : break;
575 : :
576 : 6 : case MESSAGE_READ:
577 [ - + ]: 6 : if (!recv_read (self, uuid, self->cancellable, &error))
578 : 0 : VALENT_GOTO (out);
579 : : break;
580 : :
581 : 7 : case MESSAGE_WRITE:
582 [ - + ]: 7 : if (!recv_write (self, uuid, size, self->cancellable, &error))
583 : 0 : VALENT_GOTO (out);
584 : : break;
585 : :
586 : 0 : default:
587 : 0 : g_set_error (&error,
588 : : G_IO_ERROR,
589 : : G_IO_ERROR_INVALID_ARGUMENT,
590 : : "Unknown message type (%u)",
591 : : type);
592 : 32 : VALENT_GOTO (out);
593 : : }
594 : : }
595 : :
596 : 0 : out:
597 [ # # ]: 0 : if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
598 : : {
599 : 0 : g_debug ("%s(): %s", G_STRFUNC, error->message);
600 : 0 : valent_bluez_muxer_close (self, NULL, NULL);
601 : : }
602 : :
603 [ # # ]: 0 : return NULL;
604 : : }
605 : :
606 : : static inline gboolean
607 : 4 : send_protocol_version (ValentBluezMuxer *self,
608 : : GCancellable *cancellable,
609 : : GError **error)
610 : : {
611 : 4 : uint8_t message[HEADER_SIZE + 4] = { 0, };
612 : :
613 : : /* Pack the versions big-endian
614 : : */
615 : 4 : pack_header (message, MESSAGE_PROTOCOL_VERSION, 4, PRIMARY_UUID);
616 : 4 : message[HEADER_SIZE + 0] = (PROTOCOL_MIN >> 8) & 0xff;
617 : 4 : message[HEADER_SIZE + 1] = PROTOCOL_MIN & 0xff;
618 : 4 : message[HEADER_SIZE + 2] = (PROTOCOL_MAX >> 8) & 0xff;
619 : 4 : message[HEADER_SIZE + 3] = PROTOCOL_MAX & 0xff;
620 : :
621 : 4 : return g_output_stream_write_all (self->output_stream,
622 : : &message,
623 : : sizeof (message),
624 : : NULL,
625 : : cancellable,
626 : : error);
627 : : }
628 : :
629 : : static inline gboolean
630 : 1 : send_open_channel (ValentBluezMuxer *self,
631 : : const char *uuid,
632 : : GCancellable *cancellable,
633 : : GError **error)
634 : : {
635 : 1 : uint8_t message[HEADER_SIZE] = { 0, };
636 : :
637 : 1 : pack_header (message, MESSAGE_OPEN_CHANNEL, 0, uuid);
638 : :
639 : 1 : return g_output_stream_write_all (self->output_stream,
640 : : &message,
641 : : sizeof (message),
642 : : NULL,
643 : : cancellable,
644 : : error);
645 : : }
646 : :
647 : : static inline gboolean
648 : 10 : send_close_channel (ValentBluezMuxer *self,
649 : : const char *uuid,
650 : : GCancellable *cancellable,
651 : : GError **error)
652 : : {
653 : 10 : uint8_t message[HEADER_SIZE] = { 0, };
654 : :
655 : 10 : pack_header (message, MESSAGE_CLOSE_CHANNEL, 0, uuid);
656 : :
657 : 10 : return g_output_stream_write_all (self->output_stream,
658 : : &message,
659 : : sizeof (message),
660 : : NULL,
661 : : cancellable,
662 : : error);
663 : : }
664 : :
665 : : static inline gboolean
666 : 6 : send_read (ValentBluezMuxer *self,
667 : : const char *uuid,
668 : : uint16_t size_request,
669 : : GCancellable *cancellable,
670 : : GError **error)
671 : : {
672 : 6 : uint8_t message[HEADER_SIZE + 2] = { 0, };
673 : :
674 : : /* Pack the request big-endian
675 : : */
676 : 6 : pack_header (message, MESSAGE_READ, 2, uuid);
677 : 6 : message[HEADER_SIZE + 0] = (size_request >> 8) & 0xff;
678 : 6 : message[HEADER_SIZE + 1] = size_request & 0xff;
679 : :
680 : 6 : return g_output_stream_write_all (self->output_stream,
681 : : &message,
682 : : sizeof (message),
683 : : NULL,
684 : : cancellable,
685 : : error);
686 : : }
687 : :
688 : : static inline gboolean
689 : 7 : send_write (ValentBluezMuxer *self,
690 : : const char *uuid,
691 : : uint16_t size,
692 : : const void *buffer,
693 : : GCancellable *cancellable,
694 : : GError **error)
695 : : {
696 : 7 : uint8_t message[HEADER_SIZE] = { 0, };
697 : 7 : gboolean ret;
698 : :
699 : 7 : pack_header (message, MESSAGE_WRITE, size, uuid);
700 : :
701 : 7 : ret = g_output_stream_write_all (self->output_stream,
702 : : message,
703 : : sizeof (message),
704 : : NULL,
705 : : cancellable,
706 : : error);
707 [ + - ]: 7 : if (ret)
708 : : {
709 : 7 : ret = g_output_stream_write_all (self->output_stream,
710 : : buffer,
711 : : size,
712 : : NULL,
713 : : cancellable,
714 : : error);
715 : : }
716 : :
717 : 7 : return ret;
718 : : }
719 : :
720 : : /*
721 : : * ValentObject
722 : : */
723 : : static void
724 : 0 : valent_bluez_muxer_destroy (ValentObject *object)
725 : : {
726 : 0 : ValentBluezMuxer *self = VALENT_BLUEZ_MUXER (object);
727 : :
728 : 0 : valent_bluez_muxer_close (self, NULL, NULL);
729 : :
730 : 0 : VALENT_OBJECT_CLASS (valent_bluez_muxer_parent_class)->destroy (object);
731 : 0 : }
732 : :
733 : : /*
734 : : * GObject
735 : : *
736 : : * TODO: GAsyncInitable or merge with ValentMuxChannel
737 : : */
738 : : static void
739 : 4 : valent_bluez_muxer_constructed (GObject *object)
740 : : {
741 : 4 : ValentBluezMuxer *self = VALENT_BLUEZ_MUXER (object);
742 : :
743 : 4 : G_OBJECT_CLASS (valent_bluez_muxer_parent_class)->constructed (object);
744 : :
745 : 4 : valent_object_lock (VALENT_OBJECT (self));
746 [ + - + - : 4 : g_assert (G_IS_IO_STREAM (self->base_stream));
+ - - + ]
747 : 4 : self->input_stream = g_io_stream_get_input_stream (self->base_stream);
748 : 4 : self->output_stream = g_io_stream_get_output_stream (self->base_stream);
749 : 4 : valent_object_unlock (VALENT_OBJECT (self));
750 : 4 : }
751 : :
752 : : static void
753 : 0 : valent_bluez_muxer_finalize (GObject *object)
754 : : {
755 : 0 : ValentBluezMuxer *self = VALENT_BLUEZ_MUXER (object);
756 : :
757 : 0 : valent_object_lock (VALENT_OBJECT (self));
758 [ # # ]: 0 : g_clear_object (&self->base_stream);
759 [ # # ]: 0 : g_clear_object (&self->cancellable);
760 [ # # ]: 0 : g_clear_pointer (&self->states, g_hash_table_unref);
761 : 0 : valent_object_unlock (VALENT_OBJECT (self));
762 : :
763 : 0 : G_OBJECT_CLASS (valent_bluez_muxer_parent_class)->finalize (object);
764 : 0 : }
765 : :
766 : : static void
767 : 0 : valent_bluez_muxer_get_property (GObject *object,
768 : : guint prop_id,
769 : : GValue *value,
770 : : GParamSpec *pspec)
771 : : {
772 : 0 : ValentBluezMuxer *self = VALENT_BLUEZ_MUXER (object);
773 : :
774 [ # # # ]: 0 : switch ((ValentBluezMuxerProperty)prop_id)
775 : : {
776 : 0 : case PROP_BASE_STREAM:
777 : 0 : g_value_set_object (value, self->base_stream);
778 : 0 : break;
779 : :
780 : 0 : case PROP_BUFFER_SIZE:
781 : 0 : g_value_set_uint (value, self->buffer_size);
782 : 0 : break;
783 : :
784 : 0 : default:
785 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
786 : : }
787 : 0 : }
788 : :
789 : : static void
790 : 8 : valent_bluez_muxer_set_property (GObject *object,
791 : : guint prop_id,
792 : : const GValue *value,
793 : : GParamSpec *pspec)
794 : : {
795 : 8 : ValentBluezMuxer *self = VALENT_BLUEZ_MUXER (object);
796 : :
797 [ + + - ]: 8 : switch ((ValentBluezMuxerProperty)prop_id)
798 : : {
799 : 4 : case PROP_BASE_STREAM:
800 : 4 : self->base_stream = g_value_dup_object (value);
801 : 4 : break;
802 : :
803 : 4 : case PROP_BUFFER_SIZE:
804 : 4 : self->buffer_size = g_value_get_uint (value);
805 : 4 : break;
806 : :
807 : 0 : default:
808 : 0 : G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
809 : : }
810 : 8 : }
811 : :
812 : : static void
813 : 1 : valent_bluez_muxer_class_init (ValentBluezMuxerClass *klass)
814 : : {
815 : 1 : GObjectClass *object_class = G_OBJECT_CLASS (klass);
816 : 1 : ValentObjectClass *vobject_class = VALENT_OBJECT_CLASS (klass);
817 : :
818 : 1 : object_class->constructed = valent_bluez_muxer_constructed;
819 : 1 : object_class->finalize = valent_bluez_muxer_finalize;
820 : 1 : object_class->get_property = valent_bluez_muxer_get_property;
821 : 1 : object_class->set_property = valent_bluez_muxer_set_property;
822 : :
823 : 1 : vobject_class->destroy = valent_bluez_muxer_destroy;
824 : :
825 : : /**
826 : : * ValentBluezMuxer:base-stream:
827 : : *
828 : : * The `GIOStream` being wrapped.
829 : : */
830 : 2 : properties [PROP_BASE_STREAM] =
831 : 1 : g_param_spec_object ("base-stream", NULL, NULL,
832 : : G_TYPE_IO_STREAM,
833 : : (G_PARAM_READWRITE |
834 : : G_PARAM_CONSTRUCT_ONLY |
835 : : G_PARAM_EXPLICIT_NOTIFY |
836 : : G_PARAM_STATIC_STRINGS));
837 : :
838 : : /**
839 : : * ValentBluezMuxer:buffer-size:
840 : : *
841 : : * Size of the input buffer allocated to each multiplex channel.
842 : : */
843 : 2 : properties [PROP_BUFFER_SIZE] =
844 : 1 : g_param_spec_uint ("buffer-size", NULL, NULL,
845 : : 1024, G_MAXUINT16,
846 : : DEFAULT_BUFFER_SIZE,
847 : : (G_PARAM_READWRITE |
848 : : G_PARAM_CONSTRUCT_ONLY |
849 : : G_PARAM_EXPLICIT_NOTIFY |
850 : : G_PARAM_STATIC_STRINGS));
851 : :
852 : 1 : g_object_class_install_properties (object_class, G_N_ELEMENTS (properties), properties);
853 : 1 : }
854 : :
855 : : static void
856 : 4 : valent_bluez_muxer_init (ValentBluezMuxer *self)
857 : : {
858 : 4 : valent_object_lock (VALENT_OBJECT (self));
859 : 4 : self->cancellable = g_cancellable_new ();
860 : 4 : self->protocol_version = PROTOCOL_MAX;
861 : 4 : self->states = g_hash_table_new_full (g_str_hash,
862 : : g_str_equal,
863 : : NULL,
864 : : channel_state_unref);
865 : 4 : valent_object_unlock (VALENT_OBJECT (self));
866 : 4 : }
867 : :
868 : : typedef enum
869 : : {
870 : : HANDSHAKE_ENCRYPTED = (1 << 0),
871 : : HANDSHAKE_IDENTITY_READ = (1 << 1),
872 : : HANDSHAKE_IDENTITY_SENT = (1 << 2),
873 : : HANDSHAKE_FAILED = (1 << 3),
874 : : HANDSHAKE_COMPLETE = (HANDSHAKE_ENCRYPTED |
875 : : HANDSHAKE_IDENTITY_READ |
876 : : HANDSHAKE_IDENTITY_SENT),
877 : : } HandshakeFlags;
878 : :
879 : : typedef struct
880 : : {
881 : : GIOStream *connection;
882 : : JsonNode *identity;
883 : : JsonNode *peer_identity;
884 : : HandshakeFlags flags;
885 : : } HandshakeData;
886 : :
887 : : static void
888 : 4 : handshake_data_free (gpointer user_data)
889 : : {
890 : 4 : HandshakeData *data = (HandshakeData *)user_data;
891 : :
892 [ + - ]: 4 : g_clear_object (&data->connection);
893 [ + - ]: 4 : g_clear_pointer (&data->identity, json_node_unref);
894 [ + - ]: 4 : g_clear_pointer (&data->peer_identity, json_node_unref);
895 : 4 : g_free (data);
896 : 4 : }
897 : :
898 : : static void
899 : 4 : handshake_task_complete (GTask *task)
900 : : {
901 : 4 : ValentBluezMuxer *self = g_task_get_source_object (task);
902 : 4 : HandshakeData *data = g_task_get_task_data (task);
903 : 4 : g_autoptr (ValentChannel) channel = NULL;
904 [ + - ]: 4 : g_autoptr (GTlsCertificate) certificate = NULL;
905 [ + - - - ]: 4 : g_autoptr (GTlsCertificate) peer_certificate = NULL;
906 : 4 : const char *certificate_pem;
907 : 4 : const char *peer_certificate_pem;
908 : 4 : GError *error = NULL;
909 : :
910 [ + - ]: 4 : if (valent_packet_get_string (data->identity, "certificate", &certificate_pem))
911 : : {
912 : 4 : certificate = g_tls_certificate_new_from_pem (certificate_pem, -1, &error);
913 [ - + ]: 4 : if (certificate == NULL)
914 : : {
915 : 0 : g_task_return_error (task, g_steal_pointer (&error));
916 : 0 : return;
917 : : }
918 : : }
919 : :
920 [ + - ]: 4 : if (valent_packet_get_string (data->peer_identity, "certificate", &peer_certificate_pem))
921 : : {
922 : 0 : g_autofree char *pem = NULL;
923 : :
924 : : /* Some implementations might not include the header/footer
925 : : */
926 [ + - + - : 4 : if (!g_str_has_prefix (peer_certificate_pem, CERTIFICATE_HEADER))
- + ]
927 : : {
928 : 0 : pem = g_strconcat (CERTIFICATE_HEADER,
929 : : peer_certificate_pem,
930 : : CERTIFICATE_FOOTER,
931 : : NULL);
932 : : }
933 : : else
934 : : {
935 [ - + ]: 4 : pem = g_strdup (peer_certificate_pem);
936 : : }
937 : :
938 : 4 : peer_certificate = g_tls_certificate_new_from_pem (pem, -1, &error);
939 [ - + ]: 4 : if (peer_certificate == NULL)
940 : : {
941 : 0 : g_task_return_error (task, g_steal_pointer (&error));
942 : 0 : return;
943 : : }
944 : : }
945 : : else
946 : : {
947 : 0 : g_task_return_new_error (task,
948 : : G_TLS_ERROR,
949 : : G_TLS_ERROR_CERTIFICATE_REQUIRED,
950 : : "Peer failed to send TLS certificate");
951 : 0 : return;
952 : : }
953 : :
954 : 4 : channel = g_object_new (VALENT_TYPE_BLUEZ_CHANNEL,
955 : : "base-stream", data->connection,
956 : : "certificate", certificate,
957 : : "identity", data->identity,
958 : : "peer-certificate", peer_certificate,
959 : : "peer-identity", data->peer_identity,
960 : : "muxer", self,
961 : : NULL);
962 : 4 : g_task_return_pointer (task, g_object_ref (channel), g_object_unref);
963 : : }
964 : :
965 : : static void
966 : 4 : handshake_read_identity_cb (GInputStream *stream,
967 : : GAsyncResult *result,
968 : : gpointer user_data)
969 : : {
970 : 8 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
971 : 4 : HandshakeData *data = g_task_get_task_data (task);
972 [ - - + - ]: 4 : g_autoptr (JsonNode) secure_identity = NULL;
973 : 4 : GError *error = NULL;
974 : :
975 : 4 : secure_identity = valent_packet_from_stream_finish (stream, result, &error);
976 [ - + ]: 4 : if (secure_identity == NULL)
977 : : {
978 [ # # ]: 0 : if ((data->flags & HANDSHAKE_FAILED) == 0)
979 : : {
980 : 0 : data->flags |= HANDSHAKE_FAILED;
981 : 0 : g_task_return_error (task, g_steal_pointer (&error));
982 : : }
983 : :
984 [ # # ]: 0 : return;
985 : : }
986 : :
987 [ - + ]: 4 : g_clear_pointer (&data->peer_identity, json_node_unref);
988 [ + - ]: 4 : data->peer_identity = g_steal_pointer (&secure_identity);
989 : :
990 : 4 : data->flags |= HANDSHAKE_IDENTITY_READ;
991 [ + - ]: 4 : if (data->flags == HANDSHAKE_COMPLETE)
992 : 4 : handshake_task_complete (task);
993 : : }
994 : :
995 : : static void
996 : 4 : handshake_write_identity_cb (GOutputStream *stream,
997 : : GAsyncResult *result,
998 : : gpointer user_data)
999 : : {
1000 : 8 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
1001 : 4 : HandshakeData *data = g_task_get_task_data (task);
1002 : 4 : GError *error = NULL;
1003 : :
1004 [ - + ]: 4 : if (!valent_packet_to_stream_finish (stream, result, &error))
1005 : : {
1006 [ # # ]: 0 : if ((data->flags & HANDSHAKE_FAILED) == 0)
1007 : : {
1008 : 0 : data->flags |= HANDSHAKE_FAILED;
1009 : 0 : g_task_return_error (task, g_steal_pointer (&error));
1010 : : }
1011 : :
1012 [ # # ]: 0 : return;
1013 : : }
1014 : :
1015 : 4 : data->flags |= HANDSHAKE_IDENTITY_SENT;
1016 [ - + ]: 4 : if (data->flags == HANDSHAKE_COMPLETE)
1017 : 0 : handshake_task_complete (task);
1018 : : }
1019 : :
1020 : : static void
1021 : 4 : handshake_protocol_task_cb (GObject *object,
1022 : : GAsyncResult *result,
1023 : : gpointer user_data)
1024 : : {
1025 : 8 : g_autoptr (GTask) task = G_TASK (g_steal_pointer (&user_data));
1026 : 4 : ValentBluezMuxer *self = g_task_get_source_object (task);
1027 : 4 : HandshakeData *data = g_task_get_task_data (task);
1028 : 4 : GCancellable *cancellable = g_task_get_cancellable (task);
1029 [ + - - - ]: 4 : g_autoptr (GIOStream) stream = NULL;
1030 : 4 : GError *error = NULL;
1031 : :
1032 : 4 : stream = g_task_propagate_pointer (G_TASK (result), &error);
1033 [ - + ]: 4 : if (stream == NULL)
1034 : : {
1035 : 0 : g_task_return_error (task, g_steal_pointer (&error));
1036 : 0 : return;
1037 : : }
1038 : :
1039 : 4 : self->input_thread = g_thread_try_new ("valent-bluez-muxer",
1040 : : valent_bluez_muxer_receive_loop,
1041 : : g_object_ref (self),
1042 : : &error);
1043 [ - + ]: 4 : if (self->input_thread == NULL)
1044 : : {
1045 : 0 : g_task_return_error (task, g_steal_pointer (&error));
1046 : 0 : return;
1047 : : }
1048 : :
1049 : 4 : data->connection = g_object_ref (stream);
1050 : 4 : valent_packet_to_stream (g_io_stream_get_output_stream (data->connection),
1051 : : data->identity,
1052 : : cancellable,
1053 : : (GAsyncReadyCallback)handshake_write_identity_cb,
1054 : : g_object_ref (task));
1055 : 4 : valent_packet_from_stream (g_io_stream_get_input_stream (data->connection),
1056 : : IDENTITY_BUFFER_MAX,
1057 : : cancellable,
1058 : : (GAsyncReadyCallback)handshake_read_identity_cb,
1059 : : g_object_ref (task));
1060 : : }
1061 : :
1062 : : static void
1063 : 4 : handshake_protocol_task (GTask *task,
1064 : : gpointer source_object,
1065 : : gpointer task_data,
1066 : : GCancellable *cancellable)
1067 : : {
1068 : 4 : ValentBluezMuxer *self = VALENT_BLUEZ_MUXER (source_object);
1069 : 4 : ChannelState *state = (ChannelState *)task_data;
1070 : 8 : g_autoptr (GIOStream) ret = NULL;
1071 : 4 : uint16_t size_request;
1072 : 4 : GError *error = NULL;
1073 : :
1074 : 4 : g_mutex_lock (&state->mutex);
1075 : 4 : state->read_free += state->size;
1076 : 4 : size_request = state->size;
1077 : 4 : ret = g_object_ref (state->stream);
1078 : 4 : g_mutex_unlock (&state->mutex);
1079 : :
1080 : 4 : valent_object_lock (VALENT_OBJECT (self));
1081 [ + - - + ]: 8 : if (!send_protocol_version (self, cancellable, &error) ||
1082 : 4 : !send_read (self, PRIMARY_UUID, size_request, cancellable, &error))
1083 : : {
1084 [ # # ]: 0 : g_clear_object (&ret);
1085 : 0 : valent_bluez_muxer_close (self, NULL, NULL);
1086 : : }
1087 : 4 : valent_object_unlock (VALENT_OBJECT (self));
1088 : :
1089 [ + - ]: 4 : if (ret != NULL)
1090 : 4 : g_task_return_pointer (task, g_object_ref (ret), g_object_unref);
1091 : : else
1092 : 0 : g_task_return_error (task, g_steal_pointer (&error));
1093 : 4 : }
1094 : :
1095 : : /**
1096 : : * valent_bluez_muxer_handshake:
1097 : : * @muxer: a `ValentBluezMuxer`
1098 : : * @identity: a KDE Connect identity packet
1099 : : * @cancellable: (nullable): a `GCancellable`
1100 : : * @callback: (scope async): a `GAsyncReadyCallback`
1101 : : * @user_data: user supplied data
1102 : : *
1103 : : * Attempt to negotiate a multiplex channel on @muxer. This is a two-part
1104 : : * process involving negotiating the protocol version (currently only version 1)
1105 : : * and exchanging identity packets.
1106 : : *
1107 : : * Call [class@Valent.BluezMuxer.handshake_finish] to get the result.
1108 : : *
1109 : : * Returns: (transfer full): a `ValentChannel`
1110 : : */
1111 : : void
1112 : 4 : valent_bluez_muxer_handshake (GIOStream *base_stream,
1113 : : JsonNode *identity,
1114 : : GCancellable *cancellable,
1115 : : GAsyncReadyCallback callback,
1116 : : gpointer user_data)
1117 : : {
1118 : 4 : g_autoptr (ValentBluezMuxer) muxer = NULL;
1119 : 4 : g_autoptr (ChannelState) state = NULL;
1120 : 4 : g_autoptr (GTask) protocol = NULL;
1121 [ + - ]: 4 : g_autoptr (GTask) task = NULL;
1122 : 4 : HandshakeData *data = NULL;
1123 : :
1124 [ + - + - : 4 : g_return_if_fail (G_IS_IO_STREAM (base_stream));
+ - - + ]
1125 [ + - ]: 4 : g_return_if_fail (VALENT_IS_PACKET (identity));
1126 [ + + + - : 4 : g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- + - - ]
1127 : :
1128 : 4 : muxer = g_object_new (VALENT_TYPE_BLUEZ_MUXER,
1129 : : "base-stream", base_stream,
1130 : : "buffer-size", DEFAULT_BUFFER_SIZE,
1131 : : NULL);
1132 : :
1133 : 4 : valent_object_lock (VALENT_OBJECT (muxer));
1134 : 4 : state = channel_state_new (muxer, PRIMARY_UUID);
1135 : 4 : g_hash_table_replace (muxer->states,
1136 : 4 : state->uuid,
1137 : : g_atomic_rc_box_acquire (state));
1138 : 4 : valent_object_unlock (VALENT_OBJECT (muxer));
1139 : :
1140 : 4 : data = g_new0 (HandshakeData, 1);
1141 : 4 : data->identity = json_node_ref (identity);
1142 : 4 : data->flags |= HANDSHAKE_ENCRYPTED;
1143 : :
1144 : 4 : task = g_task_new (muxer, cancellable, callback, user_data);
1145 [ + - ]: 4 : g_task_set_source_tag (task, valent_bluez_muxer_handshake);
1146 : 4 : g_task_set_task_data (task, g_steal_pointer (&data), handshake_data_free);
1147 : :
1148 : 4 : protocol = g_task_new (muxer, cancellable, handshake_protocol_task_cb, g_object_ref (task));
1149 [ + - ]: 4 : g_task_set_source_tag (protocol, handshake_protocol_task);
1150 : 4 : g_task_set_task_data (protocol, g_steal_pointer (&state), channel_state_unref);
1151 [ + - ]: 4 : g_task_run_in_thread (protocol, handshake_protocol_task);
1152 : : }
1153 : :
1154 : : /**
1155 : : * valent_bluez_muxer_handshake_finish:
1156 : : * @muxer: a `ValentBluezMuxer`
1157 : : * @result: a `GAsyncResult`
1158 : : * @error: (nullable): a `GError`
1159 : : *
1160 : : * Finishes an operation started by [class@Valent.BluezMuxer.handshake].
1161 : : *
1162 : : * Returns: (transfer full): a `ValentChannel`
1163 : : */
1164 : : ValentChannel *
1165 : 4 : valent_bluez_muxer_handshake_finish (ValentBluezMuxer *muxer,
1166 : : GAsyncResult *result,
1167 : : GError **error)
1168 : : {
1169 [ - + ]: 4 : g_return_val_if_fail (VALENT_IS_BLUEZ_MUXER (muxer), NULL);
1170 [ + - ]: 4 : g_return_val_if_fail (g_task_is_valid (result, muxer), NULL);
1171 [ + - + - ]: 4 : g_return_val_if_fail (error == NULL || *error == NULL, NULL);
1172 : :
1173 : 4 : return g_task_propagate_pointer (G_TASK (result), error);
1174 : : }
1175 : :
1176 : : /**
1177 : : * valent_bluez_muxer_close:
1178 : : * @muxer: a `ValentBluezMuxer`
1179 : : * @cancellable: (nullable): a `GCancellable`
1180 : : * @error: (nullable): a `GError`
1181 : : *
1182 : : * Close the multiplex connection.
1183 : : *
1184 : : * Returns: %TRUE if successful, or %FALSE with @error set
1185 : : */
1186 : : gboolean
1187 : 0 : valent_bluez_muxer_close (ValentBluezMuxer *muxer,
1188 : : GCancellable *cancellable,
1189 : : GError **error)
1190 : : {
1191 : 0 : GHashTableIter iter;
1192 : 0 : ChannelState *state;
1193 : 0 : gboolean ret = TRUE;
1194 : :
1195 : 0 : VALENT_ENTRY;
1196 : :
1197 [ # # ]: 0 : g_assert (VALENT_IS_BLUEZ_MUXER (muxer));
1198 : :
1199 : 0 : valent_object_lock (VALENT_OBJECT (muxer));
1200 : 0 : ret = g_io_stream_close (muxer->base_stream, cancellable, error);
1201 [ # # # # ]: 0 : if (muxer->input_thread != NULL && muxer->input_thread != g_thread_self ())
1202 : : {
1203 : 0 : g_cancellable_cancel (muxer->cancellable);
1204 : 0 : g_thread_join (g_steal_pointer (&muxer->input_thread));
1205 : :
1206 : 0 : g_hash_table_iter_init (&iter, muxer->states);
1207 [ # # ]: 0 : while (g_hash_table_iter_next (&iter, NULL, (void **)&state))
1208 : : {
1209 : 0 : g_mutex_lock (&state->mutex);
1210 : 0 : channel_state_close_unlocked (state, NULL);
1211 : 0 : g_mutex_unlock (&state->mutex);
1212 : 0 : g_hash_table_iter_remove (&iter);
1213 : : }
1214 : : }
1215 : 0 : valent_object_unlock (VALENT_OBJECT (muxer));
1216 : :
1217 : 0 : VALENT_RETURN (ret);
1218 : : }
1219 : :
1220 : : /**
1221 : : * valent_bluez_muxer_channel_accept:
1222 : : * @muxer: a `ValentBluezMuxer`
1223 : : * @uuid: a channel UUID
1224 : : * @cancellable: (nullable): a `GCancellable`
1225 : : * @error: (nullable): a `GError`
1226 : : *
1227 : : * Blocks waiting for a channel to be opened for @uuid.
1228 : : *
1229 : : * Returns: (transfer full): a `GIOStream`
1230 : : */
1231 : : GIOStream *
1232 : 1 : valent_bluez_muxer_channel_accept (ValentBluezMuxer *muxer,
1233 : : const char *uuid,
1234 : : GCancellable *cancellable,
1235 : : GError **error)
1236 : : {
1237 : 1 : g_autoptr (ChannelState) state = NULL;
1238 : 1 : GIOStream *ret = NULL;
1239 : :
1240 [ - + ]: 1 : g_assert (VALENT_IS_BLUEZ_MUXER (muxer));
1241 [ + - ]: 1 : g_assert (g_uuid_string_is_valid (uuid));
1242 [ - + - - : 1 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
1243 [ + - - + ]: 1 : g_assert (error == NULL || *error == NULL);
1244 : :
1245 : : /* HACK: Loop every second and check for the channel
1246 : : */
1247 [ + - ]: 1 : while (!g_cancellable_set_error_if_cancelled (cancellable, error))
1248 : : {
1249 : 1 : state = channel_state_lookup (muxer, uuid, NULL);
1250 [ + - ]: 1 : if (state != NULL)
1251 : : {
1252 : 1 : uint16_t size_request;
1253 : :
1254 : 1 : g_mutex_lock (&state->mutex);
1255 : 1 : state->read_free += state->size;
1256 : 1 : size_request = state->size;
1257 : 1 : ret = g_object_ref (state->stream);
1258 : 1 : g_mutex_unlock (&state->mutex);
1259 : :
1260 : 1 : valent_object_lock (VALENT_OBJECT (muxer));
1261 [ - + ]: 1 : if (!send_read (muxer, uuid, size_request, cancellable, error))
1262 : : {
1263 [ # # ]: 0 : g_clear_object (&ret);
1264 : 0 : valent_bluez_muxer_close (muxer, NULL, NULL);
1265 : : }
1266 : 1 : valent_object_unlock (VALENT_OBJECT (muxer));
1267 : 1 : break;
1268 : : }
1269 : :
1270 : 0 : g_usleep (G_USEC_PER_SEC);
1271 : : }
1272 : :
1273 : 1 : return g_steal_pointer (&ret);
1274 : : }
1275 : :
1276 : : /**
1277 : : * valent_bluez_muxer_channel_close:
1278 : : * @muxer: a `ValentBluezMuxer`
1279 : : * @uuid: a channel UUID
1280 : : * @cancellable: (nullable): a `GCancellable`
1281 : : * @error: (nullable): a `GError`
1282 : : *
1283 : : * Close the stream for the channel with @uuid associated with the
1284 : : * condition for @condition.
1285 : : *
1286 : : * Returns: %TRUE, or %FALSE with @error set
1287 : : */
1288 : : gboolean
1289 : 10 : valent_bluez_muxer_channel_close (ValentBluezMuxer *muxer,
1290 : : const char *uuid,
1291 : : GCancellable *cancellable,
1292 : : GError **error)
1293 : : {
1294 : 10 : g_autoptr (ChannelState) state = NULL;
1295 : 10 : gboolean ret = TRUE;
1296 : :
1297 [ - + ]: 10 : g_assert (VALENT_IS_BLUEZ_MUXER (muxer));
1298 [ + - ]: 10 : g_assert (g_uuid_string_is_valid (uuid));
1299 [ - + - - : 10 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
1300 [ + + + - ]: 10 : g_assert (error == NULL || *error == NULL);
1301 : :
1302 : 10 : state = channel_state_lookup (muxer, uuid, error);
1303 [ + - ]: 10 : if (state == NULL)
1304 : : return FALSE;
1305 : :
1306 : 10 : valent_object_lock (VALENT_OBJECT (muxer));
1307 : 10 : ret = send_close_channel (muxer, uuid, cancellable, error);
1308 [ + + - + ]: 10 : if (error != NULL && *error != NULL)
1309 : 6 : error = NULL;
1310 : 10 : valent_object_unlock (VALENT_OBJECT (muxer));
1311 : :
1312 : 10 : g_mutex_lock (&state->mutex);
1313 [ + + ]: 10 : if ((state->condition & (G_IO_HUP | G_IO_ERR)) == 0)
1314 : : {
1315 : 3 : ret |= channel_state_close_unlocked (state, error);
1316 : : }
1317 : 10 : g_mutex_unlock (&state->mutex);
1318 : :
1319 : 10 : return ret;
1320 : : }
1321 : :
1322 : : /**
1323 : : * valent_bluez_muxer_channel_flush:
1324 : : * @muxer: a `ValentBluezMuxer`
1325 : : * @uuid: a channel UUID
1326 : : * @cancellable: (nullable): a `GCancellable`
1327 : : * @error: (nullable): a `GError`
1328 : : *
1329 : : * Flush the streams by notifying any pollable sources or waiting threads
1330 : : * of a condition change.
1331 : : *
1332 : : * Returns: %TRUE, or %FALSE with @error set
1333 : : */
1334 : : gboolean
1335 : 5 : valent_bluez_muxer_channel_flush (ValentBluezMuxer *muxer,
1336 : : const char *uuid,
1337 : : GCancellable *cancellable,
1338 : : GError **error)
1339 : : {
1340 : 5 : g_autoptr (ChannelState) state = NULL;
1341 : 5 : gboolean ret;
1342 : :
1343 [ - + ]: 5 : g_assert (VALENT_IS_BLUEZ_MUXER (muxer));
1344 [ + - ]: 5 : g_assert (g_uuid_string_is_valid (uuid));
1345 [ - + - - : 5 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
1346 [ + + + - ]: 5 : g_assert (error == NULL || *error == NULL);
1347 : :
1348 : 5 : state = channel_state_lookup (muxer, uuid, error);
1349 [ + - ]: 5 : if (state == NULL)
1350 : : return FALSE;
1351 : :
1352 : 5 : g_mutex_lock (&state->mutex);
1353 : 5 : ret = channel_state_flush_unlocked (state, error);
1354 : 5 : g_mutex_unlock (&state->mutex);
1355 : :
1356 : 5 : return ret;
1357 : : }
1358 : :
1359 : : /**
1360 : : * valent_bluez_muxer_channel_open:
1361 : : * @muxer: a `ValentBluezMuxer`
1362 : : * @uuid: a channel UUID
1363 : : * @cancellable: (nullable): a `GCancellable`
1364 : : * @error: (nullable): a `GError`
1365 : : *
1366 : : * Attempt to open a muxed channel for @uuid.
1367 : : *
1368 : : * Returns: (transfer full): a `GIOStream`
1369 : : */
1370 : : GIOStream *
1371 : 1 : valent_bluez_muxer_channel_open (ValentBluezMuxer *muxer,
1372 : : const char *uuid,
1373 : : GCancellable *cancellable,
1374 : : GError **error)
1375 : : {
1376 : 1 : g_autoptr (ChannelState) state = NULL;
1377 : 1 : uint16_t size_request;
1378 : 1 : GIOStream *ret = NULL;
1379 : :
1380 [ - + ]: 1 : g_assert (VALENT_IS_BLUEZ_MUXER (muxer));
1381 [ + - ]: 1 : g_assert (g_uuid_string_is_valid (uuid));
1382 [ - + - - : 1 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
1383 [ + - + - ]: 1 : g_assert (error == NULL || *error == NULL);
1384 : :
1385 : 1 : valent_object_lock (VALENT_OBJECT (muxer));
1386 [ - + ]: 1 : if (g_hash_table_contains (muxer->states, uuid))
1387 : : {
1388 : 0 : g_set_error (error,
1389 : : G_IO_ERROR,
1390 : : G_IO_ERROR_ADDRESS_IN_USE,
1391 : : "Channel already open (%s)",
1392 : : uuid);
1393 : : }
1394 : : else
1395 : : {
1396 : 1 : state = channel_state_new (muxer, uuid);
1397 : 1 : g_hash_table_replace (muxer->states,
1398 : 1 : state->uuid,
1399 : : g_atomic_rc_box_acquire (state));
1400 : :
1401 : 1 : g_mutex_lock (&state->mutex);
1402 : 1 : state->read_free += state->size;
1403 : 1 : size_request = state->size;
1404 : 1 : ret = g_object_ref (state->stream);
1405 : 1 : g_mutex_unlock (&state->mutex);
1406 : :
1407 [ + - - + ]: 2 : if (!send_open_channel (muxer, uuid, cancellable, error) ||
1408 : 1 : !send_read (muxer, uuid, size_request, cancellable, error))
1409 : : {
1410 [ # # ]: 0 : g_clear_object (&ret);
1411 : 0 : valent_bluez_muxer_close (muxer, NULL, NULL);
1412 : : }
1413 : : }
1414 : 1 : valent_object_unlock (VALENT_OBJECT (muxer));
1415 : :
1416 : 1 : return g_steal_pointer (&ret);
1417 : : }
1418 : :
1419 : : /**
1420 : : * valent_bluez_muxer_channel_read:
1421 : : * @muxer: a `ValentBluezMuxer`
1422 : : * @uuid: a channel UUID
1423 : : * @buffer: a buffer to read data into
1424 : : * @count: the number of bytes that will be read from the stream
1425 : : * @blocking: whether to do blocking or non-blocking I/O
1426 : : * @cancellable: (nullable): a `GCancellable`
1427 : : * @error: (nullable): a `GError`
1428 : : *
1429 : : * Tries to read count bytes from the channel @uuid into the buffer starting at
1430 : : * @buffer.
1431 : : *
1432 : : * If @blocking is %TRUE, this function will block during the operation,
1433 : : * otherwise it may return `G_IO_ERROR_WOULD_BLOCK`.
1434 : : *
1435 : : * This is used by `ValentMuxInputStream` to implement g_input_stream_read().
1436 : : *
1437 : : * Returns: number of bytes read, or -1 on error, or 0 on end of file
1438 : : */
1439 : : gssize
1440 : 3888 : valent_bluez_muxer_channel_read (ValentBluezMuxer *muxer,
1441 : : const char *uuid,
1442 : : void *buffer,
1443 : : size_t count,
1444 : : gboolean blocking,
1445 : : GCancellable *cancellable,
1446 : : GError **error)
1447 : : {
1448 : 3888 : g_autoptr (ChannelState) state = NULL;
1449 : 3888 : gssize read;
1450 : 3888 : uint16_t size_request = 0;
1451 : :
1452 [ - + ]: 3888 : g_assert (VALENT_IS_BLUEZ_MUXER (muxer));
1453 [ + - ]: 3888 : g_assert (g_uuid_string_is_valid (uuid));
1454 [ + + + - : 3888 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- + - - ]
1455 [ + - + - ]: 3888 : g_assert (error == NULL || *error == NULL);
1456 : :
1457 : 3888 : state = channel_state_lookup (muxer, uuid, error);
1458 [ - + ]: 3888 : if (state == NULL)
1459 : : return -1;
1460 : :
1461 : 3888 : g_mutex_lock (&state->mutex);
1462 [ + + ]: 3888 : if ((state->condition & (G_IO_HUP | G_IO_ERR)) != 0)
1463 : : {
1464 : 1 : read = channel_state_read_unlocked (state, buffer, count, error);
1465 : 1 : g_mutex_unlock (&state->mutex);
1466 : 1 : return read;
1467 : : }
1468 : :
1469 [ + + ]: 3887 : if (blocking)
1470 : : {
1471 [ + + ]: 3884 : while ((state->condition & (G_IO_IN | G_IO_HUP | G_IO_ERR)) == 0)
1472 : : {
1473 [ - + ]: 6 : if (g_cancellable_set_error_if_cancelled (cancellable, error))
1474 : : {
1475 : 0 : g_mutex_unlock (&state->mutex);
1476 : 0 : return -1;
1477 : : }
1478 : :
1479 : 6 : g_cond_wait (&state->cond, &state->mutex);
1480 : : }
1481 : : }
1482 [ + + ]: 9 : else if ((state->condition & G_IO_IN) == 0)
1483 : : {
1484 : 6 : g_set_error_literal (error,
1485 : : G_IO_ERROR,
1486 : : G_IO_ERROR_WOULD_BLOCK,
1487 : : g_strerror (EAGAIN));
1488 : 6 : g_mutex_unlock (&state->mutex);
1489 : 6 : return -1;
1490 : : }
1491 : :
1492 : 3881 : read = channel_state_read_unlocked (state, buffer, count, error);
1493 [ - + ]: 3881 : if (read < 1)
1494 : : {
1495 : 0 : g_mutex_unlock (&state->mutex);
1496 : 0 : return read;
1497 : : }
1498 : :
1499 : 3881 : size_request = (state->size - state->count) - state->read_free;
1500 [ - + ]: 3881 : if ((double)size_request < state->size * 0.5)
1501 : : size_request = 0;
1502 : : else
1503 : 0 : state->read_free += size_request;
1504 : 3881 : g_mutex_unlock (&state->mutex);
1505 : :
1506 : : /* Any failure sending a multiplex message closes the connection,
1507 : : * but the buffer may be emptied after G_IO_HUP
1508 : : */
1509 [ + - ]: 3881 : if (size_request > 0)
1510 : : {
1511 : 0 : valent_object_lock (VALENT_OBJECT (muxer));
1512 [ # # ]: 0 : if (!send_read (muxer, uuid, size_request, cancellable, error))
1513 : 0 : valent_bluez_muxer_close (muxer, NULL, NULL);
1514 : 0 : valent_object_unlock (VALENT_OBJECT (muxer));
1515 : : }
1516 : :
1517 : : return read;
1518 : : }
1519 : :
1520 : : /**
1521 : : * valent_bluez_muxer_channel_write:
1522 : : * @muxer: a `ValentBluezMuxer`
1523 : : * @uuid: a channel UUID
1524 : : * @buffer: data to write
1525 : : * @count: size of the write
1526 : : * @blocking: whether to do blocking or non-blocking I/O
1527 : : * @cancellable: (nullable): a `GCancellable`
1528 : : * @error: (nullable): a `GError`
1529 : : *
1530 : : * Tries to write @count bytes from @buffer into the stream for @uuid.
1531 : : *
1532 : : * If @blocking is %TRUE, this function will block during the operation,
1533 : : * otherwise it may return `G_IO_ERROR_WOULD_BLOCK`.
1534 : : *
1535 : : * This is used by `ValentMuxOutputStream` to implement g_output_stream_write().
1536 : : *
1537 : : * Returns: number of bytes written, or -1 with @error set
1538 : : */
1539 : : gssize
1540 : 12 : valent_bluez_muxer_channel_write (ValentBluezMuxer *muxer,
1541 : : const char *uuid,
1542 : : const void *buffer,
1543 : : size_t count,
1544 : : gboolean blocking,
1545 : : GCancellable *cancellable,
1546 : : GError **error)
1547 : : {
1548 : 12 : g_autoptr (ChannelState) state = NULL;
1549 : 12 : gssize written;
1550 : :
1551 [ - + ]: 12 : g_assert (VALENT_IS_BLUEZ_MUXER (muxer));
1552 [ + - ]: 12 : g_assert (g_uuid_string_is_valid (uuid));
1553 [ - + - - : 12 : g_assert (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
- - - - ]
1554 [ + - + - ]: 12 : g_assert (error == NULL || *error == NULL);
1555 : :
1556 : 12 : state = channel_state_lookup (muxer, uuid, error);
1557 [ - + ]: 12 : if (state == NULL)
1558 : : return -1;
1559 : :
1560 : 12 : g_mutex_lock (&state->mutex);
1561 [ - + ]: 12 : if ((state->condition & (G_IO_HUP | G_IO_ERR)) != 0)
1562 : : {
1563 : 0 : g_set_error_literal (error,
1564 : : G_IO_ERROR,
1565 : : G_IO_ERROR_CLOSED,
1566 : : g_strerror (EPIPE));
1567 : 0 : g_mutex_unlock (&state->mutex);
1568 : 0 : return -1;
1569 : : }
1570 : :
1571 [ - + ]: 12 : if (blocking)
1572 : : {
1573 [ # # ]: 0 : while ((state->condition & (G_IO_OUT | G_IO_HUP | G_IO_ERR)) == 0)
1574 : : {
1575 [ # # ]: 0 : if (g_cancellable_set_error_if_cancelled (cancellable, error))
1576 : : {
1577 : 0 : g_mutex_unlock (&state->mutex);
1578 : 0 : return -1;
1579 : : }
1580 : :
1581 : 0 : g_cond_wait (&state->cond, &state->mutex);
1582 : : }
1583 : :
1584 [ # # ]: 0 : if ((state->condition & (G_IO_HUP | G_IO_ERR)) != 0)
1585 : : {
1586 : 0 : g_set_error_literal (error,
1587 : : G_IO_ERROR,
1588 : : G_IO_ERROR_CLOSED,
1589 : : g_strerror (EPIPE));
1590 : 0 : g_mutex_unlock (&state->mutex);
1591 : 0 : return -1;
1592 : : }
1593 : : }
1594 [ + + ]: 12 : else if ((state->condition & G_IO_OUT) == 0)
1595 : : {
1596 : 5 : g_set_error_literal (error,
1597 : : G_IO_ERROR,
1598 : : G_IO_ERROR_WOULD_BLOCK,
1599 : : g_strerror (EAGAIN));
1600 : 5 : g_mutex_unlock (&state->mutex);
1601 : 5 : return -1;
1602 : : }
1603 : :
1604 : 7 : written = MIN (count, state->write_free);
1605 : 7 : state->write_free -= written;
1606 [ - + ]: 7 : if (state->write_free == 0)
1607 : 0 : state->condition &= ~G_IO_OUT;
1608 : 7 : g_mutex_unlock (&state->mutex);
1609 : :
1610 : : /* Any failure sending a multiplex message closes the connection
1611 : : */
1612 : 7 : valent_object_lock (VALENT_OBJECT (muxer));
1613 [ - + ]: 7 : if (!send_write (muxer, uuid, written, buffer, cancellable, error))
1614 : : {
1615 : 0 : valent_bluez_muxer_close (muxer, NULL, NULL);
1616 : 0 : written = -1;
1617 : : }
1618 : 7 : valent_object_unlock (VALENT_OBJECT (muxer));
1619 : :
1620 : 7 : return written;
1621 : : }
1622 : :
1623 : : static gboolean
1624 : 0 : broken_dispatch (GSource *source,
1625 : : GSourceFunc callback,
1626 : : gpointer user_data)
1627 : : {
1628 : 0 : return TRUE;
1629 : : }
1630 : :
1631 : : static GSourceFuncs broken_funcs =
1632 : : {
1633 : : .dispatch = broken_dispatch,
1634 : : };
1635 : :
1636 : : typedef struct
1637 : : {
1638 : : GSource source;
1639 : : ChannelState *state;
1640 : : GIOCondition condition;
1641 : : gpointer eventfd_tag;
1642 : : } ValentMuxerSource;
1643 : :
1644 : : static gboolean
1645 : 64 : muxer_stream_source_prepare (GSource *source,
1646 : : int *timeout)
1647 : : {
1648 : 64 : ValentMuxerSource *stream_source = (ValentMuxerSource *)source;
1649 : 64 : ChannelState *state = stream_source->state;
1650 : 64 : gboolean ret = FALSE;
1651 : :
1652 : 64 : g_mutex_lock (&state->mutex);
1653 : 64 : ret = (state->condition & stream_source->condition) != 0;
1654 : 64 : g_mutex_unlock (&state->mutex);
1655 : :
1656 [ + + ]: 64 : if (ret)
1657 : 7 : *timeout = 0;
1658 : :
1659 : 64 : return ret;
1660 : : }
1661 : :
1662 : : static gboolean
1663 : 57 : muxer_stream_source_check (GSource *source)
1664 : : {
1665 : 57 : ValentMuxerSource *stream_source = (ValentMuxerSource *)source;
1666 : 57 : ChannelState *state = stream_source->state;
1667 : 57 : gboolean ret = FALSE;
1668 : 57 : uint64_t buf;
1669 : :
1670 : 57 : g_mutex_lock (&state->mutex);
1671 : 57 : ret = (state->condition & stream_source->condition) != 0;
1672 [ + + ]: 57 : if (read (state->eventfd, &buf, sizeof (uint64_t)) == -1)
1673 : : {
1674 [ - + ]: 53 : if (errno != EAGAIN && errno != EWOULDBLOCK)
1675 : : {
1676 : 0 : g_critical ("%s(): %s", G_STRFUNC, g_strerror (errno));
1677 : 0 : ret = FALSE;
1678 : : }
1679 : : }
1680 : 57 : g_mutex_unlock (&state->mutex);
1681 : :
1682 : 57 : return ret;
1683 : : }
1684 : :
1685 : : static gboolean
1686 : 11 : muxer_stream_source_dispatch (GSource *source,
1687 : : GSourceFunc callback,
1688 : : gpointer user_data)
1689 : : {
1690 [ + - ]: 11 : if (callback != NULL)
1691 : 11 : return callback (user_data);
1692 : :
1693 : : return G_SOURCE_REMOVE;
1694 : : }
1695 : :
1696 : : static void
1697 : 11 : muxer_stream_source_finalize (GSource *source)
1698 : : {
1699 : 11 : ValentMuxerSource *stream_source = (ValentMuxerSource *)source;
1700 : :
1701 [ + - ]: 11 : g_clear_pointer (&stream_source->state, channel_state_unref);
1702 : 11 : }
1703 : :
1704 : : static gboolean
1705 : 11 : muxer_stream_source_closure_callback (gpointer data)
1706 : : {
1707 : 11 : GClosure *closure = (GClosure *)data;
1708 : 11 : GValue result_value = G_VALUE_INIT;
1709 : 11 : gboolean result;
1710 : :
1711 : 11 : g_value_init (&result_value, G_TYPE_BOOLEAN);
1712 : 11 : g_closure_invoke (closure, &result_value, 0, NULL, NULL);
1713 : 11 : result = g_value_get_boolean (&result_value);
1714 : 11 : g_value_unset (&result_value);
1715 : :
1716 : 11 : return result;
1717 : : }
1718 : :
1719 : : static GSourceFuncs muxer_stream_source_funcs =
1720 : : {
1721 : : .prepare = muxer_stream_source_prepare,
1722 : : .check = muxer_stream_source_check,
1723 : : .dispatch = muxer_stream_source_dispatch,
1724 : : .finalize = muxer_stream_source_finalize,
1725 : : .closure_callback = muxer_stream_source_closure_callback,
1726 : : NULL,
1727 : : };
1728 : :
1729 : : /**
1730 : : * valent_bluez_muxer_create_source:
1731 : : * @muxer: a `ValentBluezMuxer`
1732 : : * @uuid: a channel UUID
1733 : : * @condition: a `GIOCondition`
1734 : : *
1735 : : * Create a [type@GLib.Source].
1736 : : *
1737 : : * Returns: (transfer full) (nullable): a new `GSource`
1738 : : */
1739 : : GSource *
1740 : 11 : valent_bluez_muxer_create_source (ValentBluezMuxer *muxer,
1741 : : const char *uuid,
1742 : : GIOCondition condition)
1743 : : {
1744 : 11 : g_autoptr (ChannelState) state = NULL;
1745 : 11 : GSource *source = NULL;
1746 : 11 : ValentMuxerSource *stream_source;
1747 : :
1748 [ - + ]: 11 : g_assert (VALENT_IS_BLUEZ_MUXER (muxer));
1749 [ + - ]: 11 : g_assert (g_uuid_string_is_valid (uuid));
1750 : :
1751 : 11 : state = channel_state_lookup (muxer, uuid, NULL);
1752 [ - + ]: 11 : if (state == NULL)
1753 : 0 : return g_source_new (&broken_funcs, sizeof (GSource));
1754 : :
1755 : 11 : source = g_source_new (&muxer_stream_source_funcs, sizeof (ValentMuxerSource));
1756 : 11 : g_source_set_static_name (source, "ValentMuxerSource");
1757 : :
1758 : 11 : stream_source = (ValentMuxerSource *) source;
1759 : 11 : stream_source->state = g_atomic_rc_box_acquire (state);
1760 : 11 : stream_source->condition = condition;
1761 : 22 : stream_source->eventfd_tag = g_source_add_unix_fd (source,
1762 : 11 : state->eventfd,
1763 : : G_IO_IN);
1764 : :
1765 : 11 : return g_steal_pointer (&source);
1766 : : }
1767 : :
1768 : : /**
1769 : : * valent_bluez_muxer_condition_check:
1770 : : * @muxer: a `ValentBluezMuxer`
1771 : : * @uuid: a channel UUID
1772 : : * @condition: a `GIOCondition` mask to check
1773 : : *
1774 : : * Checks on the readiness of the channel for @uuid to perform operations. The
1775 : : * operations specified in @condition are checked for and masked against the
1776 : : * currently-satisfied conditions.
1777 : : *
1778 : : * Returns: the `GIOCondition` mask of the current state
1779 : : */
1780 : : GIOCondition
1781 : 0 : valent_bluez_muxer_condition_check (ValentBluezMuxer *muxer,
1782 : : const char *uuid,
1783 : : GIOCondition condition)
1784 : : {
1785 : 0 : g_autoptr (ChannelState) state = NULL;
1786 : 0 : GIOCondition ret = 0;
1787 : :
1788 [ # # ]: 0 : g_assert (VALENT_IS_BLUEZ_MUXER (muxer));
1789 [ # # ]: 0 : g_assert (g_uuid_string_is_valid (uuid));
1790 : :
1791 : 0 : state = channel_state_lookup (muxer, uuid, NULL);
1792 [ # # ]: 0 : if (state == NULL)
1793 : : return G_IO_ERR;
1794 : :
1795 : 0 : g_mutex_lock (&state->mutex);
1796 : 0 : ret = state->condition & condition;
1797 : 0 : g_mutex_unlock (&state->mutex);
1798 : :
1799 : 0 : return ret;
1800 : : }
1801 : :
|