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