resource-asm: filter non-relevant server events.
[profile/ivi/murphy.git] / src / plugins / plugin-resource-asm.c
1 /*
2  * Copyright (c) 2012, 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 <unistd.h>
32 #include <string.h>
33 #include <errno.h>
34 #include <stdarg.h>
35 #include <netdb.h>
36 #include <sys/types.h>
37 #include <sys/socket.h>
38 #include <sys/stat.h>
39 #include <signal.h>
40 #include <wait.h>
41
42 #include <audio-session-manager.h>
43
44 #include <murphy/common.h>
45 #include <murphy/core.h>
46
47 #include <murphy/resource/client-api.h>
48
49 #include "resource-asm/asm-bridge.h"
50
51
52 typedef struct {
53     uint32_t pid;
54     mrp_htbl_t *sets;
55     uint32_t n_sets;
56     uint32_t current_handle;
57
58     bool monitor; /* if the client has set up a monitor resource */
59 } client_t;
60
61
62
63 typedef struct {
64     /* configuration */
65     const char *address;
66     const char *binary;
67
68     /* asm-bridge management */
69     pid_t pid;
70     mrp_transport_t *mt; /* listening transport */
71     mrp_transport_t *t; /* connected transport */
72     bool active;
73
74     /* resource management */
75     mrp_htbl_t *clients;
76     mrp_resource_client_t *resource_client;
77     char *zone;
78
79     char *playback_resource;
80     char *recording_resource;
81
82     mrp_htbl_t *requests;
83     uint32_t current_request;
84
85     /* murphy integration */
86     mrp_sighandler_t *sighandler;
87     mrp_context_t *ctx;
88 } asm_data_t;
89
90
91 typedef enum {
92     request_type_acquire,
93     request_type_release,
94     request_type_server_event
95 } request_type_t;
96
97
98 typedef struct {
99     /* immutable */
100     uint32_t pid;
101     uint32_t handle;
102     mrp_resource_set_t *rset;
103     uint32_t request_id;
104     asm_data_t *ctx;
105
106     /* mutable */
107     request_type_t rtype;
108
109     bool monitor;
110     bool earjack;
111 } resource_set_data_t;
112
113
114
115 enum {
116     ARG_ASM_BRIDGE,
117     ARG_ASM_ZONE,
118     ARG_ASM_TPORT_ADDRESS,
119     ARG_ASM_PLAYBACK_RESOURCE,
120     ARG_ASM_RECORDING_RESOURCE,
121 };
122
123 static int tport_setup(const char *address, asm_data_t *ctx);
124
125 static void *u_to_p(uint32_t u)
126 {
127 #ifdef __SIZEOF_POINTER__
128 #if __SIZEOF_POINTER__ == 8
129     uint64_t o = u;
130 #else
131     uint32_t o = u;
132 #endif
133 #else
134     uint32_t o = o;
135 #endif
136     return (void *) o;
137 }
138
139 static uint32_t p_to_u(const void *p)
140 {
141 #ifdef __SIZEOF_POINTER__
142 #if __SIZEOF_POINTER__ == 8
143     uint32_t o = 0;
144     uint64_t big = (uint64_t) p;
145     o = big & 0xffffffff;
146 #else
147     uint32_t o = (uint32_t) p;
148 #endif
149 #else
150     uint32_t o = p;
151 #endif
152     return o;
153 }
154
155
156 static int int_comp(const void *key1, const void *key2)
157 {
158     return key1 != key2;
159 }
160
161
162 static uint32_t int_hash(const void *key)
163 {
164     return p_to_u(key);
165 }
166
167
168 static void dump_incoming_msg(lib_to_asm_t *msg, asm_data_t *ctx)
169 {
170     MRP_UNUSED(ctx);
171
172     mrp_log_info(" --> client id:       %u", msg->instance_id);
173     mrp_log_info(" --> data handle:     %d", msg->handle);
174     mrp_log_info(" --> request id:      0x%04x", msg->request_id);
175     mrp_log_info(" --> sound event:     0x%04x", msg->sound_event);
176     mrp_log_info(" --> system resource: 0x%04x", msg->system_resource);
177     mrp_log_info(" --> state:           0x%04x", msg->sound_state);
178 #ifdef USE_SECURITY
179     {
180         int n_cookie = msg->n_cookie_bytes;
181         int i;
182
183         mrp_log_info(" --> cookie: ")
184         for (i = 0; i < n_cookie, i++) {
185             mrp_log_info("0x%02x ", msg->cookie[i]);
186         }
187         mrp_log_info("\n");
188     }
189 #endif
190 }
191
192
193
194 static void dump_outgoing_msg(asm_to_lib_t *msg, asm_data_t *ctx)
195 {
196     MRP_UNUSED(ctx);
197
198     mrp_log_info(" <-- client id:       %u", msg->instance_id);
199     mrp_log_info(" <-- alloc handle:    %d", msg->alloc_handle);
200     mrp_log_info(" <-- command handle:  %d", msg->cmd_handle);
201     mrp_log_info(" <-- sound command:   0x%04x", msg->result_sound_command);
202     mrp_log_info(" <-- state:           0x%04x", msg->result_sound_state);
203     mrp_log_info(" <-- check privilege: %s",
204                             msg->check_privilege ? "TRUE" : "FALSE");
205 }
206
207 #if 0
208 static uint32_t encode_pid_handle(uint32_t pid, uint32_t handle) {
209
210     uint32_t max_pid = 4194304; /* 2^22 */
211     uint32_t max_handle = 1024; /* 2^10 */
212
213     if (pid > max_pid || handle > max_handle) {
214         return 0;
215     }
216
217     return pid | (handle << 22);
218 }
219
220 static uint32_t get_pid(uint32_t data) {
221     uint32_t pid_mask = 0xffffffff >> 10;
222
223     return data & pid_mask;
224 }
225
226
227 static uint32_t get_handle(uint32_t data) {
228
229     return data >> 22;
230 }
231 #endif
232
233
234 static void htbl_free_set(void *key, void *object)
235 {
236     resource_set_data_t *d = object;
237
238     MRP_UNUSED(key);
239
240     if (d->rset)
241         mrp_resource_set_destroy(d->rset);
242
243     mrp_free(d);
244 }
245
246
247 static client_t *create_client(uint32_t pid) {
248     client_t *client = mrp_allocz(sizeof(client_t));
249     mrp_htbl_config_t set_conf;
250
251     if (!client)
252         return NULL;
253
254     set_conf.comp = int_comp;
255     set_conf.hash = int_hash;
256     set_conf.free = htbl_free_set;
257     set_conf.nbucket = 0;
258     set_conf.nentry = 10;
259
260     client->sets = mrp_htbl_create(&set_conf);
261
262     if (!client->sets) {
263         mrp_free(client);
264         return NULL;
265     }
266
267     client->pid = pid;
268     client->current_handle = 1;
269     client->n_sets = 0;
270
271     return client;
272 }
273
274
275 static void event_cb(uint32_t request_id, mrp_resource_set_t *set, void *data)
276 {
277     resource_set_data_t *d = data;
278     asm_data_t *ctx = d->ctx;
279
280     mrp_log_info("Event CB: id %u, set %p", request_id, set);
281     mrp_log_info("Resource set %u.%u", d->pid, d->handle);
282     mrp_log_info("Advice 0x%08x, Grant 0x%08x",
283             mrp_get_resource_set_advice(d->rset),
284             mrp_get_resource_set_grant(d->rset));
285
286     switch(d->rtype) {
287         case request_type_acquire:
288         {
289             asm_to_lib_t reply;
290             mrp_log_info("callback for acquire request %u", request_id);
291
292             /* expecting next server events */
293             d->rtype = request_type_server_event;
294
295             reply.instance_id = d->pid;
296             reply.check_privilege = TRUE;
297             reply.alloc_handle = d->handle;
298             reply.cmd_handle = d->handle;
299
300             reply.result_sound_command = ASM_COMMAND_NONE;
301
302             /* TODO: check the mask properly */
303             if (mrp_get_resource_set_grant(d->rset))
304                 reply.result_sound_state = ASM_STATE_PLAYING;
305             else
306                 reply.result_sound_state = ASM_STATE_STOP;
307
308             d->rtype = request_type_server_event;
309
310             dump_outgoing_msg(&reply, ctx);
311             mrp_transport_senddata(d->ctx->t, &reply, TAG_ASM_TO_LIB);
312             break;
313         }
314         case request_type_release:
315         {
316             asm_to_lib_t reply;
317             mrp_log_info("callback for release request %u", request_id);
318
319             /* expecting next server events */
320             d->rtype = request_type_server_event;
321
322             reply.instance_id = d->pid;
323             reply.check_privilege = TRUE;
324             reply.alloc_handle = d->handle;
325             reply.cmd_handle = d->handle;
326
327             reply.result_sound_command = ASM_COMMAND_NONE;
328             reply.result_sound_state = ASM_STATE_STOP;
329
330             d->rtype = request_type_server_event;
331
332             /* stop processing server_events */
333             d->request_id = 0;
334
335             dump_outgoing_msg(&reply, ctx);
336             mrp_transport_senddata(d->ctx->t, &reply, TAG_ASM_TO_LIB);
337             break;
338         }
339         case request_type_server_event:
340         {
341             asm_to_lib_cb_t reply;
342             mrp_log_info("callback for no request %u", request_id);
343
344             reply.instance_id = d->pid;
345             reply.handle = d->handle;
346             reply.callback_expected = FALSE;
347
348             /* TODO: get the client and see if there is the monitor
349              * resource present. If yes, tell the availability state changes
350              * through it. */
351
352             if (d->request_id == 0) {
353                 /* We either haven't requested any resources or have
354                  * given up the resources. Filter out events. */
355                 break;
356             }
357
358             /* TODO: check if the d->rset state has actually changed -> only
359              * process server side notifications in that case */
360
361             if (mrp_get_resource_set_grant(d->rset))
362                 reply.sound_command = ASM_COMMAND_PLAY;
363             else
364                 reply.sound_command = ASM_COMMAND_STOP;
365
366             /* FIXME: the player-player case needs to be solved here? */
367             reply.event_source = ASM_EVENT_SOURCE_RESOURCE_CONFLICT;
368
369             mrp_transport_senddata(d->ctx->t, &reply, TAG_ASM_TO_LIB_CB);
370
371             break;
372         }
373     }
374 }
375
376
377 static asm_to_lib_t *process_msg(lib_to_asm_t *msg, asm_data_t *ctx)
378 {
379     pid_t pid = msg->instance_id;
380
381     asm_to_lib_t *reply;
382
383     reply = mrp_allocz(sizeof(asm_to_lib_t));
384
385     reply->instance_id = pid;
386     reply->check_privilege = TRUE;
387
388     reply->alloc_handle = msg->handle;
389     reply->cmd_handle = msg->handle;
390
391     reply->result_sound_command = ASM_COMMAND_NONE;
392     reply->result_sound_state = ASM_STATE_IGNORE;
393
394     switch(msg->request_id) {
395         case ASM_REQUEST_REGISTER:
396         {
397             client_t *client;
398             bool shared = FALSE;
399             char *resource = "invalid";
400
401             mrp_log_info("REQUEST: REGISTER");
402
403             /* see if the process already has a client object */
404             client = mrp_htbl_lookup(ctx->clients, u_to_p(pid));
405
406             if (!client) {
407                 client = create_client(pid);
408                 mrp_htbl_insert(ctx->clients, u_to_p(pid), client);
409             }
410 #if 0
411             else {
412                 /* From Murphy point of view this is actually an error case,
413                  * since the application can only belong to one class. This is
414                  * a Murphy limitation and should be fixed later. */
415
416                  mrp_log_error("Application tried to register twice");
417                  goto error;
418             }
419 #endif
420
421             switch (msg->sound_event) {
422                 case ASM_EVENT_NONE:
423                     break;
424                 case ASM_EVENT_SHARE_MMPLAYER:
425                     resource = "player";
426                     shared = TRUE;
427                     break;
428                 case ASM_EVENT_SHARE_MMCAMCORDER:
429                     resource = "camera";
430                     shared = TRUE;
431                     break;
432                 case ASM_EVENT_SHARE_MMSOUND:
433                     resource = "sound";
434                     shared = TRUE;
435                     break;
436                 case ASM_EVENT_SHARE_OPENAL:
437                     shared = TRUE;
438                     break;
439                 case ASM_EVENT_SHARE_AVSYSTEM:
440                     shared = TRUE;
441                     break;
442                 case ASM_EVENT_EXCLUSIVE_MMPLAYER:
443                     resource = "player";
444                     shared = FALSE;
445                     break;
446                 case ASM_EVENT_EXCLUSIVE_MMCAMCORDER:
447                     resource = "camera";
448                     shared = FALSE;
449                     break;
450                 case ASM_EVENT_EXCLUSIVE_MMSOUND:
451                     resource = "sound";
452                     shared = FALSE;
453                     break;
454                 case ASM_EVENT_EXCLUSIVE_OPENAL:
455                     shared = FALSE;
456                     break;
457                 case ASM_EVENT_EXCLUSIVE_AVSYSTEM:
458                     shared = FALSE;
459                     break;
460                 case ASM_EVENT_NOTIFY:
461                     shared = FALSE;
462                     break;
463                 case ASM_EVENT_CALL:
464                     resource = "phone";
465                     shared = FALSE;
466                     break;
467                 case ASM_EVENT_SHARE_FMRADIO:
468                     shared = TRUE;
469                     break;
470                 case ASM_EVENT_EXCLUSIVE_FMRADIO:
471                     shared = FALSE;
472                     break;
473                 case ASM_EVENT_EARJACK_UNPLUG:
474                     resource = "earjack";
475                     shared = TRUE;
476                     break;
477                 case ASM_EVENT_ALARM:
478                     shared = FALSE;
479                     break;
480                 case ASM_EVENT_VIDEOCALL:
481                     resource = "phone";
482                     shared = FALSE;
483                     break;
484                 case ASM_EVENT_MONITOR:
485                     resource = "monitor";
486                     shared = TRUE;
487                     break;
488                 case ASM_EVENT_RICH_CALL:
489                     resource = "phone";
490                     shared = FALSE;
491                     break;
492                 default:
493                     break;
494             }
495
496             if (strcmp(resource, "invalid") == 0) {
497                 mrp_log_error("unknown resource type: %d", msg->sound_event);
498                 goto error;
499             }
500             else {
501                 uint32_t handle = client->current_handle++;
502                 resource_set_data_t *d = mrp_allocz(sizeof(resource_set_data_t));
503
504                 d->handle = handle;
505                 d->ctx = ctx;
506                 d->pid = pid;
507                 d->rtype = request_type_server_event;
508                 d->request_id = 0;
509
510                 if (strcmp(resource, "earjack") == 0) {
511                     mrp_log_info("earjack status request was received");
512                     d->earjack = TRUE;
513                 }
514                 else if (strcmp(resource, "monitor") == 0) {
515                     mrp_log_info("monitor resource was received");
516                     /* TODO: tell the available state changes to this pid
517                      * via the monitor resource. */
518                     client->monitor = TRUE;
519                     d->monitor = TRUE;
520                 }
521                 else {
522                     /* a normal resource request */
523
524                     d->rset = mrp_resource_set_create(ctx->resource_client, 0,
525                             0, event_cb, d);
526
527                     if (!d->rset) {
528                         mrp_log_error("Failed to create resource set!");
529                         goto error;
530                     }
531
532                     if (mrp_resource_set_add_resource(d->rset,
533                             ctx->playback_resource, shared, NULL, TRUE) < 0) {
534                         mrp_log_error("Failed to add playback resource!");
535                         mrp_resource_set_destroy(d->rset);
536                         mrp_free(d);
537                         goto error;
538                     }
539
540                     if (mrp_resource_set_add_resource(d->rset,
541                             ctx->recording_resource, shared, NULL, TRUE) < 0) {
542                         mrp_log_error("Failed to add recording resource!");
543                         mrp_resource_set_destroy(d->rset);
544                         mrp_free(d);
545                         goto error;
546                     }
547
548                     if (mrp_application_class_add_resource_set(resource,
549                             ctx->zone, d->rset, 0) < 0) {
550                         mrp_log_error("Failed to put the rset in a class!");
551                         mrp_resource_set_destroy(d->rset);
552                         mrp_free(d);
553                         goto error;
554                     }
555                 }
556
557                 mrp_htbl_insert(client->sets, u_to_p(handle), d);
558                 client->n_sets++;
559
560                 reply->alloc_handle = handle;
561                 reply->cmd_handle = reply->alloc_handle;
562
563                 reply->result_sound_state = ASM_STATE_WAITING;
564                 reply->result_sound_command = ASM_COMMAND_NONE;
565             }
566
567             break;
568         }
569         case ASM_REQUEST_UNREGISTER:
570             {
571                 client_t *client = mrp_htbl_lookup(ctx->clients, u_to_p(pid));
572
573                 mrp_log_info("REQUEST: UNREGISTER");
574
575                 if (client) {
576                     resource_set_data_t *d;
577
578                     d = mrp_htbl_lookup(client->sets, u_to_p(msg->handle));
579                     if (!d) {
580                         mrp_log_error("set '%u.%u' not found", pid, msg->handle);
581                         goto error;
582                     }
583
584                     if (!d->rset) {
585                         /* this is a resource request with no associated
586                          * murphy resource, meaning a monitor or earjack. */
587
588                         mrp_log_info("unregistering special resource %s",
589                                 d->monitor ? "monitor" : "earjack");
590
591                         if (d->monitor)
592                             client->monitor = FALSE;
593
594                         /* TODO: what to do with the earjack unregister case? */
595                     }
596
597                     /* the resource set id destroyed when it's removed from the
598                      * table */
599                     mrp_htbl_remove(client->sets, u_to_p(msg->handle), TRUE);
600                     client->n_sets--;
601
602                     if (client->n_sets <= 0) {
603                         mrp_htbl_remove(ctx->clients, u_to_p(pid), TRUE);
604                         client = NULL;
605                     }
606                 }
607
608                 /* TODO: free memory and check if the resource set is empty when
609                  * the resource library supports it */
610
611                 /* no reply needed! */
612                 goto noreply;
613
614                 break;
615             }
616         case ASM_REQUEST_SETSTATE:
617             {
618                 client_t *client = mrp_htbl_lookup(ctx->clients, u_to_p(pid));
619
620                 resource_set_data_t *d;
621
622                 mrp_log_info("REQUEST: SET STATE");
623
624                 if (!client) {
625                     mrp_log_error("client '%u' not found", pid);
626                     goto error;
627                 }
628
629                 d = mrp_htbl_lookup(client->sets, u_to_p(msg->handle));
630                 if (!d || !d->rset) {
631                     mrp_log_error("set '%u.%u' not found", pid, msg->handle);
632                     goto error;
633                 }
634
635                 d->request_id = ++ctx->current_request;
636
637                 switch(msg->sound_state) {
638                     case ASM_STATE_PLAYING:
639                     {
640                         d->rtype = request_type_acquire;
641
642                         mrp_log_info("requesting acquisition of playback rights"
643                                 " for set '%u.%u' (id: %u)", pid, msg->handle,
644                                 d->request_id);
645
646                         mrp_resource_set_acquire(d->rset, d->request_id);
647
648                         break;
649                     }
650                     case ASM_STATE_STOP:
651                     case ASM_STATE_PAUSE:
652                     {
653                         d->rtype = request_type_release;
654
655                         mrp_log_info("requesting release of playback rights for"
656                                 " set '%u.%u' (id: %u)", pid, msg->handle,
657                                 d->request_id);
658
659                         mrp_resource_set_release(d->rset, d->request_id);
660
661                         break;
662                     }
663                     default:
664                     {
665                         mrp_log_error("Unknown state: %d", msg->sound_state);
666                     }
667                 }
668
669                 goto noreply;
670
671                 break;
672             }
673         case ASM_REQUEST_GETSTATE:
674             mrp_log_info("REQUEST: GET STATE");
675             /* TODO: get the current resource state for msg->sound_event (which
676              * is the application class). Put it to reply->result_sound_state
677              * field. */
678             break;
679         case ASM_REQUEST_GETMYSTATE:
680             mrp_log_info("REQUEST: GET MY STATE");
681             /* TODO: get the current state for the process (resource set). Put
682              * it to reply->result_sound_state field. */
683             break;
684         case ASM_REQUEST_EMERGENT_EXIT:
685             {
686                 client_t *client = mrp_htbl_lookup(ctx->clients, u_to_p(pid));
687
688                 mrp_log_info("REQUEST: EMERGENCY EXIT");
689
690                 if (!client) {
691                     mrp_log_error("client '%u' not found", pid);
692                     goto noreply;
693                 }
694
695                 mrp_htbl_remove(ctx->clients, u_to_p(pid), TRUE);
696
697                 goto noreply;
698                 break;
699             }
700         case ASM_REQUEST_SET_SUBSESSION:
701             mrp_log_info("REQUEST: SET SUBSESSION");
702             break;
703         case ASM_REQUEST_GET_SUBSESSION:
704             mrp_log_info("REQUEST: GET SUBSESSION");
705             break;
706         default:
707             mrp_log_info("REQUEST: UNKNOWN REQUEST");
708             break;
709     }
710
711     return reply;
712
713 error:
714     /* TODO: need to write some sort of message back? */
715
716 noreply:
717
718     mrp_free(reply);
719     return NULL;
720 }
721
722
723 static void process_cb_msg(lib_to_asm_cb_t *msg, asm_data_t *ctx)
724 {
725     const char *str;
726
727     MRP_UNUSED(ctx);
728
729     /* TODO: this function might tell something to the resource library */
730
731     switch (msg->cb_result) {
732         case ASM_CB_RES_IGNORE:
733             str = "ignore";
734             break;
735         case ASM_CB_RES_NONE:
736             str = "none";
737             break;
738         case ASM_CB_RES_PAUSE:
739             str = "pause";
740             break;
741         case ASM_CB_RES_PLAYING:
742             str = "playing";
743             break;
744         case ASM_CB_RES_STOP:
745             str = "stop";
746             break;
747         default:
748             mrp_log_error("unknown callback state %d", msg->cb_result);
749             return;
750     }
751
752     mrp_log_info("client %d.%u ended in state '%s' after callback",
753             msg->instance_id, msg->handle, str);
754 }
755
756
757 static void recvdatafrom_evt(mrp_transport_t *t, void *data, uint16_t tag,
758                      mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
759 {
760     asm_data_t *ctx = user_data;
761
762     MRP_UNUSED(addr);
763     MRP_UNUSED(addrlen);
764
765     switch (tag) {
766         case TAG_LIB_TO_ASM:
767             {
768                 lib_to_asm_t *msg = data;
769                 asm_to_lib_t *reply;
770
771                 /* client requests something from us */
772
773                 dump_incoming_msg(msg, ctx);
774
775                 reply = process_msg(msg, ctx);
776                 if (reply) {
777                     dump_outgoing_msg(reply, ctx);
778                     mrp_transport_senddata(t, reply, TAG_ASM_TO_LIB);
779                 }
780                 break;
781             }
782         case TAG_LIB_TO_ASM_CB:
783             {
784                 lib_to_asm_cb_t *msg = data;
785
786                 /* client tells us which state it entered after preemption */
787
788                 process_cb_msg(msg, ctx);
789                 break;
790             }
791
792         default:
793             mrp_log_error("Unknown message received!");
794             break;
795     }
796
797     mrp_data_free(data, tag);
798 }
799
800
801 static void recvdata_evt(mrp_transport_t *t, void *data, uint16_t tag, void *user_data)
802 {
803     recvdatafrom_evt(t, data, tag, NULL, 0, user_data);
804 }
805
806
807
808 static void closed_evt(mrp_transport_t *t, int error, void *user_data)
809 {
810 #if 1
811     asm_data_t *ctx = user_data;
812
813     MRP_UNUSED(error);
814
815     mrp_log_info("closed!");
816
817     mrp_transport_disconnect(t);
818     mrp_transport_destroy(t);
819
820     ctx->t = NULL;
821
822     /* TODO: start the listening socket again and relaunch the binary? */
823 #endif
824 }
825
826
827 static void connection_evt(mrp_transport_t *lt, void *user_data)
828 {
829     asm_data_t *ctx = user_data;
830
831     mrp_log_info("connection!");
832
833     if (ctx->t) {
834         mrp_log_error("Already connected");
835     }
836     else {
837         ctx->t = mrp_transport_accept(lt, ctx, 0);
838     }
839
840     /* close the listening socket, since we only have one client */
841
842     mrp_transport_destroy(lt);
843     ctx->mt = NULL;
844 }
845
846
847
848 static int tport_setup(const char *address, asm_data_t *ctx)
849 {
850     const char *atype;
851     ssize_t alen;
852     mrp_sockaddr_t addr;
853
854     struct stat statbuf;
855
856     static mrp_transport_evt_t evt = {
857         { .recvdata = recvdata_evt },
858         { .recvdatafrom = recvdatafrom_evt },
859         .closed = closed_evt,
860         .connection = connection_evt
861     };
862
863     alen = mrp_transport_resolve(NULL, address, &addr, sizeof(addr), &atype);
864
865     if (alen <= 0) {
866         mrp_log_error("Error resolving transport address");
867         goto error;
868     }
869
870     /* remove the old socket if present */
871
872     if (strcmp(atype, "unxs") == 0) {
873         char *path = addr.unx.sun_path;
874         if (path[0] == '/') {
875             /* if local socket and file exists, remove it */
876             if (stat(path, &statbuf) == 0) {
877                 if (S_ISSOCK(statbuf.st_mode)) {
878                     if (unlink(path) < 0) {
879                         mrp_log_error("error removing the socket");
880                         goto error;
881                     }
882                 }
883                 else {
884                     mrp_log_error("a file where the socket should be created");
885                     goto error;
886                 }
887             }
888         }
889     }
890
891     ctx->mt = mrp_transport_create(ctx->ctx->ml, atype, &evt, ctx,
892             MRP_TRANSPORT_MODE_CUSTOM | MRP_TRANSPORT_NONBLOCK);
893
894     if (ctx->mt == NULL) {
895         mrp_log_error("Failed to create the transport");
896         goto error;
897     }
898
899     if (!mrp_transport_bind(ctx->mt, &addr, alen)) {
900         mrp_log_error("Failed to bind the transport to address '%s'", address);
901         goto error;
902     }
903
904
905     if (!mrp_transport_listen(ctx->mt, 5)) {
906         mrp_log_error("Failed to listen to transport");
907         goto error;
908     }
909
910     return 0;
911
912 error:
913     if (ctx->mt) {
914         mrp_transport_destroy(ctx->mt);
915         ctx->mt = NULL;
916     }
917
918     return -1;
919 }
920
921 static void signal_handler(mrp_mainloop_t *ml, mrp_sighandler_t *h,
922                            int signum, void *user_data)
923 {
924     asm_data_t *ctx = user_data;
925
926     MRP_UNUSED(ml);
927     MRP_UNUSED(h);
928
929     if (signum == SIGCHLD) {
930         /* wait for the child */
931         if (ctx->pid > 0) {
932             int ret;
933
934             mrp_log_info("Received SIGCHLD, waiting for asm-bridge");
935
936             ret = waitpid(ctx->pid, NULL, WNOHANG);
937
938             if (ret == ctx->pid) {
939                 mrp_log_warning("asm-bridge process died");
940                 ctx->pid = 0;
941             }
942         }
943     }
944 }
945
946 static void htbl_free_client(void *key, void *object)
947 {
948     MRP_UNUSED(key);
949
950     client_t *client = object;
951
952     mrp_htbl_destroy(client->sets, TRUE);
953
954     /* TODO: free memory when resource API allows that */
955     mrp_free(client);
956 }
957
958 static int asm_init(mrp_plugin_t *plugin)
959 {
960     mrp_plugin_arg_t *args = plugin->args;
961     asm_data_t *ctx = mrp_allocz(sizeof(asm_data_t));
962     pid_t pid;
963     mrp_htbl_config_t client_conf;
964
965     if (!ctx) {
966         goto error;
967     }
968
969     ctx->ctx = plugin->ctx;
970     ctx->address = args[ARG_ASM_TPORT_ADDRESS].str;
971     ctx->binary = args[ARG_ASM_BRIDGE].str;
972     ctx->zone = args[ARG_ASM_ZONE].str;
973
974     ctx->playback_resource = args[ARG_ASM_PLAYBACK_RESOURCE].str;
975     ctx->recording_resource = args[ARG_ASM_RECORDING_RESOURCE].str;
976
977     /* create the transport and put it to listen mode */
978
979     if (!mrp_msg_register_type(&asm_to_lib_descr)) {
980         mrp_log_error("Failed to register message type asm_to_lib");
981         goto error;
982     }
983
984     if (!mrp_msg_register_type(&lib_to_asm_descr)) {
985         mrp_log_error("Failed to register message type lib_to_asm");
986         goto error;
987     }
988
989     if (!mrp_msg_register_type(&asm_to_lib_cb_descr)) {
990         mrp_log_error("Failed to register message type asm_to_lib_cb");
991         goto error;
992     }
993
994     if (!mrp_msg_register_type(&lib_to_asm_cb_descr)) {
995         mrp_log_error("Failed to register message type lib_to_asm_cb");
996         goto error;
997     }
998
999     if (tport_setup(ctx->address, ctx) < 0) {
1000         goto error;
1001     }
1002
1003     /* listen to SIGCHLD signal */
1004
1005     ctx->sighandler = mrp_add_sighandler(plugin->ctx->ml, SIGCHLD, signal_handler, ctx);
1006
1007     if (!ctx->sighandler) {
1008         mrp_log_error("Failed to register signal handling");
1009         goto error;
1010     }
1011
1012     client_conf.comp = int_comp;
1013     client_conf.hash = int_hash;
1014     client_conf.free = htbl_free_client;
1015     client_conf.nbucket = 0;
1016     client_conf.nentry = 10;
1017
1018     ctx->clients = mrp_htbl_create(&client_conf);
1019
1020     if (!ctx->clients) {
1021         mrp_log_error("Error creating resource set hash table");
1022         goto error;
1023     }
1024
1025     /* create the client structure towards Murphy */
1026
1027     ctx->resource_client = mrp_resource_client_create("ASM", ctx);
1028
1029     if (!ctx->resource_client) {
1030         mrp_log_error("Failed to get a resource client");
1031         goto error;
1032     }
1033
1034     /* fork-exec the asm bridge binary */
1035
1036     mrp_log_info("going to fork!");
1037
1038     pid = fork();
1039
1040     if (pid < 0) {
1041         mrp_log_error("error launching asm-bridge");
1042         goto error;
1043     }
1044     else if (pid == 0) {
1045         /* child */
1046         execl(ctx->binary, ctx->binary, ctx->address, NULL);
1047         exit(1);
1048     }
1049     else {
1050         /* parent */
1051         ctx->pid = pid;
1052         mrp_log_info("child pid is %d", pid);
1053     }
1054
1055     plugin->data = ctx;
1056
1057     return TRUE;
1058
1059 error:
1060
1061     if (ctx->pid) {
1062         kill(ctx->pid, SIGTERM);
1063         ctx->pid = 0;
1064     }
1065
1066     if (ctx->sighandler) {
1067         mrp_del_sighandler(ctx->sighandler);
1068         ctx->sighandler = NULL;
1069     }
1070
1071     if (ctx->resource_client) {
1072         mrp_resource_client_destroy(ctx->resource_client);
1073     }
1074
1075     mrp_free(ctx);
1076
1077     return FALSE;
1078 }
1079
1080
1081 static void asm_exit(mrp_plugin_t *plugin)
1082 {
1083     asm_data_t *ctx = plugin->data;
1084
1085     if (ctx->pid) {
1086         kill(ctx->pid, SIGTERM);
1087         ctx->pid = 0;
1088     }
1089
1090     if (ctx->mt) {
1091         mrp_transport_disconnect(ctx->mt);
1092         mrp_transport_destroy(ctx->mt);
1093         ctx->mt = NULL;
1094     }
1095
1096     if (ctx->t) {
1097         mrp_transport_disconnect(ctx->t);
1098         mrp_transport_destroy(ctx->t);
1099         ctx->t = NULL;
1100     }
1101
1102     if (ctx->sighandler) {
1103         mrp_del_sighandler(ctx->sighandler);
1104         ctx->sighandler = NULL;
1105     }
1106
1107     if (ctx->clients) {
1108         mrp_htbl_destroy(ctx->clients, TRUE);
1109         ctx->clients = NULL;
1110     }
1111
1112     mrp_resource_client_destroy(ctx->resource_client);
1113
1114     mrp_free(ctx);
1115     plugin->data = NULL;
1116 }
1117
1118
1119 #define ASM_DESCRIPTION "A plugin to handle SLP Audio Session Manager client requests."
1120 #define ASM_HELP        "Audio Session Manager backend"
1121 #define ASM_VERSION     MRP_VERSION_INT(0, 0, 1)
1122 #define ASM_AUTHORS     "Ismo Puustinen <ismo.puustinen@intel.com>"
1123
1124 static mrp_plugin_arg_t args[] = {
1125     MRP_PLUGIN_ARGIDX(ARG_ASM_BRIDGE, STRING, "asm_bridge", "/usr/sbin/asm-bridge"),
1126     MRP_PLUGIN_ARGIDX(ARG_ASM_ZONE, STRING, "zone", "default"),
1127     MRP_PLUGIN_ARGIDX(ARG_ASM_TPORT_ADDRESS, STRING, "tport_address", "unxs:/tmp/murphy/asm"),
1128     MRP_PLUGIN_ARGIDX(ARG_ASM_PLAYBACK_RESOURCE, STRING, "playback_resource", "audio_playback"),
1129     MRP_PLUGIN_ARGIDX(ARG_ASM_RECORDING_RESOURCE, STRING, "recording_resource", "audio_recording"),
1130 };
1131
1132
1133 MURPHY_REGISTER_PLUGIN("resource-asm",
1134                        ASM_VERSION, ASM_DESCRIPTION, ASM_AUTHORS, ASM_HELP,
1135                        MRP_SINGLETON, asm_init, asm_exit,
1136                        args, MRP_ARRAY_SIZE(args),
1137                        NULL, 0, NULL, 0, NULL);