resource-asm: filter non-relevant server events.
[profile/ivi/murphy.git] / src / plugins / resource-asm / asm-bridge.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
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <fcntl.h>
35 #include <string.h>
36 #include <errno.h>
37
38 #include <pthread.h>
39
40 #include <sys/types.h>
41 #include <sys/ipc.h>
42 #include <sys/msg.h>
43
44 #include <signal.h>
45
46 #include <audio-session-manager.h>
47
48 #include <murphy/common.h>
49
50 #include "asm-bridge.h"
51
52
53 typedef struct ctx_s {
54     mrp_mainloop_t *ml;
55     mrp_transport_t *mt;
56     int snd_msgq;
57     mrp_htbl_t *watched_files;
58 } ctx_t;
59
60 struct watched_file {
61     char *watched_file;
62     mrp_io_watch_t *wd;
63     int32_t instance_id;
64     uint32_t handle;
65     ctx_t *ctx;
66 };
67
68 static void *wait_queue (void *arg) {
69     ASM_msg_lib_to_asm_t msg;
70
71     int *arg_thread = arg;
72
73     int asm_rcv_msgid = arg_thread[0];
74     int fd = arg_thread[1];
75
76     if (asm_rcv_msgid == -1) {
77         mrp_log_error("failed to create the receive message queue\n");
78         exit(1);
79     }
80
81     while (1) {
82         int ret = msgrcv(asm_rcv_msgid, &msg, sizeof(msg.data), 0, 0);
83
84         if (ret < 0) {
85             /* FIXME: proper error handling */
86             mrp_log_error("error receiving a message: '%s'!", strerror(errno));
87             /* remove message from queue */
88             msgrcv(asm_rcv_msgid, &msg, sizeof(msg.data), 0, MSG_NOERROR);
89             continue;
90         }
91
92         /* alignment is fine, since the first argument to the struct is a long */
93         write(fd, &msg, sizeof(ASM_msg_lib_to_asm_t));
94     }
95
96     return NULL;
97 }
98
99
100 static void dump_msg(ASM_msg_lib_to_asm_t *msg, ctx_t *ctx)
101 {
102     MRP_UNUSED(ctx);
103
104     mrp_log_info("Message id %ld:", msg->instance_id);
105
106     mrp_log_info("Data handle: %d", msg->data.handle);
107     mrp_log_info("     request id:      0x%04x", msg->data.request_id);
108     mrp_log_info("     sound event:     0x%04x", msg->data.sound_event);
109     mrp_log_info("     sound state:     0x%04x", msg->data.sound_state);
110     mrp_log_info("     system resource: 0x%04x", msg->data.system_resource);
111 #ifdef USE_SECURITY
112     {
113         int i;
114
115         mrp_log_info("     cookie: ");
116         for (i = 0; i < COOKIE_SIZE; i++) {
117             mrp_log_info("0x%02x ", msg->data.cookie[i]);
118         }
119         mrp_log_info("\n");
120     }
121 #endif
122 }
123
124
125 static int process_msg(ASM_msg_lib_to_asm_t *msg, ctx_t *ctx)
126 {
127     lib_to_asm_t res;
128     uint8_t cookie_arr[] = {};
129     uint8_t *cookie = cookie_arr;
130     int cookie_len = 0;
131
132     dump_msg(msg, ctx);
133
134     /* FIXME: instance_id is signed? */
135     res.instance_id = msg->instance_id;
136     res.handle = msg->data.handle;
137     res.request_id = msg->data.request_id;
138     res.sound_event = msg->data.sound_event;
139     res.sound_state = msg->data.sound_state;
140     res.system_resource = msg->data.system_resource;
141 #ifdef USE_SECURITY
142     {
143         cookie_len = COOKIE_SIZE;
144         cookie = msg->data.cookie;
145     }
146 #endif
147
148     res.n_cookie_bytes = cookie_len;
149     res.cookie = cookie;
150
151     if (!mrp_transport_senddata(ctx->mt, &res, lib_to_asm_descr.tag)) {
152         mrp_log_error("Failed to send message to murphy");
153         return -1;
154     }
155
156     return 0;
157 }
158
159
160 static void pipe_cb(mrp_mainloop_t *ml, mrp_io_watch_t *w, int fd,
161             mrp_io_event_t events, void *user_data)
162 {
163     ASM_msg_lib_to_asm_t msg;
164     ctx_t *ctx = user_data;
165     int bytes;
166     int ret;
167
168     MRP_UNUSED(ml);
169     MRP_UNUSED(w);
170     MRP_UNUSED(events);
171
172     bytes = read(fd, &msg, sizeof(ASM_msg_lib_to_asm_t));
173
174     if (bytes != sizeof(ASM_msg_lib_to_asm_t)) {
175         mrp_log_error("failed to read from the pipe");
176         return;
177     }
178
179     ret = process_msg(&msg, ctx);
180
181     if (ret < 0) {
182         mrp_log_error("error parsing or proxying message");
183     }
184 }
185
186
187 static void read_watch_cb(mrp_mainloop_t *ml, mrp_io_watch_t *w, int fd,
188                                   mrp_io_event_t events, void *user_data)
189 {
190     struct watched_file *wf = user_data;
191     ctx_t *ctx = wf->ctx;
192
193     MRP_UNUSED(ml);
194     MRP_UNUSED(w);
195
196     if (events & MRP_IO_EVENT_IN) {
197         uint32_t buf;
198         int ret;
199
200         ret = read(fd, &buf, sizeof(uint32_t));
201
202         if (ret == sizeof(uint32_t)) {
203             lib_to_asm_cb_t msg;
204
205             msg.instance_id = wf->instance_id;
206             msg.handle = wf->handle;
207             msg.cb_result = buf;
208
209             if (!mrp_transport_senddata(ctx->mt, &msg, lib_to_asm_cb_descr.tag)) {
210                 mrp_log_error("Failed to send message to murphy");
211             }
212         }
213     }
214     if (events & MRP_IO_EVENT_HUP) {
215         /* can we assume that the client went away? */
216         mrp_log_error("HUP event from client");
217     }
218
219     mrp_htbl_remove(ctx->watched_files, wf->watched_file, TRUE);
220     close(fd);
221 }
222
223
224 static int send_callback_to_client(asm_to_lib_cb_t *msg, ctx_t *ctx)
225 {
226 #define ASM_FILENAME_SIZE 64
227     char wr_filename[ASM_FILENAME_SIZE];
228     char rd_filename[ASM_FILENAME_SIZE];
229
230     int wr_fd = -1;
231     int rd_fd = -1;
232     uint32_t data;
233     int ret;
234
235     struct watched_file *wf = NULL;
236
237     ret = snprintf(wr_filename, ASM_FILENAME_SIZE, "/tmp/ASM.%d.%u",
238             msg->instance_id, msg->handle);
239
240     if (ret <= 0 || ret == ASM_FILENAME_SIZE)
241         goto error;
242
243     mrp_log_info("writing client preemption to file %s", wr_filename);
244
245     wr_fd = open(wr_filename, O_NONBLOCK | O_WRONLY);
246     if (wr_fd < 0) {
247         mrp_log_error("failed to open file '%s' for writing: '%s'", wr_filename,
248                 strerror(errno));
249         goto error;
250     }
251
252     if (msg->callback_expected) {
253
254         ret = snprintf(rd_filename, ASM_FILENAME_SIZE, "/tmp/ASM.%d.%ur",
255             msg->instance_id, msg->handle);
256
257         if (ret <= 0 || ret == ASM_FILENAME_SIZE)
258             goto error;
259
260         rd_fd = open(wr_filename, O_NONBLOCK | O_RDONLY);
261         if (rd_fd < 0) {
262             mrp_log_error("failed to open file '%s' for reading: '%s'",
263                     rd_filename, strerror(errno));
264             goto error;
265         }
266
267         wf = mrp_htbl_lookup(ctx->watched_files, rd_filename);
268
269         if (wf) {
270             /* already watched, this is a bad thing */
271
272             mrp_log_error("client %d.%u missed a callback notification",
273                     msg->instance_id, msg->handle);
274         }
275         else {
276
277             wf = mrp_allocz(sizeof(struct watched_file));
278
279             wf->watched_file = mrp_strdup(rd_filename);
280             wf->ctx = ctx;
281             wf->instance_id = msg->instance_id;
282             wf->handle = msg->handle;
283             wf->wd = mrp_add_io_watch(ctx->ml, rd_fd, MRP_IO_EVENT_IN | MRP_IO_EVENT_HUP,
284                 read_watch_cb, wf);
285
286             mrp_htbl_insert(ctx->watched_files, wf->watched_file, wf);
287         }
288     }
289
290     /* encode the data for sending */
291     data = 0x0000ffff;
292     data &= msg->handle;
293     data |= msg->sound_command << 16;
294     data |= msg->event_source << 24;
295
296     ret = write(wr_fd, &data, sizeof(uint32_t));
297
298     if (ret < (int) sizeof(uint32_t)) {
299         mrp_log_error("failed to write callback data to %d.%u",
300                 msg->instance_id, msg->handle);
301         goto error;
302     }
303
304     close(wr_fd);
305
306     return 0;
307
308 error:
309     if (wf && wf->watched_file) {
310         mrp_htbl_remove(ctx->watched_files, wf->watched_file, TRUE);
311     }
312
313     if (wr_fd >= 0) {
314         close(wr_fd);
315     }
316
317     if (rd_fd >= 0) {
318         close(wr_fd);
319     }
320
321     return -1;
322 #undef ASM_FILENAME_SIZE
323 }
324
325
326 static void recvfrom_murphy(mrp_transport_t *t, void *data, uint16_t tag,
327                      mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
328 {
329     ctx_t *ctx = user_data;
330
331     MRP_UNUSED(t);
332     MRP_UNUSED(addr);
333     MRP_UNUSED(addrlen);
334
335     switch (tag) {
336         case TAG_ASM_TO_LIB:
337             {
338                 asm_to_lib_t *res = data;
339                 ASM_msg_asm_to_lib_t msg;
340
341                 msg.instance_id = res->instance_id;
342                 msg.data.alloc_handle = res->alloc_handle;
343                 msg.data.cmd_handle = res->cmd_handle;
344                 msg.data.result_sound_command = res->result_sound_command;
345                 msg.data.result_sound_state = res->result_sound_state;
346 #ifdef USE_SECURITY
347                 msg.data.check_privilege = res->check_privilege;
348 #endif
349
350                 if (msgsnd(ctx->snd_msgq, (void *) &msg,
351                             sizeof(msg.data), 0) < 0) {
352                     mrp_log_error("failed to send message to client");
353                 }
354                 break;
355             }
356
357         case TAG_ASM_TO_LIB_CB:
358             {
359                 if (send_callback_to_client(data, ctx) < 0) {
360                     mrp_log_error("failed to send callback message to client");
361                 }
362                 break;
363             }
364
365         default:
366             mrp_log_error("Unknown message received!");
367             break;
368     }
369 }
370
371
372 static void recv_murphy(mrp_transport_t *t, void *data, uint16_t tag, void *user_data)
373 {
374     recvfrom_murphy(t, data, tag, NULL, 0, user_data);
375 }
376
377
378 static void closed_evt(mrp_transport_t *t, int error, void *user_data)
379 {
380     ctx_t *ctx = user_data;
381
382     MRP_UNUSED(t);
383     MRP_UNUSED(error);
384
385     mrp_log_error("server closed the connection");
386
387     mrp_mainloop_quit(ctx->ml, 0);
388 }
389
390
391 static int connect_to_murphy(char *address, ctx_t *ctx)
392 {
393     const char *atype;
394     ssize_t alen;
395     mrp_sockaddr_t addr;
396
397     static mrp_transport_evt_t evt = {
398         { .recvdata = recv_murphy },
399         { .recvdatafrom = recvfrom_murphy },
400         .closed = closed_evt,
401         .connection = NULL
402     };
403
404     if (!mrp_msg_register_type(&lib_to_asm_descr) ||
405             !mrp_msg_register_type(&asm_to_lib_descr) ||
406             !mrp_msg_register_type(&asm_to_lib_cb_descr) ||
407             !mrp_msg_register_type(&lib_to_asm_cb_descr)) {
408         mrp_log_error("Failed to register message types");
409         goto error;
410     }
411
412     alen = mrp_transport_resolve(NULL, address, &addr, sizeof(addr), &atype);
413
414     if (alen <= 0) {
415         mrp_log_error("Error resolving transport address");
416         goto error;
417     }
418
419     ctx->mt = mrp_transport_create(ctx->ml, atype, &evt, ctx,
420             MRP_TRANSPORT_MODE_CUSTOM | MRP_TRANSPORT_NONBLOCK);
421
422     if (ctx->mt == NULL) {
423         mrp_log_error("Failed to create the transport");
424         goto error;
425     }
426
427 #if 0
428     if (!mrp_transport_bind(ctx->mt, &addr, alen)) {
429         mrp_log_error("Failed to bind the transport to address '%s'", address);
430         goto error;
431     }
432 #endif
433
434     if (!mrp_transport_connect(ctx->mt, &addr, alen)) {
435         mrp_log_error("Failed to connect the transport");
436         goto error;
437     }
438
439     return 0;
440
441 error:
442     if (ctx->mt)
443         mrp_transport_destroy(ctx->mt);
444
445     return -1;
446 }
447
448 static void htbl_free_watches(void *key, void *object)
449 {
450     struct watched_file *wf = object;
451
452     MRP_UNUSED(key);
453
454     mrp_free(wf->watched_file);
455     if (wf->wd)
456         mrp_del_io_watch(wf->wd);
457
458     mrp_free(wf);
459 }
460
461
462 int main (int argc, char **argv)
463 {
464     pthread_t thread = 0;
465     void *res;
466     int pipes[2];
467     int thread_arg[2];
468     mrp_io_watch_t *iow = NULL;
469     mrp_io_event_t events = MRP_IO_EVENT_IN;
470
471     int asm_snd_msgid = msgget((key_t)4102, 0666 | IPC_CREAT);
472     int asm_rcv_msgid = msgget((key_t)2014, 0666 | IPC_CREAT);
473
474     mrp_htbl_config_t watches_conf;
475
476     ctx_t ctx;
477
478     /* set up the signal handling */
479
480     if (asm_snd_msgid == -1 || asm_rcv_msgid == -1) {
481         mrp_log_error("failed to create the message queues\n");
482         goto end;
483     }
484
485     ctx.ml = NULL;
486     ctx.mt = NULL;
487     ctx.snd_msgq = asm_snd_msgid;
488
489     if (argc != 2 || strncmp(argv[1], "unxs", 4) != 0) {
490         mrp_log_error("Usage: asm-bridge <socket_name>");
491         goto end;
492     }
493
494     watches_conf.comp = mrp_string_comp;
495     watches_conf.hash = mrp_string_hash;
496     watches_conf.free = htbl_free_watches;
497     watches_conf.nbucket = 0;
498     watches_conf.nentry = 10;
499
500     ctx.watched_files = mrp_htbl_create(&watches_conf);
501
502     ctx.ml = mrp_mainloop_create();
503
504     /* Initialize connection to murphyd */
505
506     if (connect_to_murphy(argv[1], &ctx) < 0) {
507         goto end;
508     }
509
510     /* create a pipe for communicating with the ASM thread */
511
512     if (pipe(pipes) == -1) {
513         goto end;
514     }
515
516     /* pass the message queue and the pipe writing end to the thread */
517
518     thread_arg[0] = asm_rcv_msgid;
519     thread_arg[1] = pipes[1];
520
521     /* start listening to the read end of the pipe */
522
523     iow = mrp_add_io_watch(ctx.ml, pipes[0], events, pipe_cb, &ctx);
524
525     if (!iow) {
526         goto end;
527     }
528
529     pthread_create(&thread, NULL, wait_queue, thread_arg);
530
531     /* start processing events */
532
533     mrp_mainloop_run(ctx.ml);
534
535     mrp_log_warning("shutting down asm-bridge");
536
537 end:
538     if (iow)
539         mrp_del_io_watch(iow);
540
541     if (ctx.watched_files) {
542         mrp_htbl_destroy(ctx.watched_files, TRUE);
543     }
544
545     if (thread) {
546         pthread_cancel(thread);
547         pthread_join(thread, &res);
548     }
549
550     /* free the message queues */
551
552     msgctl(asm_snd_msgid, IPC_RMID, 0);
553     msgctl(asm_rcv_msgid, IPC_RMID, 0);
554
555     exit(0);
556 }