update changelog
[profile/ivi/genivi/genivi-audio-manager.git] / domain-manager / main.c
1 /*
2  * Copyright (c) 2014, Intel Corporation
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions are
6  * met:
7  *
8  *  * Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright
11  *    notice, this list of conditions and the following disclaimer in the
12  *    documentation and/or other materials provided with the distribution.
13  *  * Neither the name of Intel Corporation nor the names of its contributors
14  *    may be used to endorse or promote products derived from this software
15  *    without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20  * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21  * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28  */
29
30 #include <stdio.h>
31 #include <stdbool.h>
32 #include <unistd.h>
33 #include <sys/types.h>
34
35 #include <murphy/common.h>
36 #include <murphy/common/dbus-libdbus.h>
37
38 #include "ini-parser.h"
39
40 /* from audiomanagertypes.h */
41
42 /* domain status */
43 #define DS_UNKNOWN        0
44 #define DS_CONTROLLED     1
45 #define DS_RUNDOWN        2
46 #define DS_DOWN           255
47
48 /* interrupt state */
49 #define IS_OFF            1
50 #define IS_INTERRUPTED    2
51
52 /* availability status */
53 #define AS_AVAILABLE      1
54 #define AS_UNAVAILABLE    2
55
56 /* availability reason */
57 #define AR_NEWMEDIA       1
58 #define AR_SAMEMEDIA      2
59 #define AR_NOMEDIA        3
60 #define AR_TEMPERATURE    4
61 #define AR_VOLTAGE        5
62 #define AR_ERRORMEDIA     6
63
64 /* mute state */
65 #define MS_MUTED          1
66 #define MS_UNMUTED        2
67
68 /* connection format */
69 #define CF_MONO           1
70 #define CF_STEREO         2
71 #define CF_AUTO           4
72
73 /* error codes */
74 #define E_OK              0
75 #define E_UNKNOWN         1
76 #define E_OUT_OF_RANGE    2
77 #define E_NOT_USED        3
78 #define E_DATABSE_ERROR   4
79 #define E_ALREADY_EXISTS  5
80 #define E_NO_CHANGE       6
81 #define E_NOT_POSSIBLE    7
82 #define E_NON_EXISTENT    8
83 #define E_ABORTED         9
84 #define E_WRONG_FORMAT    10
85
86 /* D-Bus names, paths and interfaces */
87 #define AUDIOMGR_DBUS_NAME            "org.genivi.audiomanager"
88 #define AUDIOMGR_DBUS_INTERFACE       "org.genivi.audiomanager"
89 #define AUDIOMGR_DBUS_PATH            "/org/genivi/audiomanager"
90
91 #define AUDIOMGR_DBUS_ROUTE_NAME      "RoutingInterface"
92 #define AUDIOMGR_DBUS_ROUTE_PATH      AUDIOMGR_DBUS_PATH "/routinginterface"
93
94 #define OWN_DBUS_NAME                 "test.audiomanager.domain"
95 #define OWN_DBUS_INTERFACE            "test.audiomanager.route"
96 #define OWN_DBUS_PATH                 "/"
97
98 /* audiomanager router methods */
99
100 #define AUDIOMGR_REGISTER_DOMAIN      "registerDomain"
101 #define AUDIOMGR_DOMAIN_COMPLETE      "hookDomainRegistrationComplete"
102 #define AUDIOMGR_DEREGISTER_DOMAIN    "deregisterDomain"
103
104 #define AUDIOMGR_REGISTER_SOURCE      "registerSource"
105 #define AUDIOMGR_DEREGISTER_SOURCE    "deregisterSource"
106
107 #define AUDIOMGR_REGISTER_SINK        "registerSink"
108 #define AUDIOMGR_DEREGISTER_SINK      "deregisterSink"
109
110 #define AUDIOMGR_REGISTER_GATEWAY     "registerGateway"
111 #define AUDIOMGR_DEREGISTER_GATEWAY   "deregisterGateway"
112
113 #define AUDIOMGR_CONNECT              "asyncConnect"
114 #define AUDIOMGR_CONNECT_ACK          "ackConnect"
115
116 #define AUDIOMGR_DISCONNECT           "asyncDisconnect"
117 #define AUDIOMGR_DISCONNECT_ACK       "ackDisconnect"
118
119 #define AUDIOMGR_PEEK_DOMAIN          "peekDomain"
120 #define AUDIOMGR_PEEK_SINK            "peekSink"
121 #define AUDIOMGR_PEEK_SOURCE          "peekSource"
122
123 #define AUDIOMGR_SETSINKVOL_ACK       "ackSetSinkVolume"
124 #define AUDIOMGR_SETSRCVOL_ACK        "ackSetSourceVolume"
125 #define AUDIOMGR_SINKVOLTICK_ACK      "ackSinkVolumeTick"
126 #define AUDIOMGR_SRCVOLTICK_ACK       "ackSourceVolumeTick"
127 #define AUDIOMGR_SETSINKPROP_ACK      "ackSetSinkSoundProperty"
128
129 /* config file sections */
130
131 #define CONFIG_SINK                   "sink"
132 #define CONFIG_SOURCE                 "source"
133 #define CONFIG_DOMAIN                 "domain"
134 #define CONFIG_GATEWAY                "gateway"
135 #define CONFIG_CONFIG                 "config"
136
137 /* config file keys */
138
139 #define CONFIG_DBUS_BUS               "dbus_bus"
140 #define CONFIG_DBUS_ADDRESS           "dbus_address"
141 #define CONFIG_DBUS_PATH              "dbus_path"
142 #define CONFIG_DBUS_INTERFACE         "dbus_interface"
143 #define CONFIG_NAME                   "name"
144 #define CONFIG_BUS_NAME               "bus_name"
145 #define CONFIG_NODE_NAME              "node_name"
146 #define CONFIG_EARLY                  "early"
147 #define CONFIG_NAME                   "name"
148 #define CONFIG_DOMAIN_NAME            "domain_name"
149 #define CONFIG_CONNECTION_SCRIPT      "connection_script"
150 #define CONFIG_DISCONNECTION_SCRIPT   "disconnection_script"
151 #define CONFIG_CLASS                  "class"
152 #define CONFIG_VOLUME                 "volume"
153 #define CONFIG_MAIN_VOLUME            "main_volume"
154 #define CONFIG_VISIBLE                "visible"
155 #define CONFIG_AVAILABILITY_STATUS    "availability_status"
156 #define CONFIG_AVAILABILITY_REASON    "availability_reason"
157 #define CONFIG_MUTE                   "mute"
158 #define CONFIG_INTERRUPT              "interrupt"
159
160 #define CONFIG_SINK_DOMAIN            "sink_domain"
161 #define CONFIG_SOURCE_DOMAIN          "source_domain"
162
163 typedef struct {
164     mrp_mainloop_t *ml;
165     mrp_dbus_t *dbus;
166     mrp_sighandler_t *sighandler;
167
168     char *dbus_bus;
169     char *dbus_address;
170
171     int pending_dbus_calls;
172
173     mrp_list_hook_t domains;
174 } ctx_t;
175
176 typedef struct {
177     /* configuration file */
178     char *name;
179     char *bus_name;
180     char *node_name;
181     bool early;
182
183     char *dbus_path;
184     char *dbus_interface;
185     char *connection_script;
186     char *disconnection_script;
187
188     /* audio manager */
189     uint16_t domain_id;
190     uint16_t state;
191
192     mrp_list_hook_t sources;
193     mrp_list_hook_t sinks;
194     mrp_list_hook_t gateways;
195     mrp_list_hook_t connections;
196
197     mrp_list_hook_t hook;
198     ctx_t *ctx;
199 } domain_t;
200
201 typedef struct {
202     uint16_t id;
203     char *sink;
204     char *source;
205
206     mrp_list_hook_t hook;
207 } connection_t;
208
209 typedef struct {
210     uint16_t id;
211     uint16_t state;
212
213     char *name;
214
215     char *sink_name;
216     char *source_name;
217     char *sink_domain_name;
218     char *source_domain_name;
219
220     /* these are found only after registration */
221     uint16_t sink_id;
222     uint16_t source_id;
223     uint16_t domain_sink_id;
224     uint16_t domain_source_id;
225
226     mrp_list_hook_t hook;
227     domain_t *d;
228 } gateway_t;
229
230 typedef struct {
231     uint16_t id;
232     uint16_t state;
233
234     char *name;
235     int16_t volume;
236     int16_t main_volume;
237     int16_t mute;
238
239     bool visible;
240     uint16_t klass;
241
242     int16_t availability_reason;
243     int16_t availability_status;
244
245     mrp_list_hook_t hook;
246     domain_t *d;
247 } sink_t;
248
249 typedef struct {
250     uint16_t id;
251     uint16_t state;
252
253     char *name;
254     int16_t volume;
255     int16_t main_volume;
256     uint16_t interrupt;
257
258     bool visible;
259     uint16_t klass;
260
261     int16_t availability_reason;
262     int16_t availability_status;
263
264     mrp_list_hook_t hook;
265     domain_t *d;
266 } source_t;
267
268
269 void destroy_sink(sink_t *s)
270 {
271     if (!s)
272         return;
273
274     mrp_list_delete(&s->hook);
275
276     mrp_free(s->name);
277     mrp_free(s);
278 }
279
280 void destroy_source(source_t *s)
281 {
282     if (!s)
283         return;
284
285     mrp_list_delete(&s->hook);
286
287     mrp_free(s->name);
288     mrp_free(s);
289 }
290
291 void destroy_connection(connection_t *c)
292 {
293     if (!c)
294         return;
295
296     mrp_list_delete(&c->hook);
297
298     mrp_free(c->sink);
299     mrp_free(c->source);
300     mrp_free(c);
301 }
302
303 void destroy_gateway(gateway_t *gw)
304 {
305     if (!gw)
306         return;
307
308     mrp_free(gw->name);
309     mrp_free(gw->sink_name);
310     mrp_free(gw->source_name);
311     mrp_free(gw->sink_domain_name);
312     mrp_free(gw->source_domain_name);
313
314     mrp_free(gw);
315 }
316
317 void destroy_domain(domain_t *d)
318 {
319     mrp_list_hook_t *sp, *sn;
320
321     if (!d)
322         return;
323
324     mrp_list_delete(&d->hook);
325
326     mrp_list_foreach(&d->sinks, sp, sn) {
327         sink_t *s = mrp_list_entry(sp, typeof(*s), hook);
328         destroy_sink(s);
329     }
330
331     mrp_list_foreach(&d->sources, sp, sn) {
332         source_t *s = mrp_list_entry(sp, typeof(*s), hook);
333         destroy_source(s);
334     }
335
336     mrp_list_foreach(&d->gateways, sp, sn) {
337         gateway_t *gw = mrp_list_entry(sp, typeof(*gw), hook);
338         destroy_gateway(gw);
339     }
340
341     mrp_list_foreach(&d->connections, sp, sn) {
342         connection_t *c = mrp_list_entry(sp, typeof(*c), hook);
343         destroy_connection(c);
344     }
345
346     mrp_free(d->connection_script);
347     mrp_free(d->disconnection_script);
348     mrp_free(d->name);
349     mrp_free(d->node_name);
350     mrp_free(d->bus_name);
351     mrp_free(d->dbus_path);
352     mrp_free(d->dbus_interface);
353     mrp_free(d);
354 }
355
356 static int method_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg, void *data)
357 {
358     const char *member = mrp_dbus_msg_member(msg);
359     const char *iface = mrp_dbus_msg_interface(msg);
360     const char *path = mrp_dbus_msg_path(msg);
361
362     uint16_t error_code = E_NOT_POSSIBLE;
363     mrp_dbus_msg_t *reply;
364
365     ctx_t *ctx = data;
366
367     printf("Method callback called -- member: '%s', path: '%s',"
368             " interface: '%s'\n", member, path, iface);
369
370     if (strcmp(member, AUDIOMGR_CONNECT) == 0) {
371         mrp_dbus_msg_t *ack;
372
373         char *sink_name = NULL;
374         char *source_name = NULL;
375
376         uint16_t handle;
377         uint16_t connection;
378         uint16_t source;
379         uint16_t sink;
380         int32_t format;
381
382         domain_t *connection_domain = NULL;
383         sink_t *domain_sink = NULL;
384         source_t *domain_source = NULL;
385
386         mrp_list_hook_t *sp, *sn, *kp, *kn;
387
388         mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT16, &handle);
389         mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT16, &connection);
390         mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT16, &source);
391         mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT16, &sink);
392         mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_INT32, &format);
393
394         error_code = E_OK;
395
396         mrp_dbus_reply(dbus, msg, MRP_DBUS_TYPE_INT16, &error_code,
397                 MRP_DBUS_TYPE_INVALID);
398
399         /* find the gateway */
400
401         printf("connect! h: %u, conn: %u, source: %u, sink: %u, format: %i\n",
402                 handle, connection, source, sink, format);
403
404         mrp_list_foreach(&ctx->domains, kp, kn) {
405             domain_t *d;
406             d = mrp_list_entry(kp, typeof(*d), hook);
407
408             if (strcmp(iface, d->dbus_interface) == 0 &&
409                     strcmp(path, d->dbus_path) == 0) {
410                 connection_domain = d;
411                 break;
412             }
413         }
414
415         mrp_list_foreach(&ctx->domains, kp, kn) {
416             domain_t *d;
417             d = mrp_list_entry(kp, typeof(*d), hook);
418             mrp_list_foreach(&d->sinks, sp, sn) {
419                 sink_t *s;
420                 s = mrp_list_entry(sp, typeof(*s), hook);
421                 if (s->id == sink) {
422                     sink_name = s->name;
423                     domain_sink = s;
424                 }
425             }
426             mrp_list_foreach(&d->sources, sp, sn) {
427                 source_t *s;
428                 s = mrp_list_entry(sp, typeof(*s), hook);
429                 if (s->id == source) {
430                     source_name = s->name;
431                     domain_source = s;
432                 }
433             }
434         }
435
436         printf("sink: %s, source: %s\n",
437                 sink_name ? sink_name : "(NULL)",
438                 source_name ? source_name : "(NULL)");
439
440         if (!sink_name || !source_name) {
441             error_code = E_NOT_POSSIBLE;
442         }
443         else {
444             /* create connection struct for book keeping */
445
446             connection_t *c = mrp_allocz(sizeof(connection_t));
447
448             c->id = connection;
449             c->sink = mrp_strdup(sink_name);
450             c->source = mrp_strdup(source_name);
451             mrp_list_init(&c->hook);
452
453             mrp_list_append(&connection_domain->connections, &c->hook);
454
455             if (connection_domain->connection_script) {
456                 char buf[256];
457                 int ret;
458
459                 /* FIXME: sanity check of the string */
460
461                 ret = snprintf(buf, 256, "%s %s %s %i %i %i %i %u",
462                         connection_domain->connection_script,
463                         source_name, sink_name,
464                         domain_source->volume, domain_source->main_volume,
465                         domain_sink->volume, domain_sink->main_volume,
466                         c->id);
467
468                 if (ret > 0 && ret != 256) {
469                     printf("calling: '%s'\n", buf);
470                     system(buf);
471                 }
472             }
473         }
474
475         /* send back the ack after processing */
476
477         ack = mrp_dbus_msg_method_call(dbus, AUDIOMGR_DBUS_NAME,
478                 AUDIOMGR_DBUS_ROUTE_PATH, AUDIOMGR_DBUS_INTERFACE,
479                 AUDIOMGR_CONNECT_ACK);
480
481         mrp_dbus_msg_append_basic(ack, MRP_DBUS_TYPE_UINT16, &handle);
482         mrp_dbus_msg_append_basic(ack, MRP_DBUS_TYPE_UINT16, &connection);
483         mrp_dbus_msg_append_basic(ack, MRP_DBUS_TYPE_UINT16, &error_code);
484
485         mrp_dbus_send_msg(dbus, ack);
486     }
487     else if (strcmp(member, AUDIOMGR_DISCONNECT) == 0) {
488         mrp_dbus_msg_t *ack;
489
490         uint16_t handle;
491         uint16_t connection;
492
493         mrp_list_hook_t *sp, *sn, *kp, *kn;
494
495         mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT16, &handle);
496         mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT16, &connection);
497
498         error_code = E_OK;
499
500         mrp_dbus_reply(dbus, msg, MRP_DBUS_TYPE_INT16, &error_code,
501                 MRP_DBUS_TYPE_INVALID);
502
503         /* TODO: process command here */
504
505         mrp_list_foreach(&ctx->domains, sp, sn) {
506             domain_t *d;
507             d = mrp_list_entry(sp, typeof(*d), hook);
508             mrp_list_foreach(&d->connections, kp, kn) {
509                 connection_t *c = mrp_list_entry(kp, typeof(*c), hook);
510                 if (c->id == connection) {
511                     if (d->disconnection_script) {
512                         char buf[256];
513                         int ret;
514
515                         /* FIXME: sanity check of the string */
516
517                         ret = snprintf(buf, 256, "%s %s %s %u",
518                                 d->disconnection_script,
519                                 c->source, c->sink, c->id);
520
521                         if (ret > 0 && ret != 256) {
522                             printf("calling: '%s'\n", buf);
523                             system(buf);
524                         }
525                     }
526
527                     destroy_connection(c);
528                 }
529             }
530         }
531
532         /* send back the ack after processing */
533
534         ack = mrp_dbus_msg_method_call(dbus, AUDIOMGR_DBUS_NAME,
535                 AUDIOMGR_DBUS_ROUTE_PATH, AUDIOMGR_DBUS_INTERFACE,
536                 AUDIOMGR_DISCONNECT_ACK);
537
538         mrp_dbus_msg_append_basic(ack, MRP_DBUS_TYPE_UINT16, &handle);
539         mrp_dbus_msg_append_basic(ack, MRP_DBUS_TYPE_UINT16, &connection);
540         mrp_dbus_msg_append_basic(ack, MRP_DBUS_TYPE_UINT16, &error_code);
541
542         mrp_dbus_send_msg(dbus, ack);
543     }
544
545     return TRUE;
546 }
547
548 void register_gateway_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *reply,
549         void *user_data)
550 {
551     gateway_t *gw = user_data;
552
553     if (mrp_dbus_msg_is_error(reply)) {
554         printf("ERROR register gateway cb\n");
555         return;
556     }
557
558     mrp_dbus_msg_read_basic(reply, DBUS_TYPE_UINT16, &gw->id);
559     mrp_dbus_msg_read_basic(reply, DBUS_TYPE_UINT16, &gw->state);
560
561     printf("register gateway cb: id %u, state %u\n",
562             gw->id, gw->state);
563 }
564
565 static bool register_gateway(gateway_t *gw, uint16_t id, char *name,
566         uint16_t sink_id, uint16_t source_id, uint16_t domain_sink_id,
567         uint16_t domain_source_id, uint16_t control_domain_id)
568 {
569     int16_t i;
570     uint16_t error = E_OK;
571
572     mrp_dbus_msg_t *msg = mrp_dbus_msg_method_call(gw->d->ctx->dbus,
573             AUDIOMGR_DBUS_NAME, AUDIOMGR_DBUS_ROUTE_PATH,
574             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_REGISTER_GATEWAY);
575
576     /* Source domain has to be the domain from which the stream is coming:
577      * the routing algorithm builds a tree coming from source domain to all
578      * other domains. */
579
580     printf("register gateway: %u, %s, %u, %u, %u, %u, %u\n",
581         id, name, sink_id, source_id, domain_sink_id, domain_source_id,
582         gw->d->domain_id);
583
584     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_STRUCT, NULL);
585
586     /* qsqqqqq */
587     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &id);
588     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_STRING, name);
589     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &sink_id);
590     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &source_id);
591     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &domain_sink_id);
592     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &domain_source_id);
593     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &gw->d->domain_id);
594
595     /* source connection formats ai */
596
597     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_ARRAY, "i");
598
599     {
600         int16_t one = 1;
601         mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &one);
602     }
603
604     mrp_dbus_msg_close_container(msg);
605
606     /* sink connection formats ai */
607
608     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_ARRAY, "i");
609
610     {
611         int16_t one = 1;
612         mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &one);
613     }
614
615     mrp_dbus_msg_close_container(msg);
616
617     /* conversion matrix ab*/
618
619     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_ARRAY, "b");
620
621     for (i = 1; i < 2; i++) {
622         uint32_t b = TRUE;
623         mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_BOOLEAN, &b);
624     }
625
626     mrp_dbus_msg_close_container(msg);
627
628     mrp_dbus_msg_close_container(msg);
629
630     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &id);
631     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &error);
632
633     mrp_dbus_send(gw->d->ctx->dbus, AUDIOMGR_DBUS_NAME,
634             AUDIOMGR_DBUS_ROUTE_PATH,
635             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_REGISTER_GATEWAY, -1,
636             register_gateway_cb, gw, msg);
637
638     mrp_dbus_msg_unref(msg);
639
640     gw->d->ctx->pending_dbus_calls++;
641
642     return TRUE;
643 }
644
645 static bool unregister_gateway(ctx_t *ctx, uint16_t id)
646 {
647     uint16_t error = E_OK;
648
649     mrp_dbus_msg_t *msg = mrp_dbus_msg_method_call(ctx->dbus,
650             AUDIOMGR_DBUS_NAME, AUDIOMGR_DBUS_ROUTE_PATH,
651             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_DEREGISTER_GATEWAY);
652
653     printf("unregistering gateway %u\n", id);
654
655     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &id);
656     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &error);
657
658     mrp_dbus_send_msg(ctx->dbus, msg);
659
660     mrp_dbus_msg_unref(msg);
661
662     return TRUE;
663 }
664
665 static bool unregister_sink(ctx_t *ctx, int16_t id)
666 {
667     uint16_t error = E_OK;
668
669     mrp_dbus_msg_t *msg = mrp_dbus_msg_method_call(ctx->dbus,
670             AUDIOMGR_DBUS_NAME, AUDIOMGR_DBUS_ROUTE_PATH,
671             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_DEREGISTER_SINK);
672
673     printf("unregistering sink %i\n", id);
674
675     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT16, &id);
676     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &error);
677
678     mrp_dbus_send_msg(ctx->dbus, msg);
679
680     mrp_dbus_msg_unref(msg);
681
682     return TRUE;
683 }
684
685 static bool unregister_source(ctx_t *ctx, int16_t id)
686 {
687     uint16_t error = E_OK;
688
689     mrp_dbus_msg_t *msg = mrp_dbus_msg_method_call(ctx->dbus,
690             AUDIOMGR_DBUS_NAME, AUDIOMGR_DBUS_ROUTE_PATH,
691             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_DEREGISTER_SOURCE);
692
693     printf("unregistering source %i\n", id);
694
695     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT16, &id);
696     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &error);
697
698     mrp_dbus_send_msg(ctx->dbus, msg);
699
700     mrp_dbus_msg_unref(msg);
701
702     return TRUE;
703 }
704
705 static bool register_gateways(ctx_t *ctx)
706 {
707     mrp_list_hook_t *sp, *sn, *kp, *kn;
708
709     mrp_list_foreach(&ctx->domains, kp, kn) {
710         domain_t *d;
711         d = mrp_list_entry(kp, typeof(*d), hook);
712
713         mrp_list_foreach(&d->gateways, sp, sn) {
714             gateway_t *gw;
715             gw = mrp_list_entry(sp, typeof(*gw), hook);
716
717             register_gateway(gw, 0, gw->name, gw->sink_id, gw->source_id,
718                     gw->domain_sink_id, gw->domain_source_id, gw->d->domain_id);
719         }
720     }
721
722     return TRUE;
723 }
724
725 static void peek_sink_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *reply,
726         void *user_data)
727 {
728     gateway_t *gw = user_data;
729
730     if (mrp_dbus_msg_is_error(reply)) {
731         printf("ERROR peek_sink cb\n");
732         return;
733     }
734
735     mrp_dbus_msg_read_basic(reply, DBUS_TYPE_UINT16, &gw->sink_id);
736
737     printf("peek_sink cb got id %d\n", gw->sink_id);
738
739     gw->d->ctx->pending_dbus_calls--;
740
741     if (gw->d->ctx->pending_dbus_calls > 0)
742         return;
743
744     /* start creating gateways now when the sinks and sources have been
745      * registered */
746
747     register_gateways(gw->d->ctx);
748 }
749
750 static void peek_source_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *reply,
751         void *user_data)
752 {
753     gateway_t *gw = user_data;
754
755     if (mrp_dbus_msg_is_error(reply)) {
756         printf("ERROR peek_sink cb\n");
757         return;
758     }
759
760     mrp_dbus_msg_read_basic(reply, DBUS_TYPE_UINT16, &gw->source_id);
761
762     gw->d->ctx->pending_dbus_calls--;
763
764     if (gw->d->ctx->pending_dbus_calls > 0)
765         return;
766
767     /* start creating gateways now when the sinks and sources have been
768      * registered */
769
770     register_gateways(gw->d->ctx);
771 }
772
773 static void peek_sink_domain_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *reply,
774         void *user_data)
775 {
776     gateway_t *gw = user_data;
777
778     if (mrp_dbus_msg_is_error(reply)) {
779         printf("ERROR peek_sink cb\n");
780         return;
781     }
782
783     mrp_dbus_msg_read_basic(reply, DBUS_TYPE_UINT16, &gw->domain_sink_id);
784
785     gw->d->ctx->pending_dbus_calls--;
786
787     if (gw->d->ctx->pending_dbus_calls > 0)
788         return;
789
790     /* start creating gateways now when the sinks and sources have been
791      * registered */
792
793     register_gateways(gw->d->ctx);
794 }
795
796 static void peek_source_domain_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *reply,
797         void *user_data)
798 {
799     gateway_t *gw = user_data;
800
801     if (mrp_dbus_msg_is_error(reply)) {
802         printf("ERROR peek_sink cb\n");
803         return;
804     }
805
806     mrp_dbus_msg_read_basic(reply, DBUS_TYPE_UINT16, &gw->domain_source_id);
807
808     gw->d->ctx->pending_dbus_calls--;
809
810     if (gw->d->ctx->pending_dbus_calls > 0)
811         return;
812
813     /* start creating gateways now when the sinks and sources have been
814      * registered */
815
816     register_gateways(gw->d->ctx);
817 }
818
819 static bool peek_gw_sink(gateway_t *gw)
820 {
821     mrp_dbus_msg_t *msg = mrp_dbus_msg_method_call(gw->d->ctx->dbus,
822             AUDIOMGR_DBUS_NAME, AUDIOMGR_DBUS_ROUTE_PATH,
823             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_PEEK_SINK);
824
825     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_STRING, gw->sink_name);
826
827     mrp_dbus_send(gw->d->ctx->dbus, AUDIOMGR_DBUS_NAME,
828             AUDIOMGR_DBUS_ROUTE_PATH,
829             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_PEEK_SINK, -1,
830             peek_sink_cb, gw, msg);
831
832     mrp_dbus_msg_unref(msg);
833
834     gw->d->ctx->pending_dbus_calls++;
835
836     return TRUE;
837 }
838
839 static bool peek_gw_source(gateway_t *gw)
840 {
841     mrp_dbus_msg_t *msg = mrp_dbus_msg_method_call(gw->d->ctx->dbus,
842             AUDIOMGR_DBUS_NAME, AUDIOMGR_DBUS_ROUTE_PATH,
843             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_PEEK_SOURCE);
844
845     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_STRING, gw->source_name);
846
847     mrp_dbus_send(gw->d->ctx->dbus, AUDIOMGR_DBUS_NAME,
848             AUDIOMGR_DBUS_ROUTE_PATH,
849             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_PEEK_SOURCE, -1,
850             peek_source_cb, gw, msg);
851
852     mrp_dbus_msg_unref(msg);
853
854     gw->d->ctx->pending_dbus_calls++;
855
856     return TRUE;
857 }
858
859 static bool peek_gw_source_domain(gateway_t *gw)
860 {
861     mrp_dbus_msg_t *msg = mrp_dbus_msg_method_call(gw->d->ctx->dbus,
862             AUDIOMGR_DBUS_NAME, AUDIOMGR_DBUS_ROUTE_PATH,
863             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_PEEK_DOMAIN);
864
865     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_STRING,
866             gw->source_domain_name);
867
868     mrp_dbus_send(gw->d->ctx->dbus, AUDIOMGR_DBUS_NAME,
869             AUDIOMGR_DBUS_ROUTE_PATH,
870             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_PEEK_DOMAIN, -1,
871             peek_source_domain_cb, gw, msg);
872
873     mrp_dbus_msg_unref(msg);
874
875     gw->d->ctx->pending_dbus_calls++;
876
877     return TRUE;
878 }
879
880 static bool peek_gw_sink_domain(gateway_t *gw)
881 {
882     mrp_dbus_msg_t *msg = mrp_dbus_msg_method_call(gw->d->ctx->dbus,
883             AUDIOMGR_DBUS_NAME, AUDIOMGR_DBUS_ROUTE_PATH,
884             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_PEEK_DOMAIN);
885
886     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_STRING, gw->sink_domain_name);
887
888     mrp_dbus_send(gw->d->ctx->dbus, AUDIOMGR_DBUS_NAME,
889             AUDIOMGR_DBUS_ROUTE_PATH,
890             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_PEEK_DOMAIN, -1,
891             peek_sink_domain_cb, gw, msg);
892
893     mrp_dbus_msg_unref(msg);
894
895     gw->d->ctx->pending_dbus_calls++;
896
897     return TRUE;
898 }
899
900 static bool find_id_mapping(gateway_t *gw)
901 {
902     mrp_list_hook_t *kp, *kn, *sp, *sn;
903     domain_t *domain = gw->d;
904     bool found = FALSE;
905     domain_t *sink_domain = NULL;
906     domain_t *source_domain = NULL;
907
908     /* need to go through both domains: sink and source */
909
910     mrp_list_foreach(&gw->d->ctx->domains, sp, sn) {
911         domain_t *d = mrp_list_entry(sp, typeof(*d), hook);
912         if (strcmp(gw->sink_domain_name, d->name) == 0) {
913             sink_domain = d;
914         }
915         if (strcmp(gw->source_domain_name, d->name) == 0) {
916             source_domain = d;
917         }
918     }
919
920     if (!sink_domain) {
921         printf("peeking sink\n");
922         peek_gw_sink_domain(gw);
923         peek_gw_sink(gw);
924     }
925     else {
926         mrp_list_foreach(&sink_domain->sinks, sp, sn) {
927             sink_t *s = mrp_list_entry(sp, typeof(*s), hook);
928             if (strcmp(s->name, gw->sink_name) == 0) {
929                 gw->domain_sink_id = s->d->domain_id;
930                 gw->sink_id = s->id;
931                 printf("found GW sink (%s / %u), domain (%s / %u)\n",
932                     s->name, s->id, s->d->name, s->d->domain_id);
933                 found = TRUE;
934                 break;
935             }
936         }
937     }
938
939     if (!found && sink_domain) {
940         printf("ERROR finding sink %s for gateway\n", gw->sink_name);
941         goto error;
942     }
943
944     found = FALSE;
945
946     if (!source_domain) {
947         printf("peeking source\n");
948         peek_gw_source_domain(gw);
949         peek_gw_source(gw);
950     }
951     else {
952         mrp_list_foreach(&source_domain->sources, sp, sn) {
953             source_t *s = mrp_list_entry(sp, typeof(*s), hook);
954
955             if (strcmp(s->name, gw->source_name) == 0) {
956                 gw->domain_source_id = s->d->domain_id;
957                 gw->source_id = s->id;
958                 printf("found GW source (%s / %u), domain (%s / %u)\n",
959                     s->name, s->id, s->d->name, s->d->domain_id);
960                 found = TRUE;
961                 break;
962             }
963         }
964     }
965
966     if (!found) {
967         printf("ERROR finding source for gateway %u\n", gw->id);
968         goto error;
969     }
970
971     return TRUE;
972
973 error:
974     return FALSE;
975 }
976
977
978 static void find_gateway_ids(ctx_t *ctx)
979 {
980     mrp_list_hook_t *sp, *sn, *kp, *kn;
981
982     mrp_list_foreach(&ctx->domains, kp, kn) {
983         domain_t *d;
984         d = mrp_list_entry(kp, typeof(*d), hook);
985
986         mrp_list_foreach(&d->gateways, sp, sn) {
987             gateway_t *gw;
988             gw = mrp_list_entry(sp, typeof(*gw), hook);
989
990             find_id_mapping(gw);
991         }
992     }
993 }
994
995 static void register_source_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *reply,
996         void *user_data)
997 {
998     source_t *s = user_data;
999     mrp_list_hook_t *sp, *sn;
1000
1001     if (mrp_dbus_msg_is_error(reply)) {
1002         printf("ERROR register source cb\n");
1003         return;
1004     }
1005
1006     mrp_dbus_msg_read_basic(reply, DBUS_TYPE_UINT16, &s->id);
1007     mrp_dbus_msg_read_basic(reply, DBUS_TYPE_UINT16, &s->state);
1008
1009     printf("register source cb: id %u, state %u\n",
1010             s->id, s->state);
1011
1012     s->d->ctx->pending_dbus_calls--;
1013
1014     if (s->d->ctx->pending_dbus_calls > 0)
1015         return;
1016
1017     /* start creating gateways now when the sinks and sources have been
1018      * registered */
1019
1020     find_gateway_ids(s->d->ctx);
1021
1022     if (s->d->ctx->pending_dbus_calls > 0)
1023         return;
1024
1025     /* no extra queries needed to me made */
1026
1027     register_gateways(s->d->ctx);
1028 }
1029
1030 void register_sink_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *reply,
1031         void *user_data)
1032 {
1033     sink_t *s = user_data;
1034     mrp_list_hook_t *sp, *sn;
1035
1036     if (mrp_dbus_msg_is_error(reply)) {
1037         printf("ERROR register sink cb\n");
1038         return;
1039     }
1040
1041     mrp_dbus_msg_read_basic(reply, DBUS_TYPE_UINT16, &s->id);
1042     mrp_dbus_msg_read_basic(reply, DBUS_TYPE_UINT16, &s->state);
1043
1044     printf("register sink cb: id %u, state %u\n",
1045             s->id, s->state);
1046
1047     s->d->ctx->pending_dbus_calls--;
1048
1049     if (s->d->ctx->pending_dbus_calls > 0)
1050         return;
1051
1052     /* start creating gateways now when the sinks and sources have been
1053      * registered */
1054
1055     find_gateway_ids(s->d->ctx);
1056
1057     if (s->d->ctx->pending_dbus_calls > 0)
1058         return;
1059
1060     /* no extra queries needed to me made */
1061
1062     register_gateways(s->d->ctx);
1063 }
1064
1065 static bool register_sink(sink_t *s, uint16_t id, char *name, uint16_t domain_id,
1066         int32_t klass, int16_t volume, bool visible, int32_t avail_status,
1067         int32_t avail_reason, int16_t mute, int16_t mainvolume)
1068 {
1069     int16_t i;
1070     uint32_t visible_u = !!visible;
1071     int16_t zero = 0;
1072     int16_t one = 1;
1073
1074     mrp_dbus_msg_t *msg = mrp_dbus_msg_method_call(s->d->ctx->dbus,
1075             AUDIOMGR_DBUS_NAME, AUDIOMGR_DBUS_ROUTE_PATH,
1076             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_REGISTER_SINK);
1077
1078     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_STRUCT, NULL);
1079
1080     /* qsqinb(ii)nn */
1081
1082     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &id);
1083     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_STRING, name);
1084     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &domain_id);
1085     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &klass);
1086     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT16, &volume);
1087     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_BOOLEAN, &visible_u);
1088
1089     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_STRUCT, NULL);
1090
1091     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &avail_status);
1092     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &avail_reason);
1093
1094     mrp_dbus_msg_close_container(msg);
1095
1096     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT16, &mute);
1097     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT16, &mainvolume);
1098
1099     /* sound properties a(in) */
1100
1101     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_ARRAY, "(in)");
1102
1103     for (i = 1; i < 3; i++) {
1104         mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_STRUCT, NULL);
1105
1106         mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &i);
1107         mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT16, &zero);
1108
1109         mrp_dbus_msg_close_container(msg);
1110     }
1111
1112     mrp_dbus_msg_close_container(msg);
1113
1114     /* connection formats ai */
1115
1116     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_ARRAY, "i");
1117     {
1118         mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &one);
1119     }
1120
1121     mrp_dbus_msg_close_container(msg);
1122
1123     /* main sound properties a(in) */
1124
1125     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_ARRAY, "(in)");
1126
1127     for (i = 1; i < 3; i++) {
1128         mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_STRUCT, NULL);
1129
1130         mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &i);
1131         mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT16, &zero);
1132
1133         mrp_dbus_msg_close_container(msg);
1134     }
1135
1136     mrp_dbus_msg_close_container(msg);
1137
1138     /* main notification a(iin) */
1139
1140     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_ARRAY, "(iin)");
1141
1142     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_STRUCT, NULL);
1143
1144     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &zero);
1145     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &zero);
1146     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT16, &zero);
1147
1148     mrp_dbus_msg_close_container(msg);
1149
1150     mrp_dbus_msg_close_container(msg);
1151
1152     /* notification  a(iin) */
1153
1154     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_ARRAY, "(iin)");
1155
1156     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_STRUCT, NULL);
1157
1158     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &zero);
1159     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &zero);
1160     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT16, &zero);
1161
1162     mrp_dbus_msg_close_container(msg);
1163
1164     mrp_dbus_msg_close_container(msg);
1165
1166     /* close the main container */
1167     mrp_dbus_msg_close_container(msg);
1168
1169     mrp_dbus_send(s->d->ctx->dbus, AUDIOMGR_DBUS_NAME,
1170             AUDIOMGR_DBUS_ROUTE_PATH,
1171             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_REGISTER_SINK, -1,
1172             register_sink_cb, s, msg);
1173
1174     mrp_dbus_msg_unref(msg);
1175
1176     s->d->ctx->pending_dbus_calls++;
1177
1178     return TRUE;
1179 }
1180
1181 static bool register_source(source_t *s, uint16_t id, char *name,
1182         uint16_t domain_id, uint16_t klass, uint16_t state, int16_t volume,
1183         bool visible, int16_t avail_status, int16_t avail_reason,
1184         uint16_t interrupt)
1185 {
1186     int16_t i;
1187     uint32_t visible_u = !!visible;
1188     int16_t zero = 0;
1189     int16_t one = 1;
1190
1191     mrp_dbus_msg_t *msg = mrp_dbus_msg_method_call(s->d->ctx->dbus,
1192             AUDIOMGR_DBUS_NAME, AUDIOMGR_DBUS_ROUTE_PATH,
1193             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_REGISTER_SOURCE);
1194
1195     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_STRUCT, NULL);
1196
1197     /* qqsqinb(ii)q */
1198
1199     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &id);
1200     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &domain_id);
1201     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_STRING, name);
1202     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &klass);
1203     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &state);
1204     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT16, &volume);
1205     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_BOOLEAN, &visible_u);
1206
1207     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_STRUCT, NULL);
1208
1209     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &avail_status);
1210     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &avail_reason);
1211
1212     mrp_dbus_msg_close_container(msg);
1213
1214     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &interrupt);
1215
1216     /* sound properties a(in) */
1217
1218     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_ARRAY, "(in)");
1219
1220     for (i = 1; i < 3; i++) {
1221
1222         mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_STRUCT, NULL);
1223
1224         mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &i);
1225         mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT16, &zero);
1226
1227         mrp_dbus_msg_close_container(msg);
1228     }
1229
1230     mrp_dbus_msg_close_container(msg);
1231
1232     /* connection formats ai */
1233
1234     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_ARRAY, "i");
1235     {
1236         mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &one);
1237     }
1238
1239     mrp_dbus_msg_close_container(msg);
1240
1241     /* main sound properties a(in) */
1242
1243     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_ARRAY, "(in)");
1244
1245     for (i = 1; i < 3; i++) {
1246
1247         mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_STRUCT, NULL);
1248
1249         mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &i);
1250         mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT16, &zero);
1251
1252         mrp_dbus_msg_close_container(msg);
1253     }
1254
1255     mrp_dbus_msg_close_container(msg);
1256
1257     /* main notification a(iin) */
1258
1259     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_ARRAY, "(iin)");
1260
1261     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_STRUCT, NULL);
1262
1263     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &zero);
1264     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &zero);
1265     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT16, &zero);
1266
1267     mrp_dbus_msg_close_container(msg);
1268
1269     mrp_dbus_msg_close_container(msg);
1270
1271     /* notification  a(iin) */
1272
1273     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_ARRAY, "(iin)");
1274
1275     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_STRUCT, NULL);
1276     
1277     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &zero);
1278     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT32, &zero);
1279     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT16, &zero);
1280
1281     mrp_dbus_msg_close_container(msg);
1282
1283     mrp_dbus_msg_close_container(msg);
1284
1285     /* close the main container */
1286     mrp_dbus_msg_close_container(msg);
1287
1288     mrp_dbus_send(s->d->ctx->dbus, AUDIOMGR_DBUS_NAME,
1289             AUDIOMGR_DBUS_ROUTE_PATH,
1290             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_REGISTER_SOURCE, -1,
1291             register_source_cb, s, msg);
1292
1293     mrp_dbus_msg_unref(msg);
1294
1295     s->d->ctx->pending_dbus_calls++;
1296
1297     return TRUE;
1298 }
1299
1300 static bool unregister_domain(domain_t *d, uint16_t domain_id)
1301 {
1302     mrp_dbus_msg_t *msg = mrp_dbus_msg_method_call(d->ctx->dbus,
1303             AUDIOMGR_DBUS_NAME, AUDIOMGR_DBUS_ROUTE_PATH,
1304             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_DEREGISTER_DOMAIN);
1305
1306     printf("unregistering domain %u\n", domain_id);
1307
1308     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &domain_id);
1309
1310     mrp_dbus_send_msg(d->ctx->dbus, msg);
1311
1312     mrp_dbus_msg_unref(msg);
1313
1314     mrp_dbus_remove_method(d->ctx->dbus, d->dbus_path, d->dbus_interface,
1315         AUDIOMGR_CONNECT_ACK, method_cb, d->ctx);
1316
1317     mrp_dbus_remove_method(d->ctx->dbus, d->dbus_path, d->dbus_interface,
1318         AUDIOMGR_DISCONNECT_ACK, method_cb, d->ctx);
1319
1320     return TRUE;
1321 }
1322
1323 void register_domain_cb(mrp_dbus_t *dbus, mrp_dbus_msg_t *reply,
1324         void *user_data)
1325 {
1326     domain_t *domain = user_data;
1327     ctx_t *ctx = domain->ctx;
1328     mrp_list_hook_t *kp, *kn, *sp, *sn;
1329
1330     if (mrp_dbus_msg_is_error(reply)) {
1331         printf("ERROR registering domain cb\n");
1332         return;
1333     }
1334
1335     mrp_dbus_msg_read_basic(reply, DBUS_TYPE_UINT16, &domain->domain_id);
1336     mrp_dbus_msg_read_basic(reply, DBUS_TYPE_UINT16, &domain->state);
1337
1338     printf("register domain cb: domain %u, state %u\n",
1339             domain->domain_id, domain->state);
1340
1341     ctx->pending_dbus_calls--;
1342
1343     if (ctx->pending_dbus_calls > 0)
1344         return; /* still domains to register */
1345
1346     /* register sinks and sources that belong to all domains */
1347
1348     printf("All domains are now registered, register sinks and sources next\n");
1349
1350     mrp_list_foreach(&ctx->domains, kp, kn) {
1351         domain_t *d;
1352         d = mrp_list_entry(kp, typeof(*d), hook);
1353
1354         mrp_list_foreach(&d->sinks, sp, sn) {
1355             sink_t *s;
1356             s = mrp_list_entry(sp, typeof(*s), hook);
1357
1358             register_sink(s, 0, s->name, d->domain_id, s->klass, s->volume,
1359                     s->visible, s->availability_status, s->availability_reason,
1360                     s->mute, s->main_volume);
1361         }
1362
1363         mrp_list_foreach(&d->sources, sp, sn) {
1364             source_t *s;
1365             s = mrp_list_entry(sp, typeof(*s), hook);
1366
1367             register_source(s, 0, s->name, d->domain_id, s->klass, 1,
1368                     s->volume, s->visible, s->availability_status,
1369                     s->availability_reason, s->interrupt);
1370         }
1371     }
1372 }
1373
1374
1375 static bool register_domain(domain_t *d, uint16_t domain_id, char *name,
1376         char *bus_name, char *node_name, bool early, int16_t state)
1377 {
1378     mrp_dbus_msg_t *msg = mrp_dbus_msg_method_call(d->ctx->dbus,
1379             AUDIOMGR_DBUS_NAME, AUDIOMGR_DBUS_ROUTE_PATH,
1380             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_REGISTER_DOMAIN);
1381
1382     uint32_t early_u = !!early;
1383     uint32_t complete_u = 1;
1384     uint32_t error = E_OK;
1385
1386     /* register methods */
1387
1388     if (!mrp_dbus_export_method(d->ctx->dbus,
1389                 d->dbus_path, d->dbus_interface, AUDIOMGR_CONNECT,
1390                 method_cb, d->ctx)) {
1391         printf("Failed to register " AUDIOMGR_CONNECT " method\n");
1392         exit(1);
1393     }
1394
1395     if (!mrp_dbus_export_method(d->ctx->dbus,
1396                 d->dbus_path, d->dbus_interface, AUDIOMGR_DISCONNECT,
1397                 method_cb, d->ctx)) {
1398         printf("Failed to register " AUDIOMGR_DISCONNECT " method\n");
1399         exit(1);
1400     }
1401
1402     mrp_dbus_msg_open_container(msg, MRP_DBUS_TYPE_STRUCT, NULL);
1403
1404     /* qsssbbn */
1405  
1406     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &domain_id);
1407     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_STRING, name);
1408     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_STRING, bus_name);
1409     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_STRING, node_name);
1410     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_BOOLEAN, &early_u);
1411     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_BOOLEAN, &complete_u);
1412     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_INT16, &state);
1413
1414     mrp_dbus_msg_close_container(msg);
1415
1416     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_STRING, d->ctx->dbus_address);
1417     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_STRING, d->dbus_path);
1418     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_STRING, d->dbus_interface);
1419     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &domain_id);
1420     mrp_dbus_msg_append_basic(msg, MRP_DBUS_TYPE_UINT16, &error);
1421
1422     mrp_dbus_send(d->ctx->dbus, AUDIOMGR_DBUS_NAME,
1423             AUDIOMGR_DBUS_ROUTE_PATH,
1424             AUDIOMGR_DBUS_INTERFACE, AUDIOMGR_REGISTER_DOMAIN, -1,
1425             register_domain_cb, d, msg);
1426
1427     mrp_dbus_msg_unref(msg);
1428
1429     d->ctx->pending_dbus_calls++;
1430
1431     return TRUE;
1432 }
1433
1434 char *get_string_value(ini_parser_keyvaluepair_t *pair)
1435 {
1436     int len = strlen(pair->value);
1437
1438     /* remove '"' characters */
1439
1440     if (len > 2)
1441         return strndup(pair->value+1, len-2);
1442
1443     return NULL;
1444 }
1445
1446 bool get_boolean_value(ini_parser_keyvaluepair_t *pair)
1447 {
1448     if (strcmp(pair->value, "TRUE") == 0)
1449         return TRUE;
1450
1451     return FALSE;
1452 }
1453
1454 int32_t get_number_value(ini_parser_keyvaluepair_t *pair)
1455 {
1456     char *endptr;
1457     int32_t v;
1458
1459     v = strtold(pair->value, &endptr);
1460
1461     if (pair->value == endptr)
1462         return 0;
1463
1464     return v;
1465 }
1466
1467 bool parse_gateway(ctx_t *ctx, ini_parser_section_t *section)
1468 {
1469     mrp_list_hook_t *kp, *kn, *sp, *sn;
1470     bool found = FALSE;
1471     gateway_t *gw = mrp_allocz(sizeof(gateway_t));
1472     char *domain_name = NULL;
1473     domain_t *d = NULL;
1474
1475     if (!gw)
1476         return FALSE;
1477
1478     mrp_list_init(&gw->hook);
1479
1480     mrp_list_foreach(&section->pairs, kp, kn) {
1481
1482         ini_parser_keyvaluepair_t *pair;
1483
1484         pair = mrp_list_entry(kp, typeof(*pair), hook);
1485
1486         if (strcmp(pair->key, CONFIG_NAME) == 0) {
1487             gw->name = get_string_value(pair);
1488         }
1489         else if (strcmp(pair->key, CONFIG_DOMAIN_NAME) == 0) {
1490             domain_name = get_string_value(pair);
1491         }
1492         else if (strcmp(pair->key, CONFIG_SINK_DOMAIN) == 0) {
1493             gw->sink_domain_name = get_string_value(pair);
1494         }
1495         else if (strcmp(pair->key, CONFIG_SOURCE_DOMAIN) == 0) {
1496             gw->source_domain_name = get_string_value(pair);
1497         }
1498         else if (strcmp(pair->key, CONFIG_SINK) == 0) {
1499             gw->sink_name = get_string_value(pair);
1500         }
1501         else if (strcmp(pair->key, CONFIG_SOURCE) == 0) {
1502             gw->source_name = get_string_value(pair);
1503         }
1504     }
1505
1506     if (!domain_name || !gw->sink_name || !gw->source_name ||
1507             !gw->sink_domain_name || !gw->source_domain_name) {
1508         printf("ERROR: all mandatory strings not in place\n");
1509         goto error;
1510     }
1511
1512     mrp_list_foreach(&ctx->domains, sp, sn) {
1513         d = mrp_list_entry(sp, typeof(*d), hook);
1514
1515         if (strcmp(d->name, domain_name) == 0) {
1516             gw->d = d;
1517             mrp_list_append(&d->gateways, &gw->hook);
1518             printf("found GW domain (%s / %u)\n",
1519                     d->name, d->domain_id);
1520             found = TRUE;
1521             break;
1522         }
1523     }
1524
1525     if (!found) {
1526         printf("ERROR: domain for gateway not found\n");
1527         goto error;
1528     }
1529
1530     printf("adding gateway %s to list\n", gw->name);
1531     mrp_list_append(&d->gateways, &gw->hook);
1532
1533     mrp_free(domain_name);
1534
1535     return TRUE;
1536
1537 error:
1538     mrp_free(domain_name);
1539
1540     return FALSE;
1541 }
1542
1543 bool parse_sink(ctx_t *ctx, ini_parser_section_t *section)
1544 {
1545     mrp_list_hook_t *kp, *kn, *sp, *sn;
1546     bool found = FALSE;
1547     sink_t *s = mrp_allocz(sizeof(sink_t));
1548     char *domain_name = NULL;
1549
1550     if (!s)
1551         return FALSE;
1552
1553     mrp_list_init(&s->hook);
1554
1555     mrp_list_foreach(&section->pairs, kp, kn) {
1556
1557         ini_parser_keyvaluepair_t *pair;
1558
1559         pair = mrp_list_entry(kp, typeof(*pair), hook);
1560
1561         if (strcmp(pair->key, CONFIG_NAME) == 0) {
1562             s->name = get_string_value(pair);
1563         }
1564         else if (strcmp(pair->key, CONFIG_DOMAIN_NAME) == 0) {
1565             domain_name = get_string_value(pair);
1566         }
1567         else if (strcmp(pair->key, CONFIG_CLASS) == 0) {
1568             s->klass = get_number_value(pair);
1569         }
1570         else if (strcmp(pair->key, CONFIG_VOLUME) == 0) {
1571             s->volume = get_number_value(pair);
1572         }
1573          else if (strcmp(pair->key, CONFIG_MAIN_VOLUME) == 0) {
1574             s->main_volume = get_number_value(pair);
1575         }
1576         else if (strcmp(pair->key, CONFIG_VISIBLE) == 0) {
1577             s->visible = get_boolean_value(pair);
1578         }
1579         else if (strcmp(pair->key, CONFIG_MUTE) == 0) {
1580             s->mute = get_number_value(pair);
1581         }
1582         else if (strcmp(pair->key, CONFIG_AVAILABILITY_REASON) == 0) {
1583             s->availability_reason = get_number_value(pair);
1584         }
1585         else if (strcmp(pair->key, CONFIG_AVAILABILITY_STATUS) == 0) {
1586             s->availability_status = get_number_value(pair);
1587         }
1588     }
1589
1590     /* TODO: check that we have all the strings */
1591
1592     if (!s->name || !domain_name)
1593         goto error;
1594
1595     mrp_list_foreach(&ctx->domains, sp, sn) {
1596         domain_t *d = mrp_list_entry(sp, typeof(*d), hook);
1597
1598         if (strcmp(d->name, domain_name) == 0) {
1599             s->d = d;
1600             mrp_list_append(&d->sinks, &s->hook);
1601             found = TRUE;
1602             break;
1603         }
1604     }
1605
1606     if (!found)
1607         goto error;
1608
1609     mrp_free(domain_name);
1610     return TRUE;
1611
1612 error:
1613     destroy_sink(s);
1614     mrp_free(domain_name);
1615     return FALSE;
1616 }
1617
1618 bool parse_source(ctx_t *ctx, ini_parser_section_t *section)
1619 {
1620     mrp_list_hook_t *kp, *kn, *sp, *sn;
1621     bool found = FALSE;
1622     source_t *s = mrp_allocz(sizeof(source_t));
1623     char *domain_name = NULL;
1624
1625     if (!s)
1626         return FALSE;
1627
1628     mrp_list_init(&s->hook);
1629
1630     mrp_list_foreach(&section->pairs, kp, kn) {
1631
1632         ini_parser_keyvaluepair_t *pair;
1633
1634         pair = mrp_list_entry(kp, typeof(*pair), hook);
1635
1636         if (strcmp(pair->key, CONFIG_NAME) == 0) {
1637             s->name = get_string_value(pair);
1638         }
1639         else if (strcmp(pair->key, CONFIG_DOMAIN_NAME) == 0) {
1640             domain_name = get_string_value(pair);
1641         }
1642         else if (strcmp(pair->key, CONFIG_CLASS) == 0) {
1643             s->klass = get_number_value(pair);
1644         }
1645         else if (strcmp(pair->key, CONFIG_VOLUME) == 0) {
1646             s->volume = get_number_value(pair);
1647         }
1648          else if (strcmp(pair->key, CONFIG_MAIN_VOLUME) == 0) {
1649             s->main_volume = get_number_value(pair);
1650         }
1651         else if (strcmp(pair->key, CONFIG_VISIBLE) == 0) {
1652             s->visible = get_boolean_value(pair);
1653         }
1654         else if (strcmp(pair->key, CONFIG_INTERRUPT) == 0) {
1655             s->interrupt = get_number_value(pair);
1656         }
1657         else if (strcmp(pair->key, CONFIG_AVAILABILITY_REASON) == 0) {
1658             s->availability_reason = get_number_value(pair);
1659         }
1660         else if (strcmp(pair->key, CONFIG_AVAILABILITY_STATUS) == 0) {
1661             s->availability_status = get_number_value(pair);
1662         }
1663     }
1664
1665     /* TODO: check that we have all the strings */
1666
1667     if (!s->name || !domain_name)
1668         goto error;
1669
1670     mrp_list_foreach(&ctx->domains, sp, sn) {
1671         domain_t *d = mrp_list_entry(sp, typeof(*d), hook);
1672
1673         if (strcmp(d->name, domain_name) == 0) {
1674             s->d = d;
1675             mrp_list_append(&d->sources, &s->hook);
1676             found = TRUE;
1677             break;
1678         }
1679     }
1680
1681     if (!found)
1682         goto error;
1683
1684     mrp_free(domain_name);
1685     return TRUE;
1686
1687 error:
1688     destroy_source(s);
1689     mrp_free(domain_name);
1690     return FALSE;
1691 }
1692
1693 bool parse_domain(ctx_t *ctx, ini_parser_section_t *section)
1694 {
1695     mrp_list_hook_t *kp, *kn;
1696
1697     domain_t *d = mrp_allocz(sizeof(domain_t));
1698
1699     if (!d)
1700         return FALSE;
1701
1702     mrp_list_init(&d->hook);
1703     mrp_list_init(&d->sinks);
1704     mrp_list_init(&d->sources);
1705     mrp_list_init(&d->gateways);
1706     mrp_list_init(&d->connections);
1707
1708     mrp_list_foreach(&section->pairs, kp, kn) {
1709
1710         ini_parser_keyvaluepair_t *pair;
1711
1712         pair = mrp_list_entry(kp, typeof(*pair), hook);
1713
1714         if (strcmp(pair->key, CONFIG_NAME) == 0) {
1715             d->name = get_string_value(pair);
1716         }
1717         else if (strcmp(pair->key, CONFIG_BUS_NAME) == 0) {
1718             d->bus_name = get_string_value(pair);
1719         }
1720         else if (strcmp(pair->key, CONFIG_NODE_NAME) == 0) {
1721             d->node_name = get_string_value(pair);
1722         }
1723         else if (strcmp(pair->key, CONFIG_EARLY) == 0) {
1724             d->early = get_boolean_value(pair);
1725         }
1726         else if (strcmp(pair->key, CONFIG_DBUS_PATH) == 0) {
1727             d->dbus_path = get_string_value(pair);
1728         }
1729         else if (strcmp(pair->key, CONFIG_DBUS_INTERFACE) == 0) {
1730             d->dbus_interface = get_string_value(pair);
1731         }
1732         else if (strcmp(pair->key, CONFIG_CONNECTION_SCRIPT) == 0) {
1733             d->connection_script = get_string_value(pair);
1734         }
1735         else if (strcmp(pair->key, CONFIG_DISCONNECTION_SCRIPT) == 0) {
1736             d->disconnection_script = get_string_value(pair);
1737         }
1738     }
1739
1740     if (!d->name || !d->bus_name || !d->node_name || !d->dbus_path
1741             || !d->dbus_interface)
1742         goto error;
1743
1744     /* add domain to the context domain list */
1745
1746     d->ctx = ctx;
1747
1748     mrp_list_append(&ctx->domains, &d->hook);
1749
1750     return TRUE;
1751
1752 error:
1753     printf("ERROR parsing domain section\n");
1754     destroy_domain(d);
1755     return FALSE;
1756 }
1757
1758 bool parse_config(ctx_t *ctx, ini_parser_section_t *section)
1759 {
1760     mrp_list_hook_t *kp, *kn;
1761
1762     mrp_list_foreach(&section->pairs, kp, kn) {
1763
1764         ini_parser_keyvaluepair_t *pair;
1765
1766         pair = mrp_list_entry(kp, typeof(*pair), hook);
1767
1768         if (strcmp(pair->key, CONFIG_DBUS_BUS) == 0) {
1769             ctx->dbus_bus = get_string_value(pair);
1770         }
1771         else if (strcmp(pair->key, CONFIG_DBUS_ADDRESS) == 0) {
1772             ctx->dbus_address = get_string_value(pair);
1773         }
1774     }
1775
1776     if (!ctx->dbus_bus || !ctx->dbus_address)
1777         goto error;
1778
1779     return TRUE;
1780
1781 error:
1782     printf("ERROR parsing configuration section\n");
1783     return FALSE;
1784 }
1785
1786 bool init_data_structures(ctx_t *ctx, ini_parser_result_t *result)
1787 {
1788     mrp_list_hook_t *sp, *sn;
1789
1790     mrp_list_foreach(&result->sections, sp, sn) {
1791
1792         ini_parser_section_t *section;
1793
1794         section = mrp_list_entry(sp, typeof(*section), hook);
1795
1796         if (strcmp(section->name, CONFIG_CONFIG) == 0) {
1797             if (!parse_config(ctx, section))
1798                 goto error;
1799         }
1800         else if (strcmp(section->name, CONFIG_DOMAIN) == 0) {
1801             if (!parse_domain(ctx, section))
1802                 goto error;
1803         }
1804         else if (strcmp(section->name, CONFIG_SINK) == 0) {
1805             if (!parse_sink(ctx, section))
1806                 goto error;
1807         }
1808         else if (strcmp(section->name, CONFIG_SOURCE) == 0) {
1809             if (!parse_source(ctx, section))
1810                 goto error;
1811         }
1812         else if (strcmp(section->name, CONFIG_GATEWAY) == 0) {
1813             if (!parse_gateway(ctx, section))
1814                 goto error;
1815         }
1816         else {
1817             printf("WARNING: unknown section '%s' in configuration file\n",
1818                     section->name);
1819         }
1820     }
1821
1822     return TRUE;
1823
1824 error:
1825     return FALSE;
1826 }
1827
1828 void sig_cb(mrp_sighandler_t *sighandler, int sig, void *user_data)
1829 {
1830     ctx_t *ctx = (ctx_t *) user_data;
1831
1832     if (sig == SIGINT) {
1833         mrp_mainloop_quit(ctx->ml, 0);
1834     }
1835 }
1836
1837 int main(int argc, char **argv)
1838 {
1839     ctx_t ctx;
1840     mrp_dbus_err_t err;
1841     ini_parser_result_t result;
1842     mrp_list_hook_t *sp, *sn, *kp, *kn;
1843
1844     char *bus = "session";
1845     char *filename = "config.ini";
1846
1847     if (argc > 1) {
1848         filename = argv[1];
1849     }
1850
1851     if (argc > 2) {
1852         bus = argv[2];
1853     }
1854
1855     /* parse configuration file */
1856
1857     init_result(&result);
1858
1859     if (!mrp_parse_ini_file(filename, &result)) {
1860         printf("failed to parse configuration file '%s'\n", filename);
1861         exit(1);
1862     }
1863
1864     /* initialize the context */
1865
1866     ctx.ml = mrp_mainloop_create();
1867
1868     ctx.sighandler = mrp_add_sighandler(ctx.ml, SIGINT, sig_cb, &ctx);
1869
1870     mrp_list_init(&ctx.domains);
1871
1872     ctx.pending_dbus_calls = 0;
1873
1874     if (!init_data_structures(&ctx, &result)) {
1875         printf("failed to get required information from configuration file\n");
1876         exit(1);
1877     }
1878
1879     /* connect to D-Bus */
1880
1881     ctx.dbus = mrp_dbus_connect(ctx.ml, bus, mrp_dbus_error_init(&err));
1882
1883     if (!ctx.dbus) {
1884         printf("failed to connect to '%s' bus: %s\n", bus, err.message);
1885         exit(1);
1886     }
1887
1888     if (!mrp_dbus_acquire_name(ctx.dbus, ctx.dbus_address, NULL)) {
1889         printf("Failed to acquire name %s on D-Bus\n", ctx.dbus_address);
1890         exit(1);
1891     }
1892
1893     mrp_list_foreach(&ctx.domains, sp, sn) {
1894         domain_t *d = mrp_list_entry(sp, typeof(*d), hook);
1895
1896         register_domain(d, 0, d->name, d->bus_name, d->node_name, d->early,
1897                 DS_CONTROLLED);
1898     }
1899
1900     mrp_mainloop_run(ctx.ml);
1901
1902     mrp_list_foreach(&ctx.domains, sp, sn) {
1903         domain_t *d = mrp_list_entry(sp, typeof(*d), hook);
1904
1905         mrp_list_foreach(&d->sinks, kp, kn) {
1906             sink_t *s = mrp_list_entry(kp, typeof(*s), hook);
1907             unregister_sink(d->ctx, s->id);
1908         }
1909
1910         mrp_list_foreach(&d->sources, kp, kn) {
1911             source_t *s = mrp_list_entry(kp, typeof(*s), hook);
1912             unregister_source(d->ctx, s->id);
1913         }
1914
1915         mrp_list_foreach(&d->gateways, kp, kn) {
1916             gateway_t *gw = mrp_list_entry(kp, typeof(*gw), hook);
1917             unregister_gateway(d->ctx, gw->id);
1918         }
1919
1920         unregister_domain(d, d->domain_id);
1921     }
1922
1923     mrp_list_foreach(&ctx.domains, sp, sn) {
1924         domain_t *d = mrp_list_entry(sp, typeof(*d), hook);
1925
1926         /* TODO: free memory in the unregister callbacks? */
1927         destroy_domain(d);
1928     }
1929
1930     mrp_dbus_release_name(ctx.dbus, ctx.dbus_address, NULL);
1931     mrp_del_sighandler(ctx.sighandler);
1932     mrp_mainloop_destroy(ctx.ml);
1933
1934     printf("Exiting\n");
1935
1936     exit(0);
1937 }