0691042862bac4d26bd4e3a84dfa769bf65eb2bb
[profile/ivi/murphy.git] / src / plugins / plugin-amb.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 <murphy/common.h>
31 #include <murphy/core.h>
32 #include <murphy/common/dbus.h>
33
34 #include <murphy-db/mdb.h>
35 #include <murphy-db/mqi.h>
36 #include <murphy-db/mql.h>
37
38 #include <murphy/core/lua-bindings/murphy.h>
39 #include <murphy/core/lua-utils/object.h>
40 #include <murphy/core/lua-utils/funcbridge.h>
41 #include <murphy/core/lua-decision/mdb.h>
42
43 enum {
44     ARG_AMB_DBUS_ADDRESS,
45     ARG_AMB_CONFIG_FILE
46 };
47
48 #define AMB_NAME                "name"
49 #define AMB_HANDLER             "handler"
50 #define AMB_DBUS_DATA           "dbus_data"
51 #define AMB_OBJECT              "obj"
52 #define AMB_INTERFACE           "interface"
53 #define AMB_MEMBER              "property"
54 #define AMB_SIGNATURE           "signature"
55 #define AMB_BASIC_TABLE_NAME    "basic_table_name"
56 #define AMB_OUTPUTS             "outputs"
57
58
59
60 /*
61 signal sender=:1.117 -> dest=(null destination) serial=961
62 path=/org/automotive/runningstatus/vehicleSpeed;
63 interface=org.automotive.vehicleSpeed;
64 member=VehicleSpeed
65    variant       int32 0
66
67
68 dbus-send --system --print-reply --dest=org.automotive.message.broker \
69         /org/automotive/runningstatus/vehicleSpeed \
70         org.freedesktop.DBus.Properties.Get \
71         string:'org.automotive.vehicleSpeed' string:'VehicleSpeed'
72
73 method return sender=:1.69 -> dest=:1.91 reply_serial=2
74    variant       int32 0
75 */
76
77 typedef struct {
78     char *name;
79     int type;
80     union {
81         int32_t i;
82         uint32_t u;
83         double f;
84         char *s;
85     } value;
86     bool initialized;
87 } dbus_basic_property_t;
88
89 typedef void (*property_updated_cb_t)(dbus_basic_property_t *property,
90         void *user_data);
91
92 typedef struct {
93     struct {
94         const char *obj;
95         const char *iface;
96         const char *name;
97         const char *sig;
98     } dbus_data;
99     const char *name;
100     const char *basic_table_name;
101     int handler_ref;
102     int outputs_ref;
103 } lua_amb_property_t;
104
105 typedef struct {
106     mrp_dbus_t *dbus;
107     const char *amb_addr;
108     const char *config_file;
109     lua_State *L;
110     mrp_list_hook_t lua_properties;
111 } data_t;
112
113 typedef struct {
114     mqi_column_def_t defs[4];
115
116     mql_statement_t *update_operation;
117     mqi_data_type_t type;
118     mqi_handle_t table;
119 } basic_table_data_t;
120
121 typedef struct {
122     dbus_basic_property_t prop;
123
124     property_updated_cb_t cb;
125     void *user_data;
126
127     lua_amb_property_t *lua_prop;
128
129     /* for basic tables that we manage ourselves */
130     basic_table_data_t *tdata;
131
132     mrp_list_hook_t hook;
133     data_t *ctx;
134 } dbus_property_watch_t;
135
136 static data_t *global_ctx = NULL;
137
138 static basic_table_data_t *create_basic_property_table(const char *table_name,
139         const char *member, int type);
140
141 static int subscribe_property(data_t *ctx, dbus_property_watch_t *w);
142
143 static void basic_property_updated(dbus_basic_property_t *prop, void *userdata);
144
145
146 /* Lua config */
147
148 static int amb_constructor(lua_State *L);
149 static int amb_setfield(lua_State *L);
150 static int amb_getfield(lua_State *L);
151 static void lua_amb_destroy(void *data);
152
153 #define PROPERTY_CLASS MRP_LUA_CLASS(amb, property)
154
155 MRP_LUA_METHOD_LIST_TABLE (
156     amb_methods,          /* methodlist name */
157     MRP_LUA_METHOD_CONSTRUCTOR  (amb_constructor)
158 );
159
160 MRP_LUA_METHOD_LIST_TABLE (
161     amb_overrides,     /* methodlist name */
162     MRP_LUA_OVERRIDE_CALL       (amb_constructor)
163     MRP_LUA_OVERRIDE_GETFIELD   (amb_getfield)
164     MRP_LUA_OVERRIDE_SETFIELD   (amb_setfield)
165 );
166
167 MRP_LUA_CLASS_DEF (
168     amb,                /* main class name */
169     property,           /* constructor name */
170     lua_amb_property_t, /* userdata type */
171     lua_amb_destroy,    /* userdata destructor */
172     amb_methods,        /* class methods */
173     amb_overrides       /* class overrides */
174 );
175
176
177
178 static void lua_amb_destroy(void *data)
179 {
180     lua_amb_property_t *prop = (lua_amb_property_t *)data;
181
182     MRP_LUA_ENTER;
183
184     mrp_log_info("> lua_amb_destroy");
185
186     MRP_UNUSED(prop);
187
188     MRP_LUA_LEAVE_NOARG;
189 }
190
191
192 static void destroy_prop(data_t *ctx, dbus_property_watch_t *w)
193 {
194     /* TODO */
195
196     MRP_UNUSED(ctx);
197     MRP_UNUSED(w);
198 }
199
200
201 static int amb_constructor(lua_State *L)
202 {
203     lua_amb_property_t *prop;
204     size_t field_name_len;
205     const char *field_name;
206     data_t *ctx = global_ctx;
207     dbus_property_watch_t *w;
208
209     MRP_LUA_ENTER;
210
211     mrp_log_info("> amb_constructor, stack size: %d", lua_gettop(L));
212
213     prop = mrp_lua_create_object(L, PROPERTY_CLASS, NULL);
214
215     prop->handler_ref = LUA_NOREF;
216     prop->outputs_ref = LUA_NOREF;
217
218     MRP_LUA_FOREACH_FIELD(L, 2, field_name, field_name_len) {
219         char buf[field_name_len+1];
220
221         strncpy(buf, field_name, field_name_len);
222         buf[field_name_len] = '\0';
223
224         mrp_log_info("field name: %s", buf);
225
226         if (strncmp(field_name, "dbus_data", field_name_len) == 0) {
227
228             luaL_checktype(L, -1, LUA_TTABLE);
229
230             lua_pushnil(L);
231
232             while (lua_next(L, -2)) {
233
234                 const char *key;
235                 const char *value;
236
237                 luaL_checktype(L, -2, LUA_TSTRING);
238                 luaL_checktype(L, -1, LUA_TSTRING);
239
240                 key = lua_tostring(L, -2);
241                 value = lua_tostring(L, -1);
242
243                 mrp_log_info("%s -> %s", key, value);
244
245                 if (!key || !value)
246                     goto error;
247
248                 if (strcmp(key, "signature") == 0) {
249                     prop->dbus_data.sig = mrp_strdup(value);
250                 }
251                 else if (strcmp(key, "property") == 0) {
252                     prop->dbus_data.name = mrp_strdup(value);
253                 }
254                 else if (strcmp(key, "obj") == 0) {
255                     prop->dbus_data.obj = mrp_strdup(value);
256                 }
257                 else if (strcmp(key, "interface") == 0) {
258                     prop->dbus_data.iface = mrp_strdup(value);
259                 }
260                 else {
261                     goto error;
262                 }
263
264                 lua_pop(L, 1);
265             }
266
267             /* check that we have all necessary data */
268             if (prop->dbus_data.sig == NULL ||
269                 prop->dbus_data.iface == NULL ||
270                 prop->dbus_data.obj == NULL ||
271                 prop->dbus_data.name == NULL) {
272                 goto error;
273             }
274         }
275         else if (strncmp(field_name, "handler", field_name_len) == 0) {
276             luaL_checktype(L, -1, LUA_TFUNCTION);
277             prop->handler_ref = luaL_ref(L, LUA_REGISTRYINDEX);
278             lua_pushnil(L); /* need two items on the stack */
279         }
280         else if (strncmp(field_name, AMB_NAME, field_name_len) == 0) {
281             luaL_checktype(L, -1, LUA_TSTRING);
282             prop->name = mrp_strdup(lua_tostring(L, -1));
283             mrp_lua_set_object_name(L, PROPERTY_CLASS, prop->name);
284         }
285         else if (strncmp(field_name, "basic_table_name", field_name_len) == 0) {
286             luaL_checktype(L, -1, LUA_TSTRING);
287             prop->basic_table_name = mrp_strdup(lua_tostring(L, -1));
288         }
289         else if (strncmp(field_name, AMB_OUTPUTS, field_name_len) == 0) {
290             prop->outputs_ref = luaL_ref(L, LUA_REGISTRYINDEX);
291             lua_pushnil(L); /* need two items on the stack */
292         }
293     }
294
295     if (!prop->name)
296         goto error;
297
298     if (prop->handler_ref == LUA_NOREF && !prop->basic_table_name)
299         goto error;
300
301     w = mrp_allocz(sizeof(dbus_property_watch_t));
302
303     w->ctx = ctx;
304     w->lua_prop = prop;
305     w->prop.initialized = FALSE;
306     w->prop.name = mrp_strdup(w->lua_prop->dbus_data.name);
307     w->prop.type = DBUS_TYPE_INVALID;
308
309     if (prop->handler_ref == LUA_NOREF) {
310         basic_table_data_t *tdata;
311
312         w->prop.type = w->lua_prop->dbus_data.sig[0]; /* FIXME */
313
314         tdata = create_basic_property_table(prop->basic_table_name,
315             prop->dbus_data.name, w->prop.type);
316
317         if (!tdata) {
318             goto error;
319         }
320
321         w->tdata = tdata;
322
323         w->cb = basic_property_updated;
324         w->user_data = w;
325
326         /* add_table_data(tdata, ctx); */
327         if (subscribe_property(ctx, w)) {
328             mrp_log_error("Failed to subscribe to basic property");
329             goto error;
330         }
331     }
332     else {
333         /* we now have the callback function reference */
334
335         /* TODO: refactor to decouple updating the property (calling the
336          * lua handler) from parsing the D-Bus message. Is this possible? */
337         if (subscribe_property(ctx, w)) {
338             mrp_log_error("Failed to subscribe to basic property");
339             goto error;
340         }
341     }
342
343
344     mrp_list_init(&w->hook);
345
346     mrp_list_append(&ctx->lua_properties, &w->hook);
347
348     /* TODO: need some mapping? or custom property_watch? */
349
350     /* TODO: put the object to a global table or not? maybe better to just
351      * unload them when the plugin is unloaded. */
352
353     mrp_lua_push_object(L, prop);
354
355     MRP_LUA_LEAVE(1);
356
357 error:
358     /* TODO: delete the allocated data */
359     destroy_prop(global_ctx, w);
360
361     mrp_log_error("< amb_constructor ERROR");
362     MRP_LUA_LEAVE(0);
363 }
364
365 static int amb_getfield(lua_State *L)
366 {
367     lua_amb_property_t *prop = mrp_lua_check_object(L, PROPERTY_CLASS, 1);
368     size_t field_name_len;
369     const char *field_name = lua_tolstring(L, 2, &field_name_len);
370
371     MRP_LUA_ENTER;
372
373     if (!prop)
374         goto error;
375
376     mrp_log_info("> amb_getfield");
377
378     if (strncmp(field_name, AMB_NAME, field_name_len) == 0) {
379         if (prop->name)
380             lua_pushstring(L, prop->name);
381         else
382             goto error;
383     }
384     else if (strncmp(field_name, AMB_HANDLER, field_name_len) == 0) {
385         if (prop->handler_ref != LUA_NOREF)
386             lua_rawgeti(L, LUA_REGISTRYINDEX, prop->handler_ref);
387         else
388             goto error;
389     }
390     else if (strncmp(field_name, AMB_DBUS_DATA, field_name_len) == 0) {
391         lua_newtable(L);
392
393         lua_pushstring(L, AMB_OBJECT);
394         lua_pushstring(L, prop->dbus_data.obj);
395         lua_settable(L, -3);
396
397         lua_pushstring(L, AMB_INTERFACE);
398         lua_pushstring(L, prop->dbus_data.iface);
399         lua_settable(L, -3);
400
401         lua_pushstring(L, AMB_MEMBER);
402         lua_pushstring(L, prop->dbus_data.name);
403         lua_settable(L, -3);
404
405         lua_pushstring(L, "signature");
406         lua_pushstring(L, prop->dbus_data.sig);
407         lua_settable(L, -3);
408     }
409     else if (strncmp(field_name, "basic_table_name", field_name_len) == 0) {
410         if (prop->basic_table_name)
411             lua_pushstring(L, prop->basic_table_name);
412         else
413             goto error;
414     }
415     else if (strncmp(field_name, AMB_OUTPUTS, field_name_len) == 0) {
416         if (prop->outputs_ref != LUA_NOREF)
417             lua_rawgeti(L, LUA_REGISTRYINDEX, prop->outputs_ref);
418         else
419             goto error;
420     }
421     else {
422         goto error;
423     }
424
425     MRP_LUA_LEAVE(1);
426
427 error:
428     lua_pushnil(L);
429     MRP_LUA_LEAVE(1);
430 }
431
432 static int amb_setfield(lua_State *L)
433 {
434     MRP_LUA_ENTER;
435
436     MRP_UNUSED(L);
437
438     mrp_log_info("> amb_setfield");
439
440     MRP_LUA_LEAVE(0);
441 }
442
443 #if 0
444 bool really_create_basic_handler(lua_State *L, void *data,
445                     const char *signature, mrp_funcbridge_value_t *args,
446                     char  *ret_type, mrp_funcbridge_value_t *ret_val)
447 {
448     mrp_log_info("> really_create_basic_handler");
449
450
451
452     return true;
453 }
454 #endif
455
456 /* lua config end */
457
458 static bool parse_elementary_value(lua_State *L,
459         DBusMessageIter *iter, dbus_property_watch_t *w)
460 {
461     dbus_int32_t i32_val;
462     dbus_int32_t i16_val;
463     dbus_uint32_t u32_val;
464     dbus_uint16_t u16_val;
465     uint8_t byte_val;
466     dbus_bool_t b_val;
467     double d_val;
468     char *s_val;
469
470     char sig;
471
472     MRP_UNUSED(w);
473
474     if (!iter)
475         goto error;
476
477     sig = dbus_message_iter_get_arg_type(iter);
478
479     switch (sig) {
480         case DBUS_TYPE_INT32:
481             dbus_message_iter_get_basic(iter, &i32_val);
482             lua_pushinteger(L, i32_val);
483             break;
484         case DBUS_TYPE_INT16:
485             dbus_message_iter_get_basic(iter, &i16_val);
486             lua_pushinteger(L, i16_val);
487             break;
488         case DBUS_TYPE_UINT32:
489             dbus_message_iter_get_basic(iter, &u32_val);
490             lua_pushinteger(L, u32_val);
491             break;
492         case DBUS_TYPE_UINT16:
493             dbus_message_iter_get_basic(iter, &u16_val);
494             lua_pushinteger(L, u16_val);
495             break;
496         case DBUS_TYPE_BOOLEAN:
497             dbus_message_iter_get_basic(iter, &b_val);
498             lua_pushboolean(L, b_val == TRUE);
499             break;
500         case DBUS_TYPE_BYTE:
501             dbus_message_iter_get_basic(iter, &byte_val);
502             lua_pushinteger(L, byte_val);
503             break;
504         case DBUS_TYPE_DOUBLE:
505             dbus_message_iter_get_basic(iter, &d_val);
506             lua_pushnumber(L, d_val);
507             break;
508         case DBUS_TYPE_STRING:
509             dbus_message_iter_get_basic(iter, &s_val);
510             lua_pushstring(L, s_val);
511             break;
512         default:
513             mrp_log_info("> parse_elementary_value: unknown type");
514             goto error;
515     }
516
517     return TRUE;
518
519 error:
520     return FALSE;
521 }
522
523 static bool parse_value(lua_State *L, DBusMessageIter *iter,
524         dbus_property_watch_t *w);
525
526 static bool parse_struct(lua_State *L,
527         DBusMessageIter *iter, dbus_property_watch_t *w)
528 {
529     int i = 1;
530     DBusMessageIter new_iter;
531
532     if (!iter)
533         return FALSE;
534
535     /* initialize the table */
536     lua_newtable(L);
537
538     dbus_message_iter_recurse(iter, &new_iter);
539
540     while (dbus_message_iter_get_arg_type(&new_iter) != DBUS_TYPE_INVALID) {
541
542         /* struct "index" */
543         lua_pushinteger(L, i++);
544
545         parse_value(L, &new_iter, w);
546         dbus_message_iter_next(&new_iter);
547
548         /* put the values to the table */
549         lua_settable(L, -3);
550     }
551
552     return TRUE;
553 }
554
555
556 static bool parse_dict_entry(lua_State *L,
557         DBusMessageIter *iter, dbus_property_watch_t *w)
558 {
559     DBusMessageIter new_iter;
560
561     if (!iter)
562         return FALSE;
563
564     dbus_message_iter_recurse(iter, &new_iter);
565
566     while (dbus_message_iter_get_arg_type(&new_iter) != DBUS_TYPE_INVALID) {
567
568         /* key must be elementary, value can be anything */
569
570         parse_elementary_value(L, &new_iter, w);
571         dbus_message_iter_next(&new_iter);
572
573         parse_value(L, &new_iter, w);
574         dbus_message_iter_next(&new_iter);
575
576         /* put the values to the table */
577         lua_settable(L, -3);
578     }
579
580     return TRUE;
581 }
582
583 static bool parse_array(lua_State *L,
584         DBusMessageIter *iter, dbus_property_watch_t *w)
585 {
586     DBusMessageIter new_iter;
587     int element_type;
588
589     if (!iter)
590         return FALSE;
591
592     /* the lua array */
593     lua_newtable(L);
594
595     element_type = dbus_message_iter_get_element_type(iter);
596
597     dbus_message_iter_recurse(iter, &new_iter);
598
599     /* the problem: if the value inside array is a dict entry, the
600      * indexing of elements need to be done with dict keys instead
601      * of numbers. */
602
603     if (element_type == DBUS_TYPE_DICT_ENTRY) {
604         while (dbus_message_iter_get_arg_type(&new_iter)
605             != DBUS_TYPE_INVALID) {
606
607             parse_dict_entry(L, &new_iter, w);
608             dbus_message_iter_next(&new_iter);
609         }
610     }
611
612     else {
613         int i = 1;
614
615         while (dbus_message_iter_get_arg_type(&new_iter)
616             != DBUS_TYPE_INVALID) {
617
618             /* array index */
619             lua_pushinteger(L, i++);
620
621             parse_value(L, &new_iter, w);
622             dbus_message_iter_next(&new_iter);
623
624             /* put the values to the table */
625             lua_settable(L, -3);
626         }
627     }
628
629     return TRUE;
630 }
631
632 static bool parse_value(lua_State *L, DBusMessageIter *iter,
633         dbus_property_watch_t *w)
634 {
635     char curr;
636
637     if (!iter)
638         return FALSE;
639
640     curr = dbus_message_iter_get_arg_type(iter);
641
642     switch (curr) {
643         case DBUS_TYPE_BYTE:
644         case DBUS_TYPE_BOOLEAN:
645         case DBUS_TYPE_INT16:
646         case DBUS_TYPE_INT32:
647         case DBUS_TYPE_UINT16:
648         case DBUS_TYPE_UINT32:
649         case DBUS_TYPE_DOUBLE:
650         case DBUS_TYPE_STRING:
651             return parse_elementary_value(L, iter, w);
652         case DBUS_TYPE_ARRAY:
653             return parse_array(L, iter, w);
654         case DBUS_TYPE_STRUCT:
655             return parse_struct(L, iter, w);
656         case DBUS_TYPE_DICT_ENTRY:
657             goto error; /* these are handled from parse_array */
658         case DBUS_TYPE_INVALID:
659             return TRUE;
660         default:
661             break;
662     }
663
664 error:
665     mrp_log_error("failed to parse D-Bus property (sig[i] %c)", curr);
666     return FALSE;
667 }
668
669 static void lua_property_handler(DBusMessage *msg, dbus_property_watch_t *w)
670 {
671     DBusMessageIter msg_iter;
672     DBusMessageIter variant_iter;
673     char *variant_sig = NULL;
674
675     if (!w || !msg)
676         goto error;
677
678     if (dbus_message_get_type(msg) == DBUS_MESSAGE_TYPE_ERROR)
679         goto error;
680
681     dbus_message_iter_init(msg, &msg_iter);
682
683     if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT)
684         goto error;
685
686     dbus_message_iter_recurse(&msg_iter, &variant_iter);
687
688     variant_sig = dbus_message_iter_get_signature(&variant_iter);
689
690     if (!variant_sig)
691         goto error;
692
693     /*
694     mrp_log_info("iter sig: %s, expected: %s",
695             variant_sig, w->lua_prop->dbus_data.sig);
696     */
697
698     /* check if we got what we were expecting */
699     if (strcmp(variant_sig, w->lua_prop->dbus_data.sig) != 0)
700         goto error;
701
702     if (w->lua_prop->handler_ref == LUA_NOREF)
703         goto error;
704
705     /* load the function pointer to the stack */
706     lua_rawgeti(w->ctx->L, LUA_REGISTRYINDEX, w->lua_prop->handler_ref);
707
708     /* "self" parameter */
709     mrp_lua_push_object(w->ctx->L, w->lua_prop);
710
711     /* parse values to the stack */
712     parse_value(w->ctx->L, &variant_iter, w);
713
714     /* call the handler function */
715     lua_pcall(w->ctx->L, 2, 0, 0);
716
717     dbus_free(variant_sig);
718
719     return;
720
721 error:
722     if (variant_sig)
723         dbus_free(variant_sig);
724     mrp_log_error("amb: failed to process an incoming D-Bus message");
725 }
726
727
728 static void basic_property_handler(DBusMessage *msg, dbus_property_watch_t *w)
729 {
730     DBusMessageIter msg_iter;
731     DBusMessageIter variant_iter;
732
733     dbus_int32_t i32_val;
734     dbus_int32_t i16_val;
735     dbus_uint32_t u32_val;
736     dbus_uint16_t u16_val;
737     uint8_t byte_val;
738     dbus_bool_t b_val;
739     double d_val;
740     char *s_val;
741
742     if (!w || !msg)
743         goto error;
744
745     dbus_message_iter_init(msg, &msg_iter);
746
747     if (dbus_message_iter_get_arg_type(&msg_iter) != DBUS_TYPE_VARIANT)
748         goto error;
749
750     dbus_message_iter_recurse(&msg_iter, &variant_iter);
751
752     if (dbus_message_iter_get_arg_type(&variant_iter)
753                         != w->prop.type)
754         goto error;
755
756     switch (w->prop.type) {
757         case DBUS_TYPE_INT32:
758             dbus_message_iter_get_basic(&variant_iter, &i32_val);
759             w->prop.value.i = i32_val;
760             break;
761         case DBUS_TYPE_INT16:
762             dbus_message_iter_get_basic(&variant_iter, &i16_val);
763             w->prop.value.i = i16_val;
764             break;
765         case DBUS_TYPE_UINT32:
766             dbus_message_iter_get_basic(&variant_iter, &u32_val);
767             w->prop.value.u = u32_val;
768             break;
769         case DBUS_TYPE_UINT16:
770             dbus_message_iter_get_basic(&variant_iter, &u16_val);
771             w->prop.value.u = u16_val;
772             break;
773         case DBUS_TYPE_BOOLEAN:
774             dbus_message_iter_get_basic(&variant_iter, &b_val);
775             w->prop.value.u = b_val;
776             break;
777         case DBUS_TYPE_BYTE:
778             dbus_message_iter_get_basic(&variant_iter, &byte_val);
779             w->prop.value.u = byte_val;
780             break;
781         case DBUS_TYPE_DOUBLE:
782             dbus_message_iter_get_basic(&variant_iter, &d_val);
783             w->prop.value.f = d_val;
784             break;
785         case DBUS_TYPE_STRING:
786             dbus_message_iter_get_basic(&variant_iter, &s_val);
787             w->prop.value.s = mrp_strdup(s_val);
788             break;
789         default:
790             goto error;
791     }
792
793     if (w->cb)
794         w->cb(&w->prop, w->user_data);
795
796     return;
797
798 error:
799     mrp_log_error("amb: failed to parse property value");
800     return;
801 }
802
803 static int property_signal_handler(mrp_dbus_t *dbus, DBusMessage *msg,
804         void *data)
805 {
806     dbus_property_watch_t *w = data;
807
808     MRP_UNUSED(dbus);
809
810     mrp_log_info("amb: received property signal");
811
812     if (w->tdata) {
813         basic_property_handler(msg, w);
814     }
815     else {
816         lua_property_handler(msg, w);
817     }
818
819     return TRUE;
820 }
821
822 static void property_reply_handler(mrp_dbus_t *dbus, DBusMessage *msg,
823         void *data)
824 {
825     dbus_property_watch_t *w = data;
826
827     MRP_UNUSED(dbus);
828
829     mrp_log_info("amb: received property method reply");
830
831     if (w->tdata) {
832         basic_property_handler(msg, w);
833     }
834     else {
835         lua_property_handler(msg, w);
836     }}
837
838
839 static int subscribe_property(data_t *ctx, dbus_property_watch_t *w)
840 {
841     const char *obj = w->lua_prop->dbus_data.obj;
842     const char *iface = w->lua_prop->dbus_data.iface;
843     const char *name = w->lua_prop->dbus_data.name;
844
845     mrp_log_info("subscribing to signal '%s.%s' at '%s'",
846             iface, name, obj);
847
848     mrp_dbus_subscribe_signal(ctx->dbus, property_signal_handler, w, NULL,
849             obj, iface, name, NULL);
850
851     /* Ok, now we are listening to property changes. Let's get the initial
852      * value. */
853
854     mrp_dbus_call(ctx->dbus,
855             ctx->amb_addr, obj,
856             "org.freedesktop.DBus.Properties",
857             "Get", 3000, property_reply_handler, w,
858             DBUS_TYPE_STRING, &iface,
859             DBUS_TYPE_STRING, &name,
860             DBUS_TYPE_INVALID);
861
862     return 0;
863 }
864
865
866 static void print_basic_property(dbus_basic_property_t *prop)
867 {
868     switch (prop->type) {
869         case DBUS_TYPE_INT32:
870         case DBUS_TYPE_INT16:
871             mrp_log_info("Property %s : %i", prop->name, prop->value.i);
872             break;
873         case DBUS_TYPE_UINT32:
874         case DBUS_TYPE_UINT16:
875         case DBUS_TYPE_BOOLEAN:
876         case DBUS_TYPE_BYTE:
877             mrp_log_info("Property %s : %u", prop->name, prop->value.u);
878             break;
879         case DBUS_TYPE_DOUBLE:
880             mrp_log_info("Property %s : %f", prop->name, prop->value.f);
881             break;
882         case DBUS_TYPE_STRING:
883             mrp_log_info("Property %s : %s", prop->name, prop->value.s);
884             break;
885         default:
886             mrp_log_error("Unknown value in property");
887     }
888 }
889
890 static void basic_property_updated(dbus_basic_property_t *prop, void *userdata)
891 {
892     char buf[512];
893     int buflen;
894     mql_result_t *r;
895     dbus_property_watch_t *w = userdata;
896     basic_table_data_t *tdata = w->tdata;
897     mqi_handle_t tx;
898
899     mrp_log_info("> basic_property_updated");
900
901     print_basic_property(prop);
902
903     tx = mqi_begin_transaction();
904
905     if (!prop->initialized) {
906
907         switch (tdata->type) {
908             case mqi_string:
909                 buflen = snprintf(buf, 512, "INSERT INTO %s VALUES (1, '%s', %s)",
910                     w->lua_prop->basic_table_name, prop->name, prop->value.s);
911                 break;
912             case mqi_integer:
913                 buflen = snprintf(buf, 512, "INSERT INTO %s VALUES (1, '%s', %d)",
914                     w->lua_prop->basic_table_name, prop->name, prop->value.i);
915                 break;
916             case mqi_unsignd:
917                 buflen = snprintf(buf, 512, "INSERT INTO %s VALUES (1, '%s', %u)",
918                     w->lua_prop->basic_table_name, prop->name, prop->value.u);
919                 break;
920             case mqi_floating:
921                 buflen = snprintf(buf, 512, "INSERT INTO %s VALUES (1, '%s', %f)",
922                     w->lua_prop->basic_table_name, prop->name, prop->value.f);
923                 break;
924             default:
925                 goto end;
926         }
927
928         if (buflen <= 0 || buflen == 512) {
929             goto end;
930         }
931
932         r = mql_exec_string(mql_result_string, buf);
933
934         prop->initialized = TRUE;
935     }
936     else {
937         int ret;
938
939         switch (tdata->type) {
940             case mqi_string:
941                 ret = mql_bind_value(tdata->update_operation, 1, tdata->type,
942                         prop->value.s);
943                 break;
944             case mqi_integer:
945                 ret = mql_bind_value(tdata->update_operation, 1, tdata->type,
946                         prop->value.i);
947                 break;
948             case mqi_unsignd:
949                 ret = mql_bind_value(tdata->update_operation, 1, tdata->type,
950                         prop->value.u);
951                 break;
952             case mqi_floating:
953                 ret = mql_bind_value(tdata->update_operation, 1, tdata->type,
954                         prop->value.f);
955                 break;
956             default:
957                 goto end;
958         }
959
960         if (ret < 0) {
961             mrp_log_error("failed to bind value to update operation");
962             goto end;
963         }
964
965         r = mql_exec_statement(mql_result_string, tdata->update_operation);
966     }
967
968     mrp_log_info("amb: %s", mql_result_is_success(r) ? "updated database" :
969             mql_result_error_get_message(r));
970
971     mql_result_free(r);
972
973 end:
974     mqi_commit_transaction(tx);
975 }
976
977 static void delete_basic_table_data(basic_table_data_t *tdata)
978 {
979     if (!tdata)
980         return;
981
982     if (tdata->update_operation)
983         mql_statement_free(tdata->update_operation);
984
985     if (tdata->table)
986         mqi_drop_table(tdata->table);
987
988     mrp_free(tdata);
989 }
990
991 static basic_table_data_t *create_basic_property_table(const char *table_name,
992         const char *member, int type)
993 {
994     char buf[512];
995     char *update_format;
996     /* char *insert_format; */
997     basic_table_data_t *tdata = NULL;
998     int ret;
999
1000     if (strlen(member) > 64)
1001         goto error;
1002
1003     tdata = mrp_allocz(sizeof(basic_table_data_t));
1004
1005     if (!tdata)
1006         goto error;
1007
1008     switch (type) {
1009         case DBUS_TYPE_INT32:
1010         case DBUS_TYPE_INT16:
1011             tdata->type = mqi_integer;
1012             update_format = "%d";
1013             /* insert_format = "%d"; */
1014             break;
1015         case DBUS_TYPE_UINT32:
1016         case DBUS_TYPE_UINT16:
1017         case DBUS_TYPE_BOOLEAN:
1018         case DBUS_TYPE_BYTE:
1019             tdata->type = mqi_unsignd;
1020             update_format = "%u";
1021             /* insert_format = "%u"; */
1022             break;
1023         case DBUS_TYPE_DOUBLE:
1024             tdata->type = mqi_floating;
1025             update_format = "%f";
1026             /* insert_format = "%f"; */
1027             break;
1028         case DBUS_TYPE_STRING:
1029             tdata->type = mqi_varchar;
1030             update_format = "%s";
1031             /* insert_format = "'%s'"; */
1032             break;
1033         default:
1034             mrp_log_error("unknown type %d", type);
1035             goto error;
1036     }
1037
1038     tdata->defs[0].name = "id";
1039     tdata->defs[0].type = mqi_unsignd;
1040     tdata->defs[0].length = 0;
1041     tdata->defs[0].flags = 0;
1042
1043     tdata->defs[1].name = "key";
1044     tdata->defs[1].type = mqi_varchar;
1045     tdata->defs[1].length = 64;
1046     tdata->defs[1].flags = 0;
1047
1048     tdata->defs[2].name = "value";
1049     tdata->defs[2].type = tdata->type;
1050     tdata->defs[2].length = (tdata->type == mqi_varchar) ? 128 : 0;
1051     tdata->defs[2].flags = 0;
1052
1053     memset(&tdata->defs[3], 0, sizeof(tdata->defs[3]));
1054
1055     tdata->table = MQI_CREATE_TABLE((char *) table_name, MQI_TEMPORARY,
1056             tdata->defs, NULL);
1057
1058     if (!tdata->table) {
1059         mrp_log_error("creating table '%s' failed", table_name);
1060         goto error;
1061     }
1062
1063     ret = snprintf(buf, 512, "UPDATE %s SET value = %s where id = 1",
1064             table_name, update_format);
1065
1066     if (ret <= 0 || ret == 512) {
1067         goto error;
1068     }
1069
1070     tdata->update_operation = mql_precompile(buf);
1071
1072     if (!tdata->update_operation) {
1073         mrp_log_error("buggy buf: '%s'", buf);
1074         goto error;
1075     }
1076
1077     mrp_log_info("amb: compiled update statement '%s'", buf);
1078
1079     return tdata;
1080
1081 error:
1082     mrp_log_error("amb: failed to create table %s", table_name);
1083     delete_basic_table_data(tdata);
1084     return NULL;
1085 }
1086
1087 static int load_config(lua_State *L, const char *path)
1088 {
1089     if (!luaL_loadfile(L, path) && !lua_pcall(L, 0, 0, 0))
1090         return TRUE;
1091     else {
1092         mrp_log_error("plugin-lua: failed to load config file %s.", path);
1093         mrp_log_error("%s", lua_tostring(L, -1));
1094         lua_settop(L, 0);
1095
1096         return FALSE;
1097     }
1098 }
1099
1100 static int amb_init(mrp_plugin_t *plugin)
1101 {
1102     data_t *ctx;
1103     mrp_plugin_arg_t *args = plugin->args;
1104
1105     ctx = mrp_allocz(sizeof(data_t));
1106
1107     if (!ctx)
1108         goto error;
1109
1110     plugin->data = ctx;
1111
1112     ctx->amb_addr = args[ARG_AMB_DBUS_ADDRESS].str;
1113     ctx->config_file = args[ARG_AMB_CONFIG_FILE].str;
1114
1115     mrp_log_info("amb dbus address: %s", ctx->amb_addr);
1116     mrp_log_info("amb config file: %s", ctx->config_file);
1117
1118     ctx->dbus = mrp_dbus_connect(plugin->ctx->ml, "system", NULL);
1119
1120     if (!ctx->dbus)
1121         goto error;
1122
1123     /* initialize lua support */
1124
1125     mrp_list_init(&ctx->lua_properties);
1126
1127     global_ctx = ctx;
1128
1129     ctx->L = mrp_lua_get_lua_state();
1130
1131     if (!ctx->L)
1132         goto error;
1133
1134     mrp_lua_create_object_class(ctx->L, MRP_LUA_CLASS(amb, property));
1135
1136     /* TODO: create here a "manager" lua object and put that to the global
1137      * lua table? This one then has a pointer to the C context. */
1138
1139 #if 0
1140     mrp_funcbridge_create_cfunc(L, "create_basic_handler", "amb",
1141                                 really_create_basic_handler, (void *)0x1234);
1142 #endif
1143
1144     /* 1. read the configuration file. The configuration must tell
1145             - target object (/org/automotive/runningstatus/vehicleSpeed)
1146             - target interface (org.automotive.vehicleSpeed)
1147             - target member (VehicleSpeed)
1148             - target type (int32_t)
1149             - destination table
1150      */
1151
1152     load_config(ctx->L, ctx->config_file);
1153
1154     return TRUE;
1155
1156 error:
1157     /* TODO */
1158     return FALSE;
1159 }
1160
1161
1162 static void amb_exit(mrp_plugin_t *plugin)
1163 {
1164     data_t *ctx = plugin->data;
1165     mrp_list_hook_t *p, *n;
1166
1167     /* for all subscribed properties, unsubscribe and free memory */
1168
1169     mrp_list_foreach(&ctx->lua_properties, p, n) {
1170         dbus_property_watch_t *w =
1171                 mrp_list_entry(p, dbus_property_watch_t, hook);
1172
1173         destroy_prop(ctx, w);
1174     }
1175
1176     global_ctx = NULL;
1177 }
1178
1179 #define AMB_DESCRIPTION "A plugin for Automotive Message Broker D-Bus API."
1180 #define AMB_HELP        "Access Automotive Message Broker."
1181 #define AMB_VERSION     MRP_VERSION_INT(0, 0, 1)
1182 #define AMB_AUTHORS     "Ismo Puustinen <ismo.puustinen@intel.com>"
1183
1184 static mrp_plugin_arg_t args[] = {
1185     MRP_PLUGIN_ARGIDX(ARG_AMB_DBUS_ADDRESS, STRING, "amb_address",
1186             "org.automotive.message.broker"),
1187     MRP_PLUGIN_ARGIDX(ARG_AMB_CONFIG_FILE, STRING, "config_file",
1188             "/etc/murphy/plugins/amb/config.lua"),
1189 };
1190
1191
1192 MURPHY_REGISTER_PLUGIN("amb",
1193                        AMB_VERSION, AMB_DESCRIPTION,
1194                        AMB_AUTHORS, AMB_HELP,
1195                        MRP_SINGLETON, amb_init, amb_exit,
1196                        args, MRP_ARRAY_SIZE(args),
1197                        NULL, 0, NULL, 0, NULL);