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