resource-asm: fix off-by-one underallocation.
[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 #include <fcntl.h>
42
43 #include <audio-session-manager.h>
44
45 #include <murphy/common.h>
46 #include <murphy/core.h>
47 #include <murphy/core/plugin.h>
48
49 #include <murphy/resource/client-api.h>
50
51 #include "resource-asm/asm-bridge.h"
52
53 #define DEFAULT_TRANSPORT  "unxs:/tmp/murphy/asm"
54 #define TYPE_MAP_SIZE      21
55
56 typedef struct {
57     uint32_t pid;
58     mrp_htbl_t *sets;
59     uint32_t n_sets;
60     uint32_t current_handle;
61
62     bool monitor; /* if the client has set up a monitor resource */
63 } client_t;
64
65
66 typedef struct {
67     /* configuration */
68     const char *address;
69     const char *binary;
70     const char *log;
71
72     /* asm-bridge management */
73     pid_t pid;
74     mrp_transport_t *mt; /* listening transport */
75     mrp_transport_t *t; /* connected transport */
76     bool active;
77
78     /* resource management */
79     mrp_htbl_t *clients;
80     mrp_resource_client_t *resource_client;
81     char *zone;
82
83     char *audio_playback;
84     char *audio_recording;
85     char *video_playback;
86     char *video_recording;
87
88     mrp_htbl_t *requests;
89     uint32_t current_request;
90
91     /* murphy integration */
92     mrp_sighandler_t *sighandler;
93     mrp_context_t *ctx;
94 } asm_data_t;
95
96
97 typedef enum {
98     request_type_acquire,
99     request_type_release,
100     request_type_server_event
101 } request_type_t;
102
103
104 typedef struct {
105     /* immutable */
106     uint32_t pid;
107     uint32_t handle;
108     mrp_resource_set_t *rset;
109     uint32_t request_id;
110     asm_data_t *ctx;
111
112     ASM_sound_states_t requested_state;
113     ASM_sound_states_t granted_state;
114
115     /* mutable */
116     request_type_t rtype;
117
118     bool monitor;
119     bool earjack;
120 } resource_set_data_t;
121
122
123 enum {
124     ARG_ASM_BRIDGE,
125     ARG_ASM_BRIDGE_LOG,
126     ARG_ASM_ZONE,
127     ARG_ASM_TPORT_ADDRESS,
128     ARG_ASM_AUDIO_PLAYBACK,
129     ARG_ASM_AUDIO_RECORDING,
130     ARG_ASM_VIDEO_PLAYBACK,
131     ARG_ASM_VIDEO_RECORDING,
132     ARG_ASM_EVENT_CONFIG,
133 };
134
135
136 /*
137  * mapping entry of a single ASM event to a resource set
138  *
139  * Notes:
140  *     Currently it is not possible to specify resource-specific
141  *     mandatory or shared flags; they will be applied to all resources
142  *     within the set. strict/relaxed policy attributes will be applied
143  *     only to audio playback/recording resources.
144  *
145  *     Although for the sake of completeness currently it is possible to
146  *     mark a resource in the mapping as optional, probably this never
147  *     makes any sense, as there is no notion or mechanism in the ASM API
148  *     to request or communicate any optionality to the client.
149  */
150
151 typedef struct {
152     char *asm_event;                           /* ASM event as a string */
153     char *rset_class;                          /* mapped application class */
154     unsigned rset_mask;                        /* involved resources (A/V,P/R)*/
155     bool mandatory;                            /* requires resources ? */
156     bool shared;                               /* agrees to share access */
157     uint32_t priority;                         /* priority within class */
158     bool strict;                               /* strict or relaxed policy ? */
159 } rset_class_data_t;
160
161
162 #define EVENT_PREFIX "ASM_EVENT_"
163
164 static const char *asm_event_name[] = {
165     "ASM_EVENT_SHARE_MMPLAYER",
166     "ASM_EVENT_SHARE_MMCAMCORDER",
167     "ASM_EVENT_SHARE_MMSOUND",
168     "ASM_EVENT_SHARE_OPENAL",
169     "ASM_EVENT_SHARE_AVSYSTEM",
170     "ASM_EVENT_EXCLUSIVE_MMPLAYER",
171     "ASM_EVENT_EXCLUSIVE_MMCAMCORDER",
172     "ASM_EVENT_EXCLUSIVE_MMSOUND",
173     "ASM_EVENT_EXCLUSIVE_OPENAL",
174     "ASM_EVENT_EXCLUSIVE_AVSYSTEM",
175     "ASM_EVENT_NOTIFY",
176     "ASM_EVENT_CALL",
177     "ASM_EVENT_SHARE_FMRADIO",
178     "ASM_EVENT_EXCLUSIVE_FMRADIO",
179     "ASM_EVENT_EARJACK_UNPLUG",
180     "ASM_EVENT_ALARM",
181     "ASM_EVENT_VIDEOCALL",
182     "ASM_EVENT_MONITOR",
183     "ASM_EVENT_RICH_CALL",
184     "ASM_EVENT_EMERGENCY",
185     "ASM_EVENT_EXCLUSIVE_RESOURCE",
186     NULL
187 };
188
189
190 #define MAP_EVENT(_event, _class, _resmask, _shared, _strict)   \
191     [ASM_EVENT_##_event] = {                                    \
192     asm_event: #_event,                                         \
193     rset_class: _class,                                         \
194     rset_mask:  _resmask,                                       \
195     mandatory:  TRUE,                                           \
196     shared:     _shared,                                        \
197     strict:     _strict,                                        \
198     }
199
200 #define MANDATORY TRUE
201 #define SHARED    TRUE
202 #define STRICT    TRUE
203
204 #define NONE 0x0                               /* no resources */
205 #define AP   0x1                               /* audio playback */
206 #define AR   0x2                               /* audio recording */
207 #define VP   0x4                               /* video playback */
208 #define VR   0x8                               /* video recording */
209 #define APR  (AP|AR)                           /* audio playback/recording */
210 #define VPR  (VP|VR)                           /* video playback/recording */
211 #define AVP  (AP|VP)                           /* audio/video playback */
212 #define AVPR (AP|AR|VP|VR)                     /* audio/video playback/record */
213
214 static rset_class_data_t type_map[] = {
215     MAP_EVENT(SHARE_MMPLAYER       , "player" , AVP ,  SHARED, !STRICT),
216     MAP_EVENT(SHARE_MMCAMCORDER    , "camera" , AVPR,  SHARED, !STRICT),
217     MAP_EVENT(SHARE_MMSOUND        , "event"  , AP  ,  SHARED, !STRICT),
218     MAP_EVENT(SHARE_OPENAL         , "game"   , AVP ,  SHARED, !STRICT),
219     MAP_EVENT(SHARE_AVSYSTEM       , "event"  , AP  ,  SHARED, !STRICT),
220     MAP_EVENT(EXCLUSIVE_MMPLAYER   , "player" , AVP , !SHARED, !STRICT),
221     MAP_EVENT(EXCLUSIVE_MMCAMCORDER, "camera" , AVPR, !SHARED, !STRICT),
222     MAP_EVENT(EXCLUSIVE_MMSOUND    , "event"  , AP  , !SHARED, !STRICT),
223     MAP_EVENT(EXCLUSIVE_OPENAL     , "game"   , AVP , !SHARED, !STRICT),
224     MAP_EVENT(EXCLUSIVE_AVSYSTEM   , "event"  , AP  , !SHARED, !STRICT),
225     MAP_EVENT(NOTIFY               , "event"  , AVP , !SHARED, !STRICT),
226     MAP_EVENT(CALL                 , "phone"  , APR , !SHARED, !STRICT),
227     MAP_EVENT(SHARE_FMRADIO        , "radio"  , AP  ,  SHARED, !STRICT),
228     MAP_EVENT(EXCLUSIVE_FMRADIO    , "radio"  , AP  , !SHARED, !STRICT),
229     MAP_EVENT(EARJACK_UNPLUG       , "earjack", NONE,  SHARED, !STRICT),
230     MAP_EVENT(ALARM                , "alert"  , AP  , !SHARED, !STRICT),
231     MAP_EVENT(VIDEOCALL            , "phone"  , AVPR, !SHARED, !STRICT),
232     MAP_EVENT(MONITOR              , "monitor", NONE,  SHARED, !STRICT),
233     MAP_EVENT(RICH_CALL            , "phone"  , AVPR, !SHARED, !STRICT),
234     MAP_EVENT(EMERGENCY            , "phone"  , AVPR, !SHARED, !STRICT),
235     MAP_EVENT(EXCLUSIVE_RESOURCE   , "player" , APR,  !SHARED, !STRICT),
236
237     { NULL, NULL, NONE, FALSE, FALSE, FALSE, 0 }
238 };
239
240
241 static int max_event_name;
242
243
244 static int asm_event(const char *name, int len)
245 {
246     const char *event;
247     int i;
248
249     if (!len)
250         len = strlen(name);
251
252     for (i = 0; asm_event_name[i] != NULL; i++) {
253         event = asm_event_name[i];
254
255         if (!strncasecmp(name, event, len) && event[len] == '\0')
256             return i;
257
258         event = asm_event_name[i] + sizeof(EVENT_PREFIX) - 1;
259
260         if (!strncasecmp(name, event, len) && event[len] == '\0')
261             return i;
262     }
263
264     return -1;
265 }
266
267
268 static int init_event_config(void)
269 {
270     rset_class_data_t *data;
271     int i;
272
273     for (i = 0, data = type_map; data->asm_event != NULL; i++, data++) {
274         data->asm_event  = mrp_strdup(data->asm_event);
275         data->rset_class = mrp_strdup(data->rset_class);
276
277         if (data->asm_event == NULL || data->rset_class == NULL)
278             return FALSE;
279     }
280
281     return TRUE;
282 }
283
284
285 static int parse_config(rset_class_data_t *data, char *config)
286 {
287     char *s, *colon, *f, *comma, *end;
288     int len;
289     unsigned int mask;
290
291     /*
292      * Parse a configuration entry of the form: 'class:flag1,...,flagn, where
293      * the possible flags are:
294      *
295      *     mandatory/optional: whether resources are mandatory
296      *     shared/exclusive: whether need exclusive resource ownership
297      *     strict/reaxed: apply strict or reaxed policies
298      *     priority: an integer priority between 0 and 255
299      *
300      * Eg. player:mandatory,exclusive,5,relaxed configures the ASM event to
301      *     use the player application class, take mandatory (audio) resources
302      *     in exclusive mode with priority 5, and ask relaxed policy.
303      */
304
305     s = config;
306
307     mrp_debug("parsing config entry %s = '%s'", data->asm_event, config);
308
309     colon = strchr(s, ':');
310
311     if (colon == NULL) {
312         mrp_log_error("Missing app. class name for ASM event '%s'.",
313                       data->asm_event);
314         return FALSE;
315     }
316
317     len = colon - (config) + 1;
318     mrp_free(data->rset_class);
319     data->rset_class = mrp_datadup(config, len);
320     data->rset_class[len - 1] = '\0';
321
322     mrp_debug("class name: '%s'", data->rset_class);
323
324     f = colon + 1;
325     comma = strchr(f, ',');
326
327     mask = 0x80000000;
328     while (f != NULL) {
329 #       define MATCHES(_f, _n, _l) ({                                   \
330                 mrp_debug("comparing flag '%*.*s' to '%s'...",          \
331                           _l, _l, _f, _n);                              \
332                 (!strncmp(_f, _n, _l) &&                                \
333                  (_f[_l] == '\0' || _f[_l] == ','));})
334
335         len = (comma ? comma - f : (int)strlen(f));
336
337         if      (MATCHES(f, "mandatory", len)) data->mandatory = TRUE;
338         else if (MATCHES(f, "optional" , len)) data->mandatory = FALSE;
339         else if (MATCHES(f, "shared"   , len)) data->shared    = TRUE;
340         else if (MATCHES(f, "exclusive", len)) data->shared    = FALSE;
341         else if (MATCHES(f, "strict",    len)) data->strict    = TRUE;
342         else if (MATCHES(f, "relaxed",   len)) data->strict    = FALSE;
343         else if (MATCHES(f, "none"   ,   len)) mask            = NONE;
344         else if (MATCHES(f, "AP"     ,   len)) mask           |= AP;
345         else if (MATCHES(f, "AR"     ,   len)) mask           |= AR;
346         else if (MATCHES(f, "VP"     ,   len)) mask           |= VP;
347         else if (MATCHES(f, "VR"     ,   len)) mask           |= VR;
348         else if (MATCHES(f, "APR"    ,   len)) mask           |= APR;
349         else if (MATCHES(f, "VPR"    ,   len)) mask           |= VPR;
350         else if (MATCHES(f, "AVP"    ,   len)) mask           |= AVP;
351         else if (MATCHES(f, "AVPR"   ,   len)) mask           |= AVPR;
352         else if (MATCHES(f, "audio_playback" , len)) mask     |= AP;
353         else if (MATCHES(f, "audio_recording", len)) mask     |= AR;
354         else if (MATCHES(f, "video_playback" , len)) mask     |= VP;
355         else if (MATCHES(f, "video_recording", len)) mask     |= VR;
356         else {
357             data->priority = strtoul(f, &end, 10);
358             if (end && *end && *end != ',' && *end != ';') {
359                 mrp_log_error("Invalid flag or priority (%s) for event %s.",
360                               f, data->asm_event);
361                 return FALSE;
362             }
363             if (data->priority > 256) {
364                 mrp_log_error("Out of range priority (%d) for event %s.",
365                               data->priority, data->asm_event);
366             }
367         }
368
369         /* if at the end stop, otherwise go to the next flag */
370         f = (comma ? comma + 1 : NULL);
371         comma = (f ? strchr(f, ',') : NULL);
372
373 #       undef IS_FLAG
374     }
375
376     if (mask != 0x80000000)
377         data->rset_mask = (mask & 0x0fffffff);
378
379     return TRUE;
380 }
381
382
383 static int parse_event_config(mrp_plugin_arg_t *events)
384 {
385     rset_class_data_t *data;
386     mrp_plugin_arg_t  *cfg;
387     int                evt;
388
389     if (events == NULL || events->rest.args == NULL)
390         return TRUE;
391
392     mrp_plugin_foreach_undecl_arg(events, cfg) {
393         if (cfg->type != MRP_PLUGIN_ARG_TYPE_STRING) {
394             mrp_log_warning("Ignoring non-string configuration for '%s'.",
395                             cfg->key);
396             continue;
397         }
398
399         evt = asm_event(cfg->key, 0);
400
401         if (evt < 0) {
402             mrp_log_error("Ignoring configuration for unknown ASM event '%s'.",
403                           cfg->key);
404             continue;
405         }
406
407         data = type_map + evt;
408
409         if (!parse_config(data, cfg->str)) {
410             mrp_log_error("Failed to parse configuration '%s' for ASM event "
411                           "'%s'.", cfg->str, cfg->key);
412             return FALSE;
413         }
414     }
415
416     return TRUE;
417 }
418
419
420 static void dump_event_config(void)
421 {
422     rset_class_data_t *data;
423     int i, l;
424     char resmask[16], *rmp;
425
426     if (max_event_name <= 0) {
427         for (i = 0, data = type_map; i < ASM_EVENT_MAX; i++, data++)
428             if (data->asm_event != NULL &&
429                 (l = strlen(data->asm_event)) > max_event_name)
430                 max_event_name = l;
431     }
432
433     mrp_debug("event mapping:");
434     for (i = 0, data = type_map; i < ASM_EVENT_MAX; i++, data++) {
435         rmp = resmask;
436         *rmp++ = 'A';
437         if (!(data->rset_mask & APR)) *rmp++ = '-';
438         else {
439             if (data->rset_mask & AP) *rmp++ = 'P';
440             if (data->rset_mask & AR) *rmp++ = 'R';
441         }
442         *rmp++ = '/';
443         *rmp++ = 'V';
444         if (!(data->rset_mask & VPR)) *rmp++ = '-';
445         else {
446             if (data->rset_mask & VP) *rmp++ = 'P';
447             if (data->rset_mask & VR) *rmp++ = 'R';
448         }
449         *rmp = '\0';
450         mrp_debug("%*.*s: %s (%s, prio %d, %s/%s, %s)",
451                   max_event_name, max_event_name, data->asm_event,
452                   data->rset_class, resmask, data->priority,
453                   data->mandatory ? "m" : "o", data->shared ? "s" : "e",
454                   data->strict ? "strict" : "relaxed");
455     }
456 }
457
458
459 static void *u_to_p(uint32_t u)
460 {
461 #ifdef __SIZEOF_POINTER__
462 #if __SIZEOF_POINTER__ == 8
463     uint64_t o = u;
464 #else
465     uint32_t o = u;
466 #endif
467 #else
468     uint32_t o = o;
469 #endif
470     return (void *) o;
471 }
472
473
474 static uint32_t p_to_u(const void *p)
475 {
476 #ifdef __SIZEOF_POINTER__
477 #if __SIZEOF_POINTER__ == 8
478     uint32_t o = 0;
479     uint64_t big = (uint64_t) p;
480     o = big & 0xffffffff;
481 #else
482     uint32_t o = (uint32_t) p;
483 #endif
484 #else
485     uint32_t o = p;
486 #endif
487     return o;
488 }
489
490
491 static int int_comp(const void *key1, const void *key2)
492 {
493     return key1 != key2;
494 }
495
496
497 static uint32_t int_hash(const void *key)
498 {
499     return p_to_u(key);
500 }
501
502
503 static const rset_class_data_t *map_slp_media_type_to_murphy(
504         ASM_sound_events_t media_type)
505 {
506     /* check if the event is within the bounds */
507     if (media_type <= ASM_EVENT_NONE || media_type >= ASM_EVENT_MAX)
508         return NULL;
509
510     /* check that we don't overflow */
511     if (media_type > TYPE_MAP_SIZE)
512         return NULL;
513
514     return &type_map[media_type];
515 }
516
517
518 static void dump_incoming_msg(lib_to_asm_t *msg, asm_data_t *ctx)
519 {
520     MRP_UNUSED(ctx);
521
522     mrp_log_info("   --> client id:       %u", msg->instance_id);
523     mrp_log_info("   --> data handle:     %d", msg->handle);
524     mrp_log_info("   --> request id:      0x%04x", msg->request_id);
525     mrp_log_info("   --> sound event:     0x%04x", msg->sound_event);
526     mrp_log_info("   --> system resource: 0x%04x", msg->system_resource);
527     mrp_log_info("   --> state:           0x%04x", msg->sound_state);
528 #ifdef USE_SECURITY
529     {
530         int n_cookie = msg->n_cookie_bytes;
531         int i;
532
533         mrp_log_info(" --> cookie: ")
534         for (i = 0; i < n_cookie, i++) {
535             mrp_log_info("0x%02x ", msg->cookie[i]);
536         }
537         mrp_log_info("\n");
538     }
539 #endif
540 }
541
542
543
544 static void dump_outgoing_msg(asm_to_lib_t *msg, asm_data_t *ctx)
545 {
546     MRP_UNUSED(ctx);
547
548     mrp_log_info(" <--   client id:       %u", msg->instance_id);
549     mrp_log_info(" <--   alloc handle:    %d", msg->alloc_handle);
550     mrp_log_info(" <--   command handle:  %d", msg->cmd_handle);
551     mrp_log_info(" <--   sound command:   0x%04x", msg->result_sound_command);
552     mrp_log_info(" <--   state:           0x%04x", msg->result_sound_state);
553     mrp_log_info(" <--   former event:    %d", msg->former_sound_event);
554     mrp_log_info(" <--   check privilege: %s",
555             msg->check_privilege ? "TRUE" : "FALSE");
556 }
557
558
559 static void dump_outgoing_cb_msg(asm_to_lib_cb_t *msg, asm_data_t *ctx)
560 {
561     MRP_UNUSED(ctx);
562
563     mrp_log_info(" <--   client id:       %u", msg->instance_id);
564     mrp_log_info(" <--   handle:          %d", msg->handle);
565     mrp_log_info(" <--   expect callback: %d", msg->callback_expected);
566     mrp_log_info(" <--   sound command:   0x%04x", msg->sound_command);
567     mrp_log_info(" <--   event source:    0x%04x", msg->event_source);
568 }
569
570
571 #if 0
572 static uint32_t encode_pid_handle(uint32_t pid, uint32_t handle) {
573
574     uint32_t max_pid = 4194304; /* 2^22 */
575     uint32_t max_handle = 1024; /* 2^10 */
576
577     if (pid > max_pid || handle > max_handle) {
578         return 0;
579     }
580
581     return pid | (handle << 22);
582 }
583
584 static uint32_t get_pid(uint32_t data) {
585     uint32_t pid_mask = 0xffffffff >> 10;
586
587     return data & pid_mask;
588 }
589
590
591 static uint32_t get_handle(uint32_t data) {
592
593     return data >> 22;
594 }
595 #endif
596
597
598 static void htbl_free_set(void *key, void *object)
599 {
600     resource_set_data_t *d = (resource_set_data_t *) object;
601
602     MRP_UNUSED(key);
603
604     if (d->rset)
605         mrp_resource_set_destroy(d->rset);
606
607     mrp_free(d);
608 }
609
610
611 static client_t *create_client(uint32_t pid) {
612     client_t *client = (client_t *) mrp_allocz(sizeof(client_t));
613     mrp_htbl_config_t set_conf;
614
615     if (!client)
616         return NULL;
617
618     set_conf.comp = int_comp;
619     set_conf.hash = int_hash;
620     set_conf.free = htbl_free_set;
621     set_conf.nbucket = 0;
622     set_conf.nentry = 10;
623
624     client->sets = mrp_htbl_create(&set_conf);
625
626     if (!client->sets) {
627         mrp_free(client);
628         return NULL;
629     }
630
631     client->pid = pid;
632     client->current_handle = 1;
633     client->n_sets = 0;
634
635     return client;
636 }
637
638
639 static void event_cb(uint32_t request_id, mrp_resource_set_t *set, void *data)
640 {
641     resource_set_data_t *d = (resource_set_data_t *) data;
642     asm_data_t *ctx = d->ctx;
643
644     mrp_log_info("Event CB: id %u, set %p", request_id, set);
645     mrp_log_info("Resource set %u.%u", d->pid, d->handle);
646     mrp_log_info("Advice 0x%08x, Grant 0x%08x",
647             mrp_get_resource_set_advice(d->rset),
648             mrp_get_resource_set_grant(d->rset));
649
650     switch(d->rtype) {
651         case request_type_acquire:
652         {
653             asm_to_lib_t reply;
654             mrp_log_info("callback for acquire request %u", request_id);
655
656             /* expecting next server events */
657             d->rtype = request_type_server_event;
658
659             reply.instance_id = d->pid;
660             reply.check_privilege = TRUE;
661             reply.alloc_handle = d->handle;
662             reply.cmd_handle = d->handle;
663
664             reply.result_sound_state = ASM_STATE_IGNORE;
665             reply.former_sound_event = ASM_EVENT_NONE;
666
667             /* ASM doesn't support optional resources. Thus, if we are granted
668              * anything, we are granted the whole set we requested. */
669             if (mrp_get_resource_set_grant(d->rset)) {
670                 reply.result_sound_command = ASM_COMMAND_PLAY;
671                 d->granted_state = d->requested_state;
672             }
673             else {
674                 reply.result_sound_command = ASM_COMMAND_STOP;
675             }
676
677             d->rtype = request_type_server_event;
678
679             /* only send reply when "PLAYING" state was requested ->
680              * this happens when acquire request is done */
681             dump_outgoing_msg(&reply, ctx);
682             mrp_transport_senddata(ctx->t, &reply, TAG_ASM_TO_LIB);
683             break;
684         }
685         case request_type_release:
686         {
687             mrp_log_info("callback for release request %u", request_id);
688
689             /* expecting next server events */
690             d->rtype = request_type_server_event;
691
692             /* set up event filtering */
693             d->request_id = 0;
694
695             break;
696         }
697         case request_type_server_event:
698         {
699             asm_to_lib_cb_t reply;
700             mrp_log_info("callback for no request %u", request_id);
701             uint32_t grant = mrp_get_resource_set_grant(d->rset);
702
703             reply.instance_id = d->pid;
704             reply.handle = d->handle;
705
706             /* TODO: get the client and see if there is the monitor
707              * resource present. If yes, tell the availability state changes
708              * through it. */
709
710             if (d->request_id == 0) {
711                 /* We either haven't requested any resources or have
712                  * given up the resources. Filter out events. */
713                 break;
714             }
715 #if 0
716             /* check if the d->rset state has actually changed -> only
717              * process server side notifications in that case */
718             if ((grant && d->granted_state == ASM_STATE_PLAYING) ||
719                     (!grant && d->granted_state != ASM_STATE_PLAYING)) {
720                 mrp_log_info("state didn't change -> ignoring");
721                 break;
722             }
723 #endif
724             if (grant) {
725                 reply.sound_command = ASM_COMMAND_RESUME;
726                 /* ASM doesn't send callback to RESUME commands */
727                 reply.callback_expected = FALSE;
728             }
729             else {
730                 reply.sound_command = ASM_COMMAND_PAUSE;
731                 reply.callback_expected = TRUE;
732             }
733
734             /* FIXME: the player-player case needs to be solved here? */
735             reply.event_source = ASM_EVENT_SOURCE_OTHER_PLAYER_APP;
736
737             dump_outgoing_cb_msg(&reply, ctx);
738             mrp_transport_senddata(ctx->t, &reply, TAG_ASM_TO_LIB_CB);
739
740             break;
741         }
742     }
743 }
744
745
746 static asm_to_lib_t *process_msg(lib_to_asm_t *msg, asm_data_t *ctx)
747 {
748     pid_t pid = msg->instance_id;
749     mrp_attr_t attrs[3];
750     char pidbuf[32];
751
752     asm_to_lib_t *reply;
753
754     reply = (asm_to_lib_t *) mrp_allocz(sizeof(asm_to_lib_t));
755
756     if (!reply)
757         return NULL;
758
759     reply->instance_id = pid;
760     reply->check_privilege = TRUE;
761
762     reply->alloc_handle = msg->handle;
763     reply->cmd_handle = msg->handle;
764
765     reply->result_sound_command = ASM_COMMAND_NONE;
766     reply->result_sound_state = ASM_STATE_IGNORE;
767     reply->former_sound_event = ASM_EVENT_NONE;
768
769     switch(msg->request_id) {
770         case ASM_REQUEST_REGISTER:
771         {
772             uint32_t handle;
773             resource_set_data_t *d;
774             client_t *client;
775             const rset_class_data_t *rset_data;
776
777             mrp_log_info("REQUEST: REGISTER");
778
779             /* see if the process already has a client object */
780             client = (client_t *) mrp_htbl_lookup(ctx->clients, u_to_p(pid));
781
782             if (!client) {
783                 client = create_client(pid);
784                 if (!client)
785                     goto error;
786
787                 mrp_htbl_insert(ctx->clients, u_to_p(pid), client);
788             }
789 #if 0
790             else {
791                 /* From Murphy point of view this is actually an error case,
792                  * since the application can only belong to one class. This is
793                  * a Murphy limitation and should be fixed later. */
794
795                  mrp_log_error("Application tried to register twice");
796                  goto error;
797             }
798 #endif
799
800             rset_data = map_slp_media_type_to_murphy(
801                     (ASM_sound_events_t) msg->sound_event);
802
803             if (!rset_data) {
804                 mrp_log_error("unknown resource type: %d", msg->sound_event);
805                 goto error;
806             }
807
808             handle = client->current_handle++;
809             d = (resource_set_data_t *) mrp_allocz(sizeof(resource_set_data_t));
810
811             if (!d)
812                 goto error;
813
814             d->handle = handle;
815             d->ctx = ctx;
816             d->pid = pid;
817             d->rtype = request_type_server_event;
818             d->request_id = 0;
819             d->requested_state = ASM_STATE_WAITING;
820             d->granted_state = ASM_STATE_WAITING;
821
822             if (strcmp(rset_data->rset_class, "earjack") == 0) {
823                 mrp_log_info("earjack status request was received");
824                 d->earjack = TRUE;
825             }
826             else if (strcmp(rset_data->rset_class, "monitor") == 0) {
827                 mrp_log_info("monitor resource was received");
828                 /* TODO: tell the available state changes to this pid
829                  * via the monitor resource. */
830                 client->monitor = TRUE;
831                 d->monitor = TRUE;
832             }
833             else {
834                 /* a normal resource request */
835
836                 /* we have to do a separate resource set for each request
837                  * (even originating from the same client), since they are
838                  * of the same resource type (audio_playback). */
839                 d->rset = mrp_resource_set_create(ctx->resource_client, 0,
840                         rset_data->priority, event_cb, d);
841
842                 if (!d->rset) {
843                     mrp_log_error("Failed to create resource set!");
844                     mrp_free(d);
845                     goto error;
846                 }
847
848                 snprintf(pidbuf, sizeof(pidbuf), "%u", d->pid);
849                 attrs[0].type = mqi_string;
850                 attrs[0].name = "pid";
851                 attrs[0].value.string = pidbuf;
852                 attrs[1].type = mqi_string;
853                 attrs[1].name = "policy";
854                 attrs[1].value.string = rset_data->strict ? "strict":"relaxed";
855                 attrs[2].name = NULL;
856
857                 if (rset_data->rset_mask & AP) {
858                     if (mrp_resource_set_add_resource(d->rset,
859                                 ctx->audio_playback,
860                                 rset_data->shared,
861                                 &attrs[0],
862                                 rset_data->mandatory) < 0) {
863                         mrp_log_error("Failed to add audio playback resource!");
864                         mrp_resource_set_destroy(d->rset);
865                         mrp_free(d);
866                         goto error;
867                     }
868                 }
869
870                 if (rset_data->rset_mask & AR) {
871                     if (mrp_resource_set_add_resource(d->rset,
872                                 ctx->audio_recording, rset_data->shared,
873                                 &attrs[0],
874                                 rset_data->mandatory) < 0) {
875                         mrp_log_error("Failed to add audio record resource!");
876                         mrp_resource_set_destroy(d->rset);
877                         mrp_free(d);
878                         goto error;
879                     }
880                 }
881
882                 if (rset_data->rset_mask & VP) {
883                     if (mrp_resource_set_add_resource(d->rset,
884                                 ctx->video_playback,
885                                 rset_data->shared,
886                                 &attrs[0],
887                                 rset_data->mandatory) < 0) {
888                         mrp_log_error("Failed to add video playback resource!");
889                         mrp_resource_set_destroy(d->rset);
890                         mrp_free(d);
891                         goto error;
892                     }
893                 }
894
895                 if (rset_data->rset_mask & VR) {
896                     if (mrp_resource_set_add_resource(d->rset,
897                                 ctx->video_recording, rset_data->shared,
898                                 &attrs[0],
899                                 rset_data->mandatory) < 0) {
900                         mrp_log_error("Failed to add video record resource!");
901                         mrp_resource_set_destroy(d->rset);
902                         mrp_free(d);
903                         goto error;
904                     }
905                 }
906
907                 if (mrp_application_class_add_resource_set(
908                             rset_data->rset_class, ctx->zone, d->rset, 0) < 0) {
909                     mrp_log_error("Failed to put the rset in a class!");
910                     mrp_resource_set_destroy(d->rset);
911                     mrp_free(d);
912                     goto error;
913                 }
914             }
915
916             mrp_htbl_insert(client->sets, u_to_p(handle), d);
917             client->n_sets++;
918
919             reply->alloc_handle = handle;
920             reply->cmd_handle = reply->alloc_handle;
921
922             reply->result_sound_state = ASM_STATE_WAITING;
923             reply->former_sound_event = ASM_EVENT_NONE;
924             break;
925         }
926         case ASM_REQUEST_UNREGISTER:
927             {
928                 client_t *client = (client_t *)
929                         mrp_htbl_lookup(ctx->clients, u_to_p(pid));
930
931                 mrp_log_info("REQUEST: UNREGISTER");
932
933                 if (client) {
934                     resource_set_data_t *d;
935
936                     d = (resource_set_data_t *)
937                             mrp_htbl_lookup(client->sets, u_to_p(msg->handle));
938                     if (!d) {
939                         mrp_log_error("set '%u.%u' not found", pid,
940                                 msg->handle);
941                         goto error;
942                     }
943
944                     if (!d->rset) {
945                         /* this is a resource request with no associated
946                          * murphy resource, meaning a monitor or earjack. */
947
948                         mrp_log_info("unregistering special resource %s",
949                                 d->monitor ? "monitor" : "earjack");
950
951                         if (d->monitor)
952                             client->monitor = FALSE;
953
954                         /* TODO: what to do with the earjack unregister case? */
955                     }
956
957                     /* the resource set id destroyed when it's removed from the
958                      * table */
959                     mrp_htbl_remove(client->sets, u_to_p(msg->handle), TRUE);
960                     client->n_sets--;
961
962                     if (client->n_sets <= 0) {
963                         mrp_htbl_remove(ctx->clients, u_to_p(pid), TRUE);
964                         client = NULL;
965                     }
966                 }
967
968                 /* no reply needed! */
969                 goto noreply;
970
971                 break;
972             }
973         case ASM_REQUEST_SETSTATE:
974             {
975                 client_t *client =
976                         (client_t *) mrp_htbl_lookup(ctx->clients, u_to_p(pid));
977
978                 resource_set_data_t *d;
979
980                 mrp_log_info("REQUEST: SET STATE");
981
982                 if (!client) {
983                     mrp_log_error("client '%u' not found", pid);
984                     goto error;
985                 }
986
987                 d = (resource_set_data_t *)
988                         mrp_htbl_lookup(client->sets, u_to_p(msg->handle));
989                 if (!d || !d->rset) {
990                     mrp_log_error("set '%u.%u' not found", pid, msg->handle);
991                     goto error;
992                 }
993
994                 d->request_id = ++ctx->current_request;
995                 d->requested_state = (ASM_sound_states_t) msg->sound_state;
996
997                 switch(msg->sound_state) {
998                     case ASM_STATE_PLAYING:
999                     {
1000                         /* requests done for "PLAYING" state need a reply, others don't */
1001                         d->rtype = request_type_acquire;
1002
1003                         mrp_log_info("requesting acquisition of playback rights"
1004                                 " for set '%u.%u' (id: %u)", pid, msg->handle,
1005                                 d->request_id);
1006
1007                         mrp_resource_set_acquire(d->rset, d->request_id);
1008
1009                         break;
1010                     }
1011                     case ASM_STATE_STOP:
1012                     case ASM_STATE_PAUSE:
1013                     {
1014                         d->rtype = request_type_release;
1015
1016                         mrp_log_info("requesting release of playback rights for"
1017                                 " set '%u.%u' (id: %u)", pid, msg->handle,
1018                                 d->request_id);
1019
1020                         mrp_resource_set_release(d->rset, d->request_id);
1021
1022                         break;
1023                     }
1024                     default:
1025                     {
1026                         mrp_log_error("Unknown state: %d", msg->sound_state);
1027                     }
1028                 }
1029
1030                 goto noreply;
1031             }
1032         case ASM_REQUEST_GETSTATE:
1033             {
1034                 const rset_class_data_t *rset_data;
1035
1036                 rset_data = map_slp_media_type_to_murphy(
1037                         (ASM_sound_events_t) msg->sound_event);
1038
1039                 mrp_log_info("REQUEST: GET STATE for %s",
1040                         rset_data ? rset_data->rset_class : "NULL");
1041
1042                 /* TODO: get the status for rset_data->rset_class . */
1043                 reply->result_sound_state = ASM_STATE_IGNORE;
1044                 reply->former_sound_event = ASM_EVENT_NONE;
1045
1046                 break;
1047             }
1048         case ASM_REQUEST_GETMYSTATE:
1049             {
1050                 client_t *client = (client_t *)
1051                         mrp_htbl_lookup(ctx->clients, u_to_p(pid));
1052                 resource_set_data_t *d;
1053
1054                 mrp_log_info("REQUEST: GET MY STATE");
1055
1056                 if (!client) {
1057                     mrp_log_error("client '%u' not found", pid);
1058                     goto error;
1059                 }
1060
1061                 d = (resource_set_data_t *)
1062                         mrp_htbl_lookup(client->sets, u_to_p(msg->handle));
1063                 if (!d || !d->rset) {
1064                     mrp_log_error("set '%u.%u' not found", pid, msg->handle);
1065                     goto error;
1066                 }
1067
1068                 reply->result_sound_state = d->granted_state;
1069                 reply->former_sound_event = ASM_EVENT_NONE;
1070                 break;
1071             }
1072         case ASM_REQUEST_EMERGENT_EXIT:
1073             {
1074                 client_t *client = (client_t *)
1075                         mrp_htbl_lookup(ctx->clients, u_to_p(pid));
1076
1077                 mrp_log_info("REQUEST: EMERGENCY EXIT");
1078
1079                 if (!client) {
1080                     mrp_log_error("client '%u' not found", pid);
1081                     goto noreply;
1082                 }
1083
1084                 mrp_htbl_remove(ctx->clients, u_to_p(pid), TRUE);
1085
1086                 goto noreply;
1087             }
1088         case ASM_REQUEST_SET_SUBSESSION:
1089             mrp_log_info("REQUEST: SET SUBSESSION");
1090             break;
1091         case ASM_REQUEST_GET_SUBSESSION:
1092             mrp_log_info("REQUEST: GET SUBSESSION");
1093             break;
1094         default:
1095             mrp_log_info("REQUEST: UNKNOWN REQUEST");
1096             break;
1097     }
1098
1099     return reply;
1100
1101 error:
1102     /* write some message back to avoid client locking */
1103     return reply;
1104
1105 noreply:
1106
1107     mrp_free(reply);
1108     return NULL;
1109 }
1110
1111
1112 static void process_cb_msg(lib_to_asm_cb_t *msg, asm_data_t *ctx)
1113 {
1114     const char *str;
1115
1116     MRP_UNUSED(ctx);
1117
1118     /* TODO: this function might tell something to the resource library */
1119
1120     switch (msg->cb_result) {
1121         case ASM_CB_RES_IGNORE:
1122             str = "ignore";
1123             break;
1124         case ASM_CB_RES_NONE:
1125             str = "none";
1126             break;
1127         case ASM_CB_RES_PAUSE:
1128             str = "pause";
1129             break;
1130         case ASM_CB_RES_PLAYING:
1131             str = "playing";
1132             break;
1133         case ASM_CB_RES_STOP:
1134             str = "stop";
1135             break;
1136         default:
1137             mrp_log_error("unknown callback state %d", msg->cb_result);
1138             return;
1139     }
1140
1141     mrp_log_info("client %d.%u ended in state '%s' after callback",
1142             msg->instance_id, msg->handle, str);
1143 }
1144
1145
1146 static void recvdatafrom_evt(mrp_transport_t *t, void *data, uint16_t tag,
1147                      mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
1148 {
1149     asm_data_t *ctx = (asm_data_t *) user_data;
1150
1151     MRP_UNUSED(addr);
1152     MRP_UNUSED(addrlen);
1153
1154     switch (tag) {
1155         case TAG_LIB_TO_ASM:
1156             {
1157                 lib_to_asm_t *msg = (lib_to_asm_t *) data;
1158                 asm_to_lib_t *reply;
1159
1160                 /* client requests something from us */
1161
1162                 dump_incoming_msg(msg, ctx);
1163
1164                 reply = process_msg(msg, ctx);
1165                 if (reply) {
1166                     dump_outgoing_msg(reply, ctx);
1167                     mrp_transport_senddata(t, reply, TAG_ASM_TO_LIB);
1168                     mrp_free(reply);
1169                 }
1170                 mrp_log_info("");
1171                 break;
1172             }
1173         case TAG_LIB_TO_ASM_CB:
1174             {
1175                 lib_to_asm_cb_t *msg = (lib_to_asm_cb_t *) data;
1176
1177                 /* client tells us which state it entered after preemption */
1178
1179                 process_cb_msg(msg, ctx);
1180                 mrp_log_info("");
1181                 break;
1182             }
1183
1184         default:
1185             mrp_log_error("Unknown message received!");
1186             break;
1187     }
1188
1189     mrp_data_free(data, tag);
1190 }
1191
1192
1193 static void recvdata_evt(mrp_transport_t *t, void *data, uint16_t tag, void *user_data)
1194 {
1195     recvdatafrom_evt(t, data, tag, NULL, 0, user_data);
1196 }
1197
1198
1199
1200 static void closed_evt(mrp_transport_t *t, int error, void *user_data)
1201 {
1202 #if 1
1203     asm_data_t *ctx = (asm_data_t *) user_data;
1204
1205     MRP_UNUSED(error);
1206
1207     mrp_log_info("closed!");
1208
1209     mrp_transport_disconnect(t);
1210     mrp_transport_destroy(t);
1211
1212     ctx->t = NULL;
1213
1214     /* TODO: start the listening socket again and relaunch the binary? */
1215 #endif
1216 }
1217
1218
1219 static void connection_evt(mrp_transport_t *lt, void *user_data)
1220 {
1221     asm_data_t *ctx = (asm_data_t *) user_data;
1222
1223     mrp_log_info("connection!");
1224
1225     if (ctx->t) {
1226         mrp_log_error("Already connected");
1227     }
1228     else {
1229         ctx->t = mrp_transport_accept(lt, ctx, 0);
1230     }
1231
1232     /* close the listening socket, since we only have one client */
1233
1234     mrp_transport_destroy(lt);
1235     ctx->mt = NULL;
1236 }
1237
1238
1239 static int tport_setup(const char *address, asm_data_t *ctx)
1240 {
1241     const char *atype;
1242     ssize_t alen;
1243     mrp_sockaddr_t addr;
1244
1245     struct stat statbuf;
1246
1247     static mrp_transport_evt_t evt = {
1248         { .recvdata = recvdata_evt },
1249         { .recvdatafrom = recvdatafrom_evt },
1250         .closed = closed_evt,
1251         .connection = connection_evt
1252     };
1253
1254     alen = mrp_transport_resolve(NULL, address, &addr, sizeof(addr), &atype);
1255
1256     if (alen <= 0) {
1257         mrp_log_error("Error resolving transport address");
1258         goto error;
1259     }
1260
1261     /* remove the old socket if present */
1262
1263     if (strcmp(atype, "unxs") == 0) {
1264         char *path = addr.unx.sun_path;
1265         if (path[0] == '/') {
1266             /* if local socket and file exists, remove it */
1267             if (stat(path, &statbuf) == 0) {
1268                 if (S_ISSOCK(statbuf.st_mode)) {
1269                     if (unlink(path) < 0) {
1270                         mrp_log_error("error removing the socket");
1271                         goto error;
1272                     }
1273                 }
1274                 else {
1275                     mrp_log_error("a file where the socket should be created");
1276                     goto error;
1277                 }
1278             }
1279         }
1280     }
1281
1282     ctx->mt = mrp_transport_create(ctx->ctx->ml, atype, &evt, ctx,
1283             MRP_TRANSPORT_MODE_DATA | MRP_TRANSPORT_NONBLOCK);
1284
1285     if (ctx->mt == NULL) {
1286         mrp_log_error("Failed to create the transport");
1287         goto error;
1288     }
1289
1290     if (!mrp_transport_bind(ctx->mt, &addr, alen)) {
1291         mrp_log_error("Failed to bind the transport to address '%s'", address);
1292         goto error;
1293     }
1294
1295
1296     if (!mrp_transport_listen(ctx->mt, 5)) {
1297         mrp_log_error("Failed to listen to transport");
1298         goto error;
1299     }
1300
1301     return 0;
1302
1303 error:
1304     if (ctx->mt) {
1305         mrp_transport_destroy(ctx->mt);
1306         ctx->mt = NULL;
1307     }
1308
1309     return -1;
1310 }
1311
1312 static void signal_handler(mrp_sighandler_t *h, int signum, void *user_data)
1313 {
1314     asm_data_t *ctx = (asm_data_t *) user_data;
1315
1316     MRP_UNUSED(h);
1317
1318     if (signum == SIGCHLD) {
1319         /* wait for the child */
1320         if (ctx->pid > 0) {
1321             int ret;
1322
1323             mrp_log_info("Received SIGCHLD, waiting for asm-bridge");
1324
1325             ret = waitpid(ctx->pid, NULL, WNOHANG);
1326
1327             if (ret == ctx->pid) {
1328                 mrp_log_warning("asm-bridge process died");
1329                 ctx->pid = 0;
1330             }
1331         }
1332     }
1333 }
1334
1335 static void htbl_free_client(void *key, void *object)
1336 {
1337     MRP_UNUSED(key);
1338
1339     client_t *client = (client_t *) object;
1340
1341     mrp_htbl_destroy(client->sets, TRUE);
1342
1343     /* TODO: free memory when resource API allows that */
1344     mrp_free(client);
1345 }
1346
1347 static int close_fds()
1348 {
1349     int maxfd;
1350     int i;
1351     int newin, newout, newerr;
1352     int ret = -1;
1353
1354     /* Closing all file descriptors in a protable way is tricky, so improve
1355        this function as we go. */
1356
1357     maxfd = sysconf(_SC_OPEN_MAX);
1358
1359     for (i = 0; i < maxfd; i++) {
1360         if (i != fileno(stdin) && i != fileno(stdout) && i != fileno(stderr))
1361             close(i);
1362     }
1363
1364     /* redirect the streams to /dev/null */
1365
1366     newin = open("/dev/null", O_RDONLY);
1367     newout = open("/dev/null", O_WRONLY);
1368     newerr = open("/dev/null", O_WRONLY);
1369
1370     if (newin < 0 || newout < 0 || newerr < 0)
1371         goto end;
1372
1373     if (dup2(newin, fileno(stdin)) < 0 ||
1374         dup2(newout, fileno(stdout)) < 0 ||
1375         dup2(newerr, fileno(stderr)) < 0)
1376         goto end;
1377
1378     ret = 0;
1379
1380 end:
1381     close(newin);
1382     close(newout);
1383     close(newerr);
1384
1385     return ret;
1386 }
1387
1388 static int asm_init(mrp_plugin_t *plugin)
1389 {
1390     mrp_plugin_arg_t *args = plugin->args;
1391     asm_data_t *ctx = (asm_data_t *) mrp_allocz(sizeof(asm_data_t));
1392     pid_t pid;
1393     mrp_htbl_config_t client_conf;
1394
1395     if (!ctx) {
1396         goto error;
1397     }
1398
1399     ctx->ctx = plugin->ctx;
1400     ctx->address = args[ARG_ASM_TPORT_ADDRESS].str;
1401     ctx->binary = args[ARG_ASM_BRIDGE].str;
1402     ctx->zone = args[ARG_ASM_ZONE].str;
1403     ctx->log = args[ARG_ASM_BRIDGE_LOG].str;
1404
1405     ctx->audio_playback = args[ARG_ASM_AUDIO_PLAYBACK].str;
1406     ctx->audio_recording = args[ARG_ASM_AUDIO_RECORDING].str;
1407     ctx->video_playback = args[ARG_ASM_VIDEO_PLAYBACK].str;
1408     ctx->video_recording = args[ARG_ASM_VIDEO_RECORDING].str;
1409
1410     if (!init_event_config()) {
1411         mrp_log_error("Failed to initialize event/class mapping.");
1412         return FALSE;
1413     }
1414
1415     if (!parse_event_config(&args[ARG_ASM_EVENT_CONFIG])) {
1416         mrp_log_error("Failed to parse event mapping configuration.");
1417         return FALSE;
1418     }
1419     else
1420         dump_event_config();
1421
1422     /* create the transport and put it to listen mode */
1423
1424     if (!mrp_msg_register_type(&asm_to_lib_descr)) {
1425         mrp_log_error("Failed to register message type asm_to_lib");
1426         goto error;
1427     }
1428
1429     if (!mrp_msg_register_type(&lib_to_asm_descr)) {
1430         mrp_log_error("Failed to register message type lib_to_asm");
1431         goto error;
1432     }
1433
1434     if (!mrp_msg_register_type(&asm_to_lib_cb_descr)) {
1435         mrp_log_error("Failed to register message type asm_to_lib_cb");
1436         goto error;
1437     }
1438
1439     if (!mrp_msg_register_type(&lib_to_asm_cb_descr)) {
1440         mrp_log_error("Failed to register message type lib_to_asm_cb");
1441         goto error;
1442     }
1443
1444     if (tport_setup(ctx->address, ctx) < 0) {
1445         goto error;
1446     }
1447
1448     /* listen to SIGCHLD signal */
1449
1450     ctx->sighandler = mrp_add_sighandler(plugin->ctx->ml, SIGCHLD, signal_handler, ctx);
1451
1452     if (!ctx->sighandler) {
1453         mrp_log_error("Failed to register signal handling");
1454         goto error;
1455     }
1456
1457     client_conf.comp = int_comp;
1458     client_conf.hash = int_hash;
1459     client_conf.free = htbl_free_client;
1460     client_conf.nbucket = 0;
1461     client_conf.nentry = 10;
1462
1463     ctx->clients = mrp_htbl_create(&client_conf);
1464
1465     if (!ctx->clients) {
1466         mrp_log_error("Error creating resource set hash table");
1467         goto error;
1468     }
1469
1470     /* create the client structure towards Murphy */
1471
1472     ctx->resource_client = mrp_resource_client_create("ASM", ctx);
1473
1474     if (!ctx->resource_client) {
1475         mrp_log_error("Failed to get a resource client");
1476         goto error;
1477     }
1478
1479     /* fork-exec the asm bridge binary */
1480
1481     mrp_log_info("going to fork!");
1482
1483     pid = fork();
1484
1485     if (pid < 0) {
1486         mrp_log_error("error launching asm-bridge");
1487         goto error;
1488     }
1489     else if (pid == 0) {
1490         /* child */
1491         if (close_fds() < 0) {
1492             mrp_log_error("close_fds() failed");
1493             exit(1);
1494         }
1495         if (ctx->log != NULL)
1496             setenv(ASM_BRIDGE_LOG_ENVVAR, ctx->log, 1);
1497         execl(ctx->binary, ctx->binary, ctx->address, NULL);
1498         exit(1);
1499     }
1500     else {
1501         /* parent */
1502         ctx->pid = pid;
1503         mrp_log_info("child pid is %d", pid);
1504     }
1505
1506     plugin->data = ctx;
1507
1508     return TRUE;
1509
1510 error:
1511
1512     if (!ctx)
1513         return FALSE;
1514
1515     if (ctx->pid) {
1516         kill(ctx->pid, SIGTERM);
1517         ctx->pid = 0;
1518     }
1519
1520     if (ctx->sighandler) {
1521         mrp_del_sighandler(ctx->sighandler);
1522         ctx->sighandler = NULL;
1523     }
1524
1525     if (ctx->resource_client) {
1526         mrp_resource_client_destroy(ctx->resource_client);
1527     }
1528
1529     mrp_free(ctx);
1530
1531     return FALSE;
1532 }
1533
1534
1535 static void asm_exit(mrp_plugin_t *plugin)
1536 {
1537     asm_data_t *ctx = (asm_data_t *) plugin->data;
1538
1539     if (ctx->pid) {
1540         kill(ctx->pid, SIGTERM);
1541         ctx->pid = 0;
1542     }
1543
1544     if (ctx->mt) {
1545         mrp_transport_disconnect(ctx->mt);
1546         mrp_transport_destroy(ctx->mt);
1547         ctx->mt = NULL;
1548     }
1549
1550     if (ctx->t) {
1551         mrp_transport_disconnect(ctx->t);
1552         mrp_transport_destroy(ctx->t);
1553         ctx->t = NULL;
1554     }
1555
1556     if (ctx->sighandler) {
1557         mrp_del_sighandler(ctx->sighandler);
1558         ctx->sighandler = NULL;
1559     }
1560
1561     if (ctx->clients) {
1562         mrp_htbl_destroy(ctx->clients, TRUE);
1563         ctx->clients = NULL;
1564     }
1565
1566     mrp_resource_client_destroy(ctx->resource_client);
1567
1568     mrp_free(ctx);
1569     plugin->data = NULL;
1570 }
1571
1572
1573 #define ASM_DESCRIPTION "A plugin to handle SLP Audio Session Manager client requests."
1574 #define ASM_HELP        "Audio Session Manager backend"
1575 #define ASM_VERSION     MRP_VERSION_INT(0, 0, 1)
1576 #define ASM_AUTHORS     "Ismo Puustinen <ismo.puustinen@intel.com>"
1577
1578 static mrp_plugin_arg_t args[] = {
1579     MRP_PLUGIN_ARGIDX(ARG_ASM_BRIDGE, STRING, "asm_bridge", "/usr/sbin/asm-bridge"),
1580     MRP_PLUGIN_ARGIDX(ARG_ASM_BRIDGE_LOG, STRING, "asm_bridge_log", NULL),
1581     MRP_PLUGIN_ARGIDX(ARG_ASM_ZONE, STRING, "zone", "default"),
1582     MRP_PLUGIN_ARGIDX(ARG_ASM_TPORT_ADDRESS, STRING, "tport_address", DEFAULT_TRANSPORT),
1583     MRP_PLUGIN_ARGIDX(ARG_ASM_AUDIO_PLAYBACK, STRING, "audio_playback", "audio_playback"),
1584     MRP_PLUGIN_ARGIDX(ARG_ASM_AUDIO_RECORDING, STRING, "audio_recording", "audio_recording"),
1585     MRP_PLUGIN_ARGIDX(ARG_ASM_VIDEO_PLAYBACK, STRING, "video_playback", "video_playback"),
1586     MRP_PLUGIN_ARGIDX(ARG_ASM_VIDEO_RECORDING, STRING, "video_recording", "video_recording"),
1587     MRP_PLUGIN_ARGIDX(ARG_ASM_EVENT_CONFIG, UNDECL, NULL, NULL)
1588 };
1589
1590
1591 MURPHY_REGISTER_PLUGIN("resource-asm",
1592                        ASM_VERSION, ASM_DESCRIPTION, ASM_AUTHORS, ASM_HELP,
1593                        MRP_SINGLETON, asm_init, asm_exit,
1594                        args, MRP_ARRAY_SIZE(args),
1595                        NULL, 0, NULL, 0, NULL);