amb: free more memory.
[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
31 #include <sys/types.h>
32 #include <unistd.h>
33 #include <sys/stat.h>
34
35 #include <murphy/common.h>
36 #include <murphy/core.h>
37 #include <murphy/common/dbus-libdbus.h>
38 #include <murphy/common/process.h>
39
40 #include <murphy-db/mdb.h>
41 #include <murphy-db/mqi.h>
42 #include <murphy-db/mql.h>
43
44 #include <murphy/core/lua-utils/object.h>
45 #include <murphy/core/lua-bindings/murphy.h>
46 #include <murphy/core/lua-utils/funcbridge.h>
47 #include <murphy/core/lua-decision/mdb.h>
48 #include <murphy/core/lua-decision/element.h>
49
50 enum {
51     ARG_AMB_DBUS_ADDRESS,
52     ARG_AMB_DBUS_BUS,
53     ARG_AMB_CONFIG_FILE,
54     ARG_AMB_ID,
55     ARG_AMB_TPORT_ADDRESS,
56     ARG_AMB_STARTUP_DELAY,
57     ARG_AMB_FORCE_SUBSCRIPTION,
58 };
59
60 enum amb_type {
61     amb_string = 's',
62     amb_bool   = 'b',
63     amb_int32  = 'i',
64     amb_int16  = 'n',
65     amb_uint32 = 'u',
66     amb_uint16 = 'q',
67     amb_byte   = 'y',
68     amb_double = 'd',
69 };
70
71 #define AMB_NAME                "name"
72 #define AMB_HANDLER             "handler"
73 #define AMB_DBUS_DATA           "dbus_data"
74 #define AMB_OBJECT              "obj"
75 #define AMB_INTERFACE           "interface"
76 #define AMB_MEMBER              "property"
77 #define AMB_OBJECTNAME          "objectname"
78 #define AMB_SIGNATURE           "signature"
79 #define AMB_BASIC_TABLE_NAME    "basic_table_name"
80 #define AMB_OUTPUTS             "outputs"
81
82
83 /*
84 signal sender=:1.35 -> dest=(null destination) serial=716 path=/c0ffee8ac6054a06903459c1deadbeef/0/Transmission; interface=org.automotive.Transmission; member=GearPositionChanged
85    variant       int32 6
86    double 1.38978e+09
87 signal sender=:1.35 -> dest=(null destination) serial=717 path=/c0ffee8ac6054a06903459c1deadbeef/0/Transmission; interface=org.freedesktop.DBus.Properties; member=PropertiesChanged
88    string "org.automotive.Transmission"
89    array [
90       dict entry(
91          string "GearPosition"
92          variant             int32 6
93       )
94       dict entry(
95          string "GearPositionSequence"
96          variant             int32 -1
97       )
98       dict entry(
99          string "Time"
100          variant             double 1.38978e+09
101       )
102    ]
103    array [
104    ]
105
106 signal sender=:1.35 -> dest=(null destination) serial=718 path=/c0ffee8ac6054a06903459c1deadbeef/0/VehicleSpeed; interface=org.automotive.VehicleSpeed; member=VehicleSpeedChanged
107    variant       uint16 0
108    double 1.38978e+09
109 signal sender=:1.35 -> dest=(null destination) serial=719 path=/c0ffee8ac6054a06903459c1deadbeef/0/VehicleSpeed; interface=org.freedesktop.DBus.Properties; member=PropertiesChanged
110    string "org.automotive.VehicleSpeed"
111    array [
112       dict entry(
113          string "VehicleSpeed"
114          variant             uint16 0
115       )
116       dict entry(
117          string "VehicleSpeedSequence"
118          variant             int32 -1
119       )
120       dict entry(
121          string "Time"
122          variant             double 1.38978e+09
123       )
124    ]
125    array [
126    ]
127 */
128
129 typedef struct {
130     char *name;
131     mrp_dbus_type_t type;
132     union {
133         int32_t i;
134         uint32_t u;
135         double f;
136         char *s;
137     } value;
138     bool initialized;
139 } dbus_basic_property_t;
140
141 typedef void (*property_updated_cb_t)(dbus_basic_property_t *property,
142         void *user_data);
143
144 typedef struct {
145     struct {
146         char *obj;
147         char *iface;
148         char *name;            /* property name ("GearPosition") */
149         char *objectname;      /* amb object to query ("Transmission") */
150         char *signature;
151         bool undefined_object_path;
152     } dbus_data;
153     char *name;
154     char *basic_table_name;
155     int handler_ref;
156     int outputs_ref;
157 } lua_amb_property_t;
158
159 typedef struct {
160     mrp_dbus_t *dbus;
161     const char *amb_addr;
162     const char *config_file;
163     const char *amb_id;
164     const char *tport_addr;
165     bool force_subscription;
166     lua_State *L;
167     mrp_list_hook_t lua_properties;
168     mrp_htbl_t *dbus_property_objects; /* path to dbus_property_object_t */
169
170     mrp_process_state_t amb_state;
171     uint32_t amb_startup_delay;
172
173     mrp_mainloop_t *ml;
174
175     mrp_transport_t *lt;
176     mrp_transport_t *t;
177 } data_t;
178
179 typedef struct {
180     mqi_column_def_t defs[4];
181
182     mql_statement_t *update_operation;
183     mqi_data_type_t type;
184     mqi_handle_t table;
185 } basic_table_data_t;
186
187 typedef struct {
188     /* PropertiesChanged signals are received by the object and may contain
189      * several properties that are changed. Hence we'll need to represent that
190      * with a custom datatype. */
191
192     /* htbl backpointer for freeing */
193     char *path;
194
195     /* list of properties that belong to this object */
196     mrp_htbl_t *dbus_properties; /* interface+property to dbus_property_watch_t */
197
198     mrp_list_hook_t hook;
199 } dbus_property_object_t;
200
201 typedef struct {
202     dbus_basic_property_t prop;
203     dbus_property_object_t *o; /* top level */
204
205     property_updated_cb_t cb;
206     void *user_data;
207
208     lua_amb_property_t *lua_prop;
209
210     /* for basic tables that we manage ourselves */
211     basic_table_data_t *tdata;
212
213     /* htbl backpointer for freeing */
214     char *key;
215
216     mrp_list_hook_t hook;
217     data_t *ctx;
218 } dbus_property_watch_t;
219
220 static data_t *global_ctx = NULL;
221
222 static basic_table_data_t *create_basic_property_table(const char *table_name,
223         const char *member, int type);
224 static void delete_basic_table_data(basic_table_data_t *tdata);
225 static int find_property_object(data_t *ctx, dbus_property_watch_t *w,
226         const char *prop);
227 static int subscribe_property(data_t *ctx, dbus_property_watch_t *w);
228 static void basic_property_updated(dbus_basic_property_t *prop, void *userdata);
229 static int create_transport(mrp_mainloop_t *ml, data_t *ctx);
230 static int property_signal_handler(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
231         void *data);
232
233 /* Lua config */
234
235 static int amb_constructor(lua_State *L);
236 static int amb_setfield(lua_State *L);
237 static int amb_getfield(lua_State *L);
238 static void lua_amb_destroy(void *data);
239
240 #define PROPERTY_CLASS MRP_LUA_CLASS(amb, property)
241
242 MRP_LUA_METHOD_LIST_TABLE (
243     amb_methods,          /* methodlist name */
244     MRP_LUA_METHOD_CONSTRUCTOR  (amb_constructor)
245 );
246
247 MRP_LUA_METHOD_LIST_TABLE (
248     amb_overrides,     /* methodlist name */
249     MRP_LUA_OVERRIDE_CALL       (amb_constructor)
250     MRP_LUA_OVERRIDE_GETFIELD   (amb_getfield)
251     MRP_LUA_OVERRIDE_SETFIELD   (amb_setfield)
252 );
253
254 MRP_LUA_CLASS_DEF (
255     amb,                /* main class name */
256     property,           /* constructor name */
257     lua_amb_property_t, /* userdata type */
258     lua_amb_destroy,    /* userdata destructor */
259     amb_methods,        /* class methods */
260     amb_overrides       /* class overrides */
261 );
262
263
264
265 static void lua_amb_destroy(void *data)
266 {
267     lua_amb_property_t *prop = (lua_amb_property_t *)data;
268
269     MRP_LUA_ENTER;
270
271     mrp_log_info("AMB: lua_amb_destroy");
272
273     MRP_UNUSED(prop);
274
275     MRP_LUA_LEAVE_NOARG;
276 }
277
278
279 static int count_keys_cb(void *key, void *object, void *user_data)
280 {
281     int *count = user_data;
282
283     MRP_UNUSED(key);
284     MRP_UNUSED(object);
285
286     *count = *count + 1;
287
288     return MRP_HTBL_ITER_MORE;
289 }
290
291
292 static void destroy_prop(data_t *ctx, dbus_property_watch_t *w)
293 {
294     dbus_property_object_t *o = w->o;
295     int len = 0;
296
297     /* remove property from the object */
298
299     mrp_htbl_remove(o->dbus_properties, w->key, FALSE);
300
301     /* check if no-one is interested in the signals o receives */
302
303     mrp_htbl_foreach(o->dbus_properties, count_keys_cb, &len);
304
305     if (len == 0) {
306         mrp_dbus_unsubscribe_signal(ctx->dbus, property_signal_handler, o,
307                 NULL, o->path,
308                 "org.freedesktop.DBus.Properties",
309                 "PropertiesChanged", NULL);
310         mrp_htbl_remove(ctx->dbus_property_objects, o->path, TRUE);
311     }
312
313     mrp_free(w->key);
314
315     /* delete the table data */
316
317     if (w->tdata)
318         delete_basic_table_data(w->tdata);
319
320     if (w->lua_prop) {
321         /* TODO */
322         char *name = w->lua_prop->name;
323         mrp_free(w->lua_prop->basic_table_name);
324         mrp_free(w->lua_prop->dbus_data.iface);
325         mrp_free(w->lua_prop->dbus_data.name);
326         mrp_free(w->lua_prop->dbus_data.obj);
327         mrp_free(w->lua_prop->dbus_data.objectname);
328         mrp_free(w->lua_prop->dbus_data.signature);
329
330         mrp_lua_destroy_object(ctx->L, name, 0, w->lua_prop);
331
332         mrp_free(name);
333     }
334
335     mrp_free(w);
336 }
337
338
339 static int amb_constructor(lua_State *L)
340 {
341     lua_amb_property_t *prop;
342     size_t field_name_len = 0;
343     const char *field_name;
344     data_t *ctx = global_ctx;
345     dbus_property_watch_t *w = NULL;
346     char *error = "unknown error";
347
348     MRP_LUA_ENTER;
349
350     mrp_debug("AMB: amb_constructor, stack size: %d", lua_gettop(L));
351
352     prop = (lua_amb_property_t *)
353             mrp_lua_create_object(L, PROPERTY_CLASS, NULL, 0);
354
355     if (!prop)
356         goto error;
357
358     prop->handler_ref = LUA_NOREF;
359     prop->outputs_ref = LUA_NOREF;
360
361     MRP_LUA_FOREACH_FIELD(L, 2, field_name, field_name_len) {
362         char buf[field_name_len+1];
363
364         strncpy(buf, field_name, field_name_len);
365         buf[field_name_len] = '\0';
366
367         /* mrp_log_info("field name: %s", buf); */
368
369         if (strncmp(field_name, "dbus_data", field_name_len) == 0) {
370
371             luaL_checktype(L, -1, LUA_TTABLE);
372
373             lua_pushnil(L);
374
375             while (lua_next(L, -2)) {
376
377                 const char *key;
378                 const char *value;
379
380                 luaL_checktype(L, -2, LUA_TSTRING);
381                 luaL_checktype(L, -1, LUA_TSTRING);
382
383                 key = lua_tostring(L, -2);
384                 value = lua_tostring(L, -1);
385
386                 mrp_log_info("%s -> %s", key, value);
387
388                 if (!key || !value) {
389                     error = "key or value undefined";
390                     goto error;
391                 }
392
393                 if (strcmp(key, AMB_SIGNATURE) == 0) {
394                     prop->dbus_data.signature = mrp_strdup(value);
395                 }
396                 else if (strcmp(key, AMB_MEMBER) == 0) {
397                     prop->dbus_data.name = mrp_strdup(value);
398                 }
399                 else if (strcmp(key, AMB_OBJECTNAME) == 0) {
400                     prop->dbus_data.objectname = mrp_strdup(value);
401                 }
402                 else if (strcmp(key, AMB_OBJECT) == 0) {
403                     if (strcmp(value, "undefined") == 0) {
404                         /* need to query AMB for finding the object path */
405                         mrp_log_info("querying AMB for correct path");
406                         prop->dbus_data.undefined_object_path = TRUE;
407                         prop->dbus_data.obj = NULL;
408                     }
409                     else {
410                         prop->dbus_data.undefined_object_path = FALSE;
411                         prop->dbus_data.obj = mrp_strdup(value);
412                     }
413                 }
414                 else if (strcmp(key, AMB_INTERFACE) == 0) {
415                     prop->dbus_data.iface = mrp_strdup(value);
416                 }
417                 else {
418                     error = "unknown key";
419                     goto error;
420                 }
421
422                 lua_pop(L, 1);
423             }
424
425             if (prop->dbus_data.objectname == NULL)
426                 prop->dbus_data.objectname = mrp_strdup(prop->dbus_data.name);
427
428             /* check that we have all necessary data */
429             if (prop->dbus_data.signature == NULL ||
430                 prop->dbus_data.iface == NULL ||
431                 prop->dbus_data.name == NULL ||
432                 prop->dbus_data.objectname == NULL ||
433                 (!prop->dbus_data.undefined_object_path &&
434                  prop->dbus_data.obj == NULL)) {
435                 error = "missing data";
436                 goto error;
437             }
438         }
439         else if (strncmp(field_name, "handler", field_name_len) == 0) {
440             luaL_checktype(L, -1, LUA_TFUNCTION);
441             prop->handler_ref = luaL_ref(L, LUA_REGISTRYINDEX);
442             lua_pushnil(L); /* need two items on the stack */
443         }
444         else if (strncmp(field_name, AMB_NAME, field_name_len) == 0) {
445             luaL_checktype(L, -1, LUA_TSTRING);
446             prop->name = mrp_strdup(lua_tostring(L, -1));
447             mrp_lua_set_object_name(L, PROPERTY_CLASS, prop->name);
448         }
449         else if (strncmp(field_name, "basic_table_name", field_name_len) == 0) {
450             luaL_checktype(L, -1, LUA_TSTRING);
451             prop->basic_table_name = mrp_strdup(lua_tostring(L, -1));
452         }
453         else if (strncmp(field_name, AMB_OUTPUTS, field_name_len) == 0) {
454             prop->outputs_ref = luaL_ref(L, LUA_REGISTRYINDEX);
455             lua_pushnil(L); /* need two items on the stack */
456         }
457     }
458
459     if (!prop->name) {
460         error = "missing property name";
461         goto error;
462     }
463
464     if (prop->handler_ref == LUA_NOREF && !prop->basic_table_name) {
465         error = "missing table name";
466         goto error;
467     }
468
469     w = (dbus_property_watch_t *) mrp_allocz(sizeof(dbus_property_watch_t));
470
471     if (!w) {
472         error = "out of memory";
473         goto error;
474     }
475
476     w->ctx = ctx;
477     w->lua_prop = prop;
478     w->prop.initialized = FALSE;
479     w->prop.type = MRP_DBUS_TYPE_INVALID;
480     w->prop.name = mrp_strdup(w->lua_prop->dbus_data.name);
481
482     if (!w->prop.name) {
483         error = "missing watch property name";
484         goto error;
485     }
486
487     if (prop->handler_ref == LUA_NOREF) {
488         basic_table_data_t *tdata;
489
490         w->prop.type = w->lua_prop->dbus_data.signature[0]; /* FIXME */
491
492         tdata = create_basic_property_table(prop->basic_table_name,
493             prop->dbus_data.name, w->prop.type);
494
495         if (!tdata) {
496             error = "could not create table data";
497             goto error;
498         }
499
500         w->tdata = tdata;
501
502         w->cb = basic_property_updated;
503         w->user_data = w;
504     }
505
506     mrp_list_init(&w->hook);
507
508     mrp_list_append(&ctx->lua_properties, &w->hook);
509
510     /* TODO: need some mapping? or custom property_watch? */
511
512     /* TODO: put the object to a global table or not? maybe better to just
513      * unload them when the plugin is unloaded. */
514
515     mrp_lua_push_object(L, prop);
516
517     MRP_LUA_LEAVE(1);
518
519 error:
520     destroy_prop(global_ctx, w);
521
522     mrp_log_error("AMB: amb_constructor error: %s", error);
523     MRP_LUA_LEAVE(0);
524 }
525
526 static int amb_getfield(lua_State *L)
527 {
528     lua_amb_property_t *prop = (lua_amb_property_t *)
529             mrp_lua_check_object(L, PROPERTY_CLASS, 1);
530     size_t field_name_len;
531     const char *field_name = lua_tolstring(L, 2, &field_name_len);
532
533     MRP_LUA_ENTER;
534
535     if (!prop)
536         goto error;
537
538     mrp_debug("AMB: amb_getfield");
539
540     if (strncmp(field_name, AMB_NAME, field_name_len) == 0) {
541         if (prop->name)
542             lua_pushstring(L, prop->name);
543         else
544             goto error;
545     }
546     else if (strncmp(field_name, AMB_HANDLER, field_name_len) == 0) {
547         if (prop->handler_ref != LUA_NOREF)
548             lua_rawgeti(L, LUA_REGISTRYINDEX, prop->handler_ref);
549         else
550             goto error;
551     }
552     else if (strncmp(field_name, AMB_DBUS_DATA, field_name_len) == 0) {
553         lua_newtable(L);
554
555         lua_pushstring(L, AMB_OBJECT);
556         if (prop->dbus_data.obj)
557             lua_pushstring(L, prop->dbus_data.obj);
558         else
559             lua_pushstring(L, "undefined");
560         lua_settable(L, -3);
561
562         lua_pushstring(L, AMB_INTERFACE);
563         lua_pushstring(L, prop->dbus_data.iface);
564         lua_settable(L, -3);
565
566         lua_pushstring(L, AMB_MEMBER);
567         lua_pushstring(L, prop->dbus_data.name);
568         lua_settable(L, -3);
569         lua_pushstring(L, AMB_OBJECTNAME);
570         lua_pushstring(L, prop->dbus_data.objectname);
571         lua_settable(L, -3);
572
573         lua_pushstring(L, AMB_SIGNATURE);
574         lua_pushstring(L, prop->dbus_data.signature);
575         lua_settable(L, -3);
576     }
577     else if (strncmp(field_name, "basic_table_name", field_name_len) == 0) {
578         if (prop->basic_table_name)
579             lua_pushstring(L, prop->basic_table_name);
580         else
581             goto error;
582     }
583     else if (strncmp(field_name, AMB_OUTPUTS, field_name_len) == 0) {
584         if (prop->outputs_ref != LUA_NOREF)
585             lua_rawgeti(L, LUA_REGISTRYINDEX, prop->outputs_ref);
586         else
587             goto error;
588     }
589     else {
590         goto error;
591     }
592
593     MRP_LUA_LEAVE(1);
594
595 error:
596     lua_pushnil(L);
597     MRP_LUA_LEAVE(1);
598 }
599
600 static int amb_setfield(lua_State *L)
601 {
602     MRP_LUA_ENTER;
603
604     MRP_UNUSED(L);
605
606     mrp_debug("AMB: amb_setfield");
607
608     MRP_LUA_LEAVE(0);
609 }
610
611 /* lua config end */
612
613 static bool parse_elementary_value(lua_State *L,
614         mrp_dbus_msg_t *msg, dbus_property_watch_t *w)
615 {
616     int32_t i32_val;
617     int16_t i16_val;
618     uint32_t u32_val;
619     uint16_t u16_val;
620     uint8_t byte_val;
621     double d_val;
622     char *s_val;
623
624     mrp_dbus_type_t sig;
625
626     MRP_UNUSED(w);
627
628     sig = mrp_dbus_msg_arg_type(msg, NULL);
629
630     switch (sig) {
631         case MRP_DBUS_TYPE_INT32:
632             mrp_dbus_msg_read_basic(msg, sig, &i32_val);
633             lua_pushinteger(L, i32_val);
634             break;
635         case MRP_DBUS_TYPE_INT16:
636             mrp_dbus_msg_read_basic(msg, sig, &i16_val);
637             lua_pushinteger(L, i16_val);
638             break;
639         case MRP_DBUS_TYPE_UINT32:
640             mrp_dbus_msg_read_basic(msg, sig, &u32_val);
641             lua_pushinteger(L, u32_val);
642             break;
643         case MRP_DBUS_TYPE_UINT16:
644             mrp_dbus_msg_read_basic(msg, sig, &u16_val);
645             lua_pushinteger(L, u16_val);
646             break;
647         case MRP_DBUS_TYPE_BOOLEAN:
648             mrp_dbus_msg_read_basic(msg, sig, &u32_val);
649             lua_pushboolean(L, u32_val == TRUE);
650             break;
651         case MRP_DBUS_TYPE_BYTE:
652             mrp_dbus_msg_read_basic(msg, sig, &byte_val);
653             lua_pushinteger(L, byte_val);
654             break;
655         case MRP_DBUS_TYPE_DOUBLE:
656             mrp_dbus_msg_read_basic(msg, sig, &d_val);
657             lua_pushnumber(L, d_val);
658             break;
659         case MRP_DBUS_TYPE_STRING:
660             mrp_dbus_msg_read_basic(msg, sig, &s_val);
661             lua_pushstring(L, s_val);
662             break;
663         default:
664             mrp_log_error("AMB: parse_elementary_value: unknown type");
665             goto error;
666     }
667
668     return TRUE;
669
670 error:
671     return FALSE;
672 }
673
674 static bool parse_value(lua_State *L, mrp_dbus_msg_t *msg,
675         dbus_property_watch_t *w);
676
677 static bool parse_struct(lua_State *L,
678         mrp_dbus_msg_t *msg, dbus_property_watch_t *w)
679 {
680     int i = 1;
681
682     /* initialize the table */
683     lua_newtable(L);
684
685     mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_STRUCT, NULL);
686
687     while (mrp_dbus_msg_arg_type(msg, NULL) != MRP_DBUS_TYPE_INVALID) {
688
689         /* struct "index" */
690         lua_pushinteger(L, i++);
691
692         parse_value(L, msg, w);
693
694         /* put the values to the table */
695         lua_settable(L, -3);
696     }
697
698     mrp_dbus_msg_exit_container(msg);
699
700     return TRUE;
701 }
702
703
704 static bool parse_dict_entry(lua_State *L,
705         mrp_dbus_msg_t *msg, dbus_property_watch_t *w)
706 {
707     mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_DICT_ENTRY, NULL);
708
709     while (mrp_dbus_msg_arg_type(msg, NULL) != MRP_DBUS_TYPE_INVALID) {
710
711         /* key must be elementary, value can be anything */
712         parse_elementary_value(L, msg, w);
713         parse_value(L, msg, w);
714
715         /* put the values to the table */
716         lua_settable(L, -3);
717     }
718
719     mrp_dbus_msg_exit_container(msg); /* dict entry */
720
721     return TRUE;
722 }
723
724 static bool parse_array(lua_State *L,
725         mrp_dbus_msg_t *msg, dbus_property_watch_t *w)
726 {
727     /* the lua array */
728     lua_newtable(L);
729
730     mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_ARRAY, NULL);
731
732     /* the problem: if the value inside array is a dict entry, the
733      * indexing of elements need to be done with dict keys instead
734      * of numbers. */
735
736     if (mrp_dbus_msg_arg_type(msg, NULL) == MRP_DBUS_TYPE_DICT_ENTRY) {
737         while (mrp_dbus_msg_arg_type(msg, NULL) != MRP_DBUS_TYPE_INVALID) {
738             parse_dict_entry(L, msg, w);
739         }
740     }
741     else {
742         int i = 1;
743
744         while (mrp_dbus_msg_arg_type(msg, NULL) != MRP_DBUS_TYPE_INVALID) {
745
746             /* array index */
747             lua_pushinteger(L, i++);
748
749             parse_value(L, msg, w);
750
751             /* put the values to the table */
752             lua_settable(L, -3);
753         }
754     }
755
756     mrp_dbus_msg_exit_container(msg); /* array */
757
758     return TRUE;
759 }
760
761 static bool parse_value(lua_State *L, mrp_dbus_msg_t *msg,
762         dbus_property_watch_t *w)
763 {
764     mrp_dbus_type_t curr;
765
766     curr = mrp_dbus_msg_arg_type(msg, NULL);
767
768     switch (curr) {
769         case MRP_DBUS_TYPE_BYTE:
770         case MRP_DBUS_TYPE_BOOLEAN:
771         case MRP_DBUS_TYPE_INT16:
772         case MRP_DBUS_TYPE_INT32:
773         case MRP_DBUS_TYPE_UINT16:
774         case MRP_DBUS_TYPE_UINT32:
775         case MRP_DBUS_TYPE_DOUBLE:
776         case MRP_DBUS_TYPE_STRING:
777             return parse_elementary_value(L, msg, w);
778         case MRP_DBUS_TYPE_ARRAY:
779             return parse_array(L, msg, w);
780         case MRP_DBUS_TYPE_STRUCT:
781             return parse_struct(L, msg, w);
782         case MRP_DBUS_TYPE_DICT_ENTRY:
783             goto error; /* these are handled from parse_array */
784         case MRP_DBUS_TYPE_INVALID:
785             return TRUE;
786         default:
787             break;
788     }
789
790 error:
791     mrp_log_error("AMB: failed to parse D-Bus property (sig[i] %c)", curr);
792     return FALSE;
793 }
794
795 static void lua_property_handler(mrp_dbus_msg_t *msg, dbus_property_watch_t *w)
796 {
797 #if 0
798     char *variant_sig = NULL;
799 #endif
800
801     if (!w || !msg) {
802         mrp_log_error("AMB: no dbus property watch set");
803         goto end;
804     }
805
806     if (w->lua_prop->handler_ref == LUA_NOREF) {
807         mrp_log_error("AMB: no lua reference");
808         goto end;
809     }
810
811     if (mrp_dbus_msg_type(msg) == MRP_DBUS_MESSAGE_TYPE_ERROR) {
812         mrp_log_error("AMB: error message from ambd");
813         goto end;
814     }
815
816     if (!mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_VARIANT, NULL)) {
817         mrp_log_error("AMB: message parameter wasn't a variant");
818         goto end;
819     }
820
821     /*
822     mrp_log_info("iter sig: %s, expected: %s",
823             variant_sig, w->lua_prop->dbus_data.signature);
824     */
825
826
827 #if 0
828     /* FIXME: check this when the D-Bus API has support */
829     variant_sig = dbus_message_iter_get_signature(&variant_iter);
830
831     if (!variant_sig) {
832         mrp_log_error("amb: could not get variant signature");
833         goto error;
834     }
835
836     /* check if we got what we were expecting */
837     if (strcmp(variant_sig, w->lua_prop->dbus_data.signature) != 0) {
838         mrp_log_error("amb: dbus data signature didn't match");
839         goto error;
840     }
841 #endif
842
843     /* load the function pointer to the stack */
844     lua_rawgeti(w->ctx->L, LUA_REGISTRYINDEX, w->lua_prop->handler_ref);
845
846     /* "self" parameter */
847     mrp_lua_push_object(w->ctx->L, w->lua_prop);
848
849     /* parse values to the stack */
850     parse_value(w->ctx->L, msg, w);
851
852     /* call the handler function */
853     lua_pcall(w->ctx->L, 2, 0, 0);
854
855     mrp_dbus_msg_exit_container(msg);
856
857 end:
858     /* TODO: clean up the variant sig string */
859     return;
860 }
861
862
863 static void basic_property_handler(mrp_dbus_msg_t *msg, dbus_property_watch_t *w)
864 {
865     int32_t i32_val;
866     int16_t i16_val;
867     uint32_t u32_val;
868     uint16_t u16_val;
869     uint8_t byte_val;
870     double d_val;
871     char *s_val;
872
873     if (!w || !msg) {
874         mrp_log_error("AMB: no dbus property watch set");
875         goto error;
876     }
877
878     if (!mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_VARIANT, NULL)) {
879         mrp_log_error("AMB: message parameter wasn't a variant");
880         goto error;
881     }
882
883     /* check that D-Bus type matches the expected type */
884
885     if (mrp_dbus_msg_arg_type(msg, NULL) != w->prop.type)  {
886         mrp_log_error("AMB: argument type %c did not match expected type %c",
887                 mrp_dbus_msg_arg_type(msg, NULL), w->prop.type);
888         goto error;
889     }
890
891     switch (w->prop.type) {
892         case MRP_DBUS_TYPE_INT32:
893             mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_INT32, &i32_val);
894             w->prop.value.i = i32_val;
895             break;
896         case MRP_DBUS_TYPE_INT16:
897             mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_INT16, &i16_val);
898             w->prop.value.i = i16_val;
899             break;
900         case MRP_DBUS_TYPE_UINT32:
901             mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT32, &u32_val);
902             w->prop.value.u = u32_val;
903             break;
904         case MRP_DBUS_TYPE_UINT16:
905             mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_UINT16, &u16_val);
906             w->prop.value.u = u16_val;
907             break;
908         case MRP_DBUS_TYPE_BOOLEAN:
909             mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_BOOLEAN, &u32_val);
910             w->prop.value.u = u32_val;
911             break;
912         case MRP_DBUS_TYPE_BYTE:
913             mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_BYTE, &byte_val);
914             w->prop.value.u = byte_val;
915             break;
916         case MRP_DBUS_TYPE_DOUBLE:
917             mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_DOUBLE, &d_val);
918             w->prop.value.f = d_val;
919             break;
920         case MRP_DBUS_TYPE_STRING:
921             mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &s_val);
922             w->prop.value.s = mrp_strdup(s_val);
923             break;
924         default:
925             mrp_dbus_msg_exit_container(msg);
926             goto error;
927     }
928
929     mrp_dbus_msg_exit_container(msg);
930
931     if (w->cb)
932         w->cb(&w->prop, w->user_data);
933
934     return;
935
936 error:
937     mrp_log_error("AMB: failed to parse property value");
938     return;
939 }
940
941 static int property_signal_handler(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
942         void *data)
943 {
944     dbus_property_object_t *o = (dbus_property_object_t *) data;
945     dbus_property_watch_t *w;
946     char *interface;
947
948     MRP_UNUSED(dbus);
949
950     if (!msg) {
951         mrp_log_error("AMB: message is NULL");
952     }
953
954     if (mrp_dbus_msg_arg_type(msg, NULL) != MRP_DBUS_TYPE_STRING)  {
955         mrp_log_error("AMB: argument type %c , expected interface string",
956                 mrp_dbus_msg_arg_type(msg, NULL));
957         return TRUE;
958     }
959
960     mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &interface);
961
962     /* loop for all the properties */
963     mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_ARRAY, NULL);
964
965     while (mrp_dbus_msg_arg_type(msg, NULL) == MRP_DBUS_TYPE_DICT_ENTRY) {
966         char *property_name;
967
968         mrp_dbus_msg_enter_container(msg, MRP_DBUS_TYPE_DICT_ENTRY, NULL);
969
970         if (mrp_dbus_msg_arg_type(msg, NULL) != MRP_DBUS_TYPE_STRING)  {
971             mrp_log_error("AMB: argument type %c, expected property name",
972                 mrp_dbus_msg_arg_type(msg, NULL));
973             return TRUE;
974         }
975
976         mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_STRING, &property_name);
977
978         /* the right handler for the property is now defined by
979          * property_name and interface */
980
981         if (interface && property_name) {
982             int interface_len = strlen(interface);
983             int property_name_len = strlen(property_name);
984             char key[interface_len + 1 + property_name_len + 1];
985
986             strncpy(key, interface, interface_len);
987             *(key+interface_len) = '-';
988             strncpy(key+interface_len+1, property_name, property_name_len);
989             key[interface_len + 1 + property_name_len] = '\0';
990
991             mrp_debug("AMB: looking up property watch from %p with key '%s'",
992                     o, key);
993
994             /* find the right dbus_property_watch */
995             w = mrp_htbl_lookup(o->dbus_properties, key);
996
997             if (w) {
998                 /* we are interested in this property of this object */
999                 mrp_debug("AMB: PropertiesChanged for %s; %s handling",
1000                         property_name, w->tdata ? "basic" : "lua");
1001
1002                 /* process the variant from the dict */
1003                 if (w->tdata) {
1004                     basic_property_handler(msg, w);
1005                 }
1006                 else {
1007                     lua_property_handler(msg, w);
1008                 }
1009             }
1010         }
1011         mrp_dbus_msg_exit_container(msg); /* dict entry */
1012     }
1013     mrp_dbus_msg_exit_container(msg); /* array entry */
1014
1015     return TRUE;
1016 }
1017
1018 static void property_reply_handler(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
1019         void *data)
1020 {
1021     dbus_property_watch_t *w = (dbus_property_watch_t *) data;
1022
1023     MRP_UNUSED(dbus);
1024
1025     mrp_log_info("AMB: received property method reply, going for %s handling",
1026             w->tdata ? "basic" : "lua");
1027
1028     if (w->tdata) {
1029         basic_property_handler(msg, w);
1030     }
1031     else {
1032         lua_property_handler(msg, w);
1033     }
1034 }
1035
1036
1037 static void find_property_reply_handler(mrp_dbus_t *dbus, mrp_dbus_msg_t *msg,
1038         void *data)
1039 {
1040     dbus_property_watch_t *w = (dbus_property_watch_t *) data;
1041     char *obj = NULL;
1042
1043     MRP_UNUSED(dbus);
1044
1045     if (mrp_dbus_msg_type(msg) == MRP_DBUS_MESSAGE_TYPE_ERROR) {
1046         mrp_log_error("AMB: Error when trying to find an AMB object path");
1047         goto error;
1048     }
1049
1050     if(!mrp_dbus_msg_read_basic(msg, MRP_DBUS_TYPE_OBJECT_PATH, &obj)) {
1051         mrp_log_error("AMB: Error fetching the object path from the message");
1052         goto error;
1053     }
1054
1055     mrp_free((char *) w->lua_prop->dbus_data.obj);
1056     w->lua_prop->dbus_data.obj = mrp_strdup(obj);
1057
1058     mrp_debug("AMB: path for property %s: %s", w->lua_prop->dbus_data.name,
1059             w->lua_prop->dbus_data.obj);
1060
1061     subscribe_property(w->ctx, w);
1062
1063 error:
1064     return;
1065 }
1066
1067
1068 static int find_property_object(data_t *ctx, dbus_property_watch_t *w,
1069         const char *prop)
1070 {
1071     if (!ctx || !w || !prop)
1072         return -1;
1073
1074     mrp_log_info("AMB: finding object path of property '%s'", prop);
1075
1076     mrp_dbus_call(ctx->dbus,
1077             ctx->amb_addr, "/",
1078             "org.automotive.Manager",
1079             "findProperty", 3000, find_property_reply_handler, w,
1080             MRP_DBUS_TYPE_STRING, prop, MRP_DBUS_TYPE_INVALID);
1081
1082     return 0;
1083 }
1084
1085 static void free_dbus_property_object(dbus_property_object_t *o)
1086 {
1087     mrp_htbl_destroy(o->dbus_properties, FALSE);
1088     mrp_free(o->path);
1089     mrp_free(o);
1090 }
1091
1092 static void htbl_free_prop(void *key, void *object)
1093 {
1094     MRP_UNUSED(key);
1095     MRP_UNUSED(object);
1096 }
1097
1098 static int subscribe_property(data_t *ctx, dbus_property_watch_t *w)
1099 {
1100     const char *obj = w->lua_prop->dbus_data.obj;
1101     const char *iface = w->lua_prop->dbus_data.iface;
1102     const char *name = w->lua_prop->dbus_data.name;
1103     dbus_property_object_t *o;
1104     int interface_len = strlen(w->lua_prop->dbus_data.iface);
1105     int name_len = strlen(w->lua_prop->dbus_data.name);
1106
1107     mrp_log_info("AMB: subscribing to PropertiesChanged signal at '%s'", obj);
1108
1109     o = (dbus_property_object_t *) mrp_htbl_lookup(ctx->dbus_property_objects,
1110             (void *) obj);
1111
1112     if (!o) {
1113         mrp_htbl_config_t prop_conf;
1114
1115         prop_conf.comp = mrp_string_comp;
1116         prop_conf.hash = mrp_string_hash;
1117         prop_conf.free = htbl_free_prop;
1118         prop_conf.nbucket = 0;
1119         prop_conf.nentry = 10;
1120
1121         o = (dbus_property_object_t *) mrp_allocz(sizeof(dbus_property_object_t));
1122         o->path = strdup(obj);
1123         if (!o->path)
1124             return -1;
1125         o->dbus_properties = mrp_htbl_create(&prop_conf);
1126         if (!o->dbus_properties) {
1127             mrp_free(o->path);
1128             return -1;
1129         }
1130
1131         mrp_htbl_insert(ctx->dbus_property_objects, o->path, o);
1132     }
1133
1134     /* generate the key and add the watch to the object */
1135
1136     w->key = mrp_allocz(interface_len + 1 + name_len + 1);
1137
1138     if (!w->key) {
1139         free_dbus_property_object(o);
1140         return -1;
1141     }
1142
1143     strncpy(w->key, iface, interface_len);
1144     *(w->key+interface_len) = '-';
1145     strncpy(w->key+interface_len+1, name, name_len);
1146
1147     mrp_debug("AMB: inserting property watch to %p with key '%s'", o, w->key);
1148
1149     mrp_htbl_insert(o->dbus_properties, w->key, w);
1150     w->o = o;
1151
1152     mrp_dbus_subscribe_signal(ctx->dbus, property_signal_handler, o, NULL,
1153             obj, "org.freedesktop.DBus.Properties", "PropertiesChanged", NULL);
1154
1155     /* Ok, now we are listening to property changes. Let's get the initial
1156      * value. */
1157
1158     mrp_dbus_call(ctx->dbus,
1159             ctx->amb_addr, obj,
1160             "org.freedesktop.DBus.Properties",
1161             "Get", 3000, property_reply_handler, w,
1162             MRP_DBUS_TYPE_STRING, iface,
1163             MRP_DBUS_TYPE_STRING, name,
1164             MRP_DBUS_TYPE_INVALID);
1165
1166     return 0;
1167 }
1168
1169
1170 static void print_basic_property(dbus_basic_property_t *prop)
1171 {
1172     switch (prop->type) {
1173         case MRP_DBUS_TYPE_INT32:
1174         case MRP_DBUS_TYPE_INT16:
1175             mrp_debug("AMB: Property %s : %i", prop->name, prop->value.i);
1176             break;
1177         case MRP_DBUS_TYPE_UINT32:
1178         case MRP_DBUS_TYPE_UINT16:
1179         case MRP_DBUS_TYPE_BOOLEAN:
1180         case MRP_DBUS_TYPE_BYTE:
1181             mrp_debug("AMB: Property %s : %u", prop->name, prop->value.u);
1182             break;
1183         case MRP_DBUS_TYPE_DOUBLE:
1184             mrp_debug("AMB: Property %s : %f", prop->name, prop->value.f);
1185             break;
1186         case MRP_DBUS_TYPE_STRING:
1187             mrp_debug("AMB: Property %s : %s", prop->name, prop->value.s);
1188             break;
1189         default:
1190             mrp_log_error("AMB: Unknown value in property");
1191     }
1192 }
1193
1194 static void basic_property_updated(dbus_basic_property_t *prop, void *userdata)
1195 {
1196     char buf[512];
1197     int buflen;
1198     mql_result_t *r;
1199     dbus_property_watch_t *w = (dbus_property_watch_t *) userdata;
1200     basic_table_data_t *tdata = w->tdata;
1201     mqi_handle_t tx;
1202
1203     mrp_debug("AMB: basic_property_updated");
1204
1205     print_basic_property(prop);
1206
1207     tx = mqi_begin_transaction();
1208
1209     if (!prop->initialized) {
1210
1211         switch (tdata->type) {
1212             case mqi_string:
1213                 buflen = snprintf(buf, 512, "INSERT INTO %s VALUES (1, '%s', %s)",
1214                     w->lua_prop->basic_table_name, prop->name, prop->value.s);
1215                 break;
1216             case mqi_integer:
1217                 buflen = snprintf(buf, 512, "INSERT INTO %s VALUES (1, '%s', %d)",
1218                     w->lua_prop->basic_table_name, prop->name, (int) prop->value.i);
1219                 break;
1220             case mqi_unsignd:
1221                 buflen = snprintf(buf, 512, "INSERT INTO %s VALUES (1, '%s', %u)",
1222                     w->lua_prop->basic_table_name, prop->name, prop->value.u);
1223                 break;
1224             case mqi_floating:
1225                 buflen = snprintf(buf, 512, "INSERT INTO %s VALUES (1, '%s', %f)",
1226                     w->lua_prop->basic_table_name, prop->name, prop->value.f);
1227                 break;
1228             default:
1229                 goto end;
1230         }
1231
1232         if (buflen <= 0 || buflen == 512) {
1233             goto end;
1234         }
1235
1236         r = mql_exec_string(mql_result_string, buf);
1237
1238         prop->initialized = TRUE;
1239     }
1240     else {
1241         int ret;
1242
1243         switch (tdata->type) {
1244             case mqi_string:
1245                 ret = mql_bind_value(tdata->update_operation, 1, tdata->type,
1246                         prop->value.s);
1247                 break;
1248             case mqi_integer:
1249                 ret = mql_bind_value(tdata->update_operation, 1, tdata->type,
1250                         prop->value.i);
1251                 break;
1252             case mqi_unsignd:
1253                 ret = mql_bind_value(tdata->update_operation, 1, tdata->type,
1254                         prop->value.u);
1255                 break;
1256             case mqi_floating:
1257                 ret = mql_bind_value(tdata->update_operation, 1, tdata->type,
1258                         prop->value.f);
1259                 break;
1260             default:
1261                 goto end;
1262         }
1263
1264         if (ret < 0) {
1265             mrp_log_error("AMB: failed to bind value to update operation");
1266             goto end;
1267         }
1268
1269         r = mql_exec_statement(mql_result_string, tdata->update_operation);
1270     }
1271
1272     mrp_debug("amb: %s", mql_result_is_success(r) ? "updated database" :
1273               mql_result_error_get_message(r));
1274
1275     mql_result_free(r);
1276
1277 end:
1278     mqi_commit_transaction(tx);
1279 }
1280
1281 static void delete_basic_table_data(basic_table_data_t *tdata)
1282 {
1283     if (!tdata)
1284         return;
1285
1286     if (tdata->update_operation)
1287         mql_statement_free(tdata->update_operation);
1288
1289     if (tdata->table)
1290         mqi_drop_table(tdata->table);
1291
1292     mrp_free(tdata);
1293 }
1294
1295 static basic_table_data_t *create_basic_property_table(const char *table_name,
1296         const char *member, int type)
1297 {
1298     char buf[512];
1299     char *update_format;
1300     /* char *insert_format; */
1301     basic_table_data_t *tdata = NULL;
1302     int ret;
1303
1304     if (strlen(member) > 64)
1305         goto error;
1306
1307     tdata = (basic_table_data_t *) mrp_allocz(sizeof(basic_table_data_t));
1308
1309     if (!tdata)
1310         goto error;
1311
1312     switch (type) {
1313         case MRP_DBUS_TYPE_INT32:
1314         case MRP_DBUS_TYPE_INT16:
1315             tdata->type = mqi_integer;
1316             update_format = "%d";
1317             /* insert_format = "%d"; */
1318             break;
1319         case MRP_DBUS_TYPE_UINT32:
1320         case MRP_DBUS_TYPE_UINT16:
1321         case MRP_DBUS_TYPE_BOOLEAN:
1322         case MRP_DBUS_TYPE_BYTE:
1323             tdata->type = mqi_unsignd;
1324             update_format = "%u";
1325             /* insert_format = "%u"; */
1326             break;
1327         case MRP_DBUS_TYPE_DOUBLE:
1328             tdata->type = mqi_floating;
1329             update_format = "%f";
1330             /* insert_format = "%f"; */
1331             break;
1332         case MRP_DBUS_TYPE_STRING:
1333             tdata->type = mqi_varchar;
1334             update_format = "%s";
1335             /* insert_format = "'%s'"; */
1336             break;
1337         default:
1338             mrp_log_error("unknown type %d", type);
1339             goto error;
1340     }
1341
1342     tdata->defs[0].name = "id";
1343     tdata->defs[0].type = mqi_unsignd;
1344     tdata->defs[0].length = 0;
1345     tdata->defs[0].flags = 0;
1346
1347     tdata->defs[1].name = "key";
1348     tdata->defs[1].type = mqi_varchar;
1349     tdata->defs[1].length = 64;
1350     tdata->defs[1].flags = 0;
1351
1352     tdata->defs[2].name = "value";
1353     tdata->defs[2].type = tdata->type;
1354     tdata->defs[2].length = (tdata->type == mqi_varchar) ? 128 : 0;
1355     tdata->defs[2].flags = 0;
1356
1357     memset(&tdata->defs[3], 0, sizeof(tdata->defs[3]));
1358
1359     tdata->table = MQI_CREATE_TABLE((char *) table_name, MQI_TEMPORARY,
1360             tdata->defs, NULL);
1361
1362     if (!tdata->table) {
1363         mrp_log_error("AMB: creating table '%s' failed", table_name);
1364         goto error;
1365     }
1366
1367     ret = snprintf(buf, 512, "UPDATE %s SET value = %s where id = 1",
1368             table_name, update_format);
1369
1370     if (ret <= 0 || ret == 512) {
1371         goto error;
1372     }
1373
1374     tdata->update_operation = mql_precompile(buf);
1375
1376     if (!tdata->update_operation) {
1377         mrp_log_error("AMB: buggy buf: '%s'", buf);
1378         goto error;
1379     }
1380
1381     mrp_log_info("AMB: compiled update statement '%s'", buf);
1382
1383     return tdata;
1384
1385 error:
1386     mrp_log_error("AMB: failed to create table %s", table_name);
1387     delete_basic_table_data(tdata);
1388     return NULL;
1389 }
1390
1391 static int load_config(lua_State *L, const char *path)
1392 {
1393     if (!luaL_loadfile(L, path) && !lua_pcall(L, 0, 0, 0))
1394         return TRUE;
1395     else {
1396         mrp_log_error("AMB: failed to load config file %s.", path);
1397         mrp_log_error("%s", lua_tostring(L, -1));
1398         lua_settop(L, 0);
1399
1400         return FALSE;
1401     }
1402 }
1403
1404 static void amb_startup_timer(mrp_timer_t *t, void *data)
1405 {
1406     data_t *ctx = (data_t *) data;
1407     mrp_list_hook_t *p, *n;
1408
1409     mrp_del_timer(t);
1410
1411     /* check that ambd hasn't crashed meanwhile */
1412
1413     if (!ctx->force_subscription && ctx->amb_state != MRP_PROCESS_STATE_READY)
1414         return;
1415
1416     mrp_log_info("AMB: delayed querying of ambd properties\n");
1417
1418     /* query the ambd property D-Bus paths again and start listening to
1419      * the signals. */
1420
1421     mrp_list_foreach(&ctx->lua_properties, p, n) {
1422         dbus_property_watch_t *w =
1423                 mrp_list_entry(p, dbus_property_watch_t, hook);
1424
1425         if (w->lua_prop->dbus_data.undefined_object_path)
1426             find_property_object(ctx, w, w->lua_prop->dbus_data.objectname);
1427         else
1428             subscribe_property(ctx, w);
1429     }
1430 }
1431
1432 static int unsubscribe_signal_cb(void *key, void *object, void *user_data)
1433 {
1434     dbus_property_object_t *o = (dbus_property_object_t *) object;
1435     data_t *ctx = (data_t *) user_data;
1436
1437     MRP_UNUSED(key);
1438
1439     mrp_dbus_unsubscribe_signal(ctx->dbus, property_signal_handler, o,
1440             NULL, o->path,
1441             "org.freedesktop.DBus.Properties",
1442             "PropertiesChanged", NULL);
1443     return MRP_HTBL_ITER_MORE;
1444 }
1445
1446 static void amb_watch(const char *id, mrp_process_state_t state, void *data)
1447 {
1448     data_t *ctx = (data_t *) data;
1449
1450     if (strcmp(id, ctx->amb_id) != 0)
1451         return;
1452
1453     mrp_log_info("AMB: ambd state changed to %s",
1454             state == MRP_PROCESS_STATE_READY ? "ready" : "not ready");
1455
1456
1457     if (state == MRP_PROCESS_STATE_NOT_READY &&
1458             ctx->amb_state == MRP_PROCESS_STATE_READY) {
1459
1460         mrp_log_error("AMB: lost connection to ambd");
1461
1462         /* stop listening to the ambd signals */
1463         mrp_htbl_foreach(ctx->dbus_property_objects, unsubscribe_signal_cb, ctx);
1464     }
1465     else if (state == MRP_PROCESS_STATE_READY &&
1466             ctx->amb_state != MRP_PROCESS_STATE_READY) {
1467
1468         mrp_log_info("AMB: ambd was started up\n");
1469
1470         /* give amb some time to get the D-Bus interface ready */
1471
1472         mrp_add_timer(ctx->ml, ctx->amb_startup_delay, amb_startup_timer, ctx);
1473     }
1474
1475     ctx->amb_state = state;
1476 }
1477
1478 /* functions for handling updating the AMB properties */
1479
1480 static int update_amb_property(char *name, enum amb_type type, void *value,
1481         data_t *ctx)
1482 {
1483     int ret = -1;
1484     mrp_msg_t *msg = mrp_msg_create(
1485             MRP_MSG_TAG_STRING(1, name),
1486             MRP_MSG_FIELD_END);
1487
1488     if (!msg)
1489         goto end;
1490
1491     if (!ctx->t)
1492         goto end;
1493
1494     switch(type) {
1495         case amb_string:
1496             mrp_msg_append(msg, 2, MRP_MSG_FIELD_STRING, value);
1497             break;
1498         case amb_bool:
1499             mrp_msg_append(msg, 2, MRP_MSG_FIELD_BOOL, *(bool *)value);
1500             break;
1501         case amb_int32:
1502             mrp_msg_append(msg, 2, MRP_MSG_FIELD_INT32, *(int32_t *)value);
1503             break;
1504         case amb_int16:
1505             mrp_msg_append(msg, 2, MRP_MSG_FIELD_INT16, *(int16_t *)value);
1506             break;
1507         case amb_uint32:
1508             mrp_msg_append(msg, 2, MRP_MSG_FIELD_UINT32, *(uint32_t *)value);
1509             break;
1510         case amb_uint16:
1511             mrp_msg_append(msg, 2, MRP_MSG_FIELD_UINT16, *(uint16_t *)value);
1512             break;
1513         case amb_byte:
1514             mrp_msg_append(msg, 2, MRP_MSG_FIELD_UINT8, *(uint8_t *)value);
1515             break;
1516         case amb_double:
1517             mrp_msg_append(msg, 2, MRP_MSG_FIELD_DOUBLE, *(double *)value);
1518             break;
1519     }
1520
1521     if (!mrp_transport_send(ctx->t, msg))
1522         goto end;
1523
1524     mrp_log_info("AMB: Sent message to ambd");
1525
1526     ret = 0;
1527
1528 end:
1529     if (msg)
1530         mrp_msg_unref(msg);
1531
1532     return ret;
1533 }
1534
1535
1536 static bool initiate_func(lua_State *L, void *data,
1537                     const char *signature, mrp_funcbridge_value_t *args,
1538                     char  *ret_type, mrp_funcbridge_value_t *ret_val)
1539 {
1540     MRP_UNUSED(L);
1541     MRP_UNUSED(args);
1542     MRP_UNUSED(data);
1543
1544     if (!signature || signature[0] != 'o') {
1545         return false;
1546     }
1547
1548     *ret_type = MRP_FUNCBRIDGE_BOOLEAN;
1549     ret_val->boolean = true;
1550
1551     return TRUE;
1552 }
1553
1554
1555 static bool update_func(lua_State *L, void *data,
1556                     const char *signature, mrp_funcbridge_value_t *args,
1557                     char  *ret_type, mrp_funcbridge_value_t *ret_val)
1558 {
1559     mrp_lua_sink_t *sink;
1560     const char *type, *property;
1561
1562     const char *s_val;
1563     int32_t i_val;
1564     uint32_t u_val;
1565     double d_val;
1566
1567     int ret = -1;
1568     char *error = "unknown error";
1569
1570     MRP_UNUSED(L);
1571
1572     if (!signature || signature[0] != 'o') {
1573         mrp_log_error("AMB: invalid signature '%s'", signature ? signature : "NULL");
1574         goto error;
1575     }
1576
1577     sink = (mrp_lua_sink_t *) args[0].pointer;
1578
1579     property = mrp_lua_sink_get_property(sink);
1580
1581     if (!property || strlen(property) == 0) {
1582         error = "invalid property";
1583         goto error;
1584     }
1585
1586     /* ok, for now we only support updates of basic values */
1587
1588     type = mrp_lua_sink_get_type(sink);
1589
1590     if (!type || strlen(type) != 1) {
1591         error = "invalid type";
1592         goto error;
1593     }
1594
1595     switch (type[0]) {
1596         case amb_double:
1597             d_val = mrp_lua_sink_get_floating(sink,0,0,0);
1598             ret = update_amb_property((char *) property,
1599                     (enum amb_type) type[0], &d_val, (data_t *) data);
1600             break;
1601         case amb_int16:
1602         case amb_int32:
1603             i_val = mrp_lua_sink_get_integer(sink,0,0,0);
1604             ret = update_amb_property((char *) property,
1605                     (enum amb_type) type[0], &i_val, (data_t *) data);
1606             break;
1607         case amb_bool:
1608         case amb_byte:
1609         case amb_uint16:
1610         case amb_uint32:
1611             u_val = mrp_lua_sink_get_unsigned(sink,0,0,0);
1612             ret = update_amb_property((char *) property,
1613                     (enum amb_type) type[0], &u_val, (data_t *) data);
1614             break;
1615         case amb_string:
1616             s_val = mrp_lua_sink_get_string(sink,0,0,0,NULL,0);
1617             ret = update_amb_property((char *) property,
1618                     (enum amb_type) type[0], (void *) s_val,
1619                     (data_t *) data);
1620             break;
1621     }
1622
1623     if (ret < 0) {
1624         error = "error updating property";
1625         goto error;
1626     }
1627
1628     *ret_type = MRP_FUNCBRIDGE_BOOLEAN;
1629     ret_val->boolean = true;
1630
1631     return TRUE;
1632
1633 error:
1634     mrp_log_error("AMB: error processing the property change!");
1635
1636     *ret_type = MRP_FUNCBRIDGE_BOOLEAN;
1637     ret_val->boolean = false;
1638
1639     /* FIXME: this shouldn't be needed, but without this the element handler crashes */
1640     ret_val->string = mrp_strdup(error);
1641
1642     return TRUE;
1643 }
1644
1645
1646 static void recvdatafrom_evt(mrp_transport_t *t, void *data, uint16_t tag,
1647                      mrp_sockaddr_t *addr, socklen_t addrlen, void *user_data)
1648 {
1649     /* At the moment we are not receiving anything from AMB through this
1650      * transport, however that might change */
1651
1652     MRP_UNUSED(t);
1653     MRP_UNUSED(data);
1654     MRP_UNUSED(tag);
1655     MRP_UNUSED(addr);
1656     MRP_UNUSED(addrlen);
1657     MRP_UNUSED(user_data);
1658 }
1659
1660
1661 static void recvdata_evt(mrp_transport_t *t, void *data, uint16_t tag, void *user_data)
1662 {
1663     recvdatafrom_evt(t, data, tag, NULL, 0, user_data);
1664 }
1665
1666
1667 static void closed_evt(mrp_transport_t *t, int error, void *user_data)
1668 {
1669     data_t *ctx = (data_t *) user_data;
1670
1671     MRP_UNUSED(error);
1672
1673     mrp_transport_destroy(t);
1674     ctx->t = NULL;
1675
1676     /* open the listening socket again */
1677
1678     if (!ctx->lt) {
1679         create_transport(t->ml, ctx);
1680     }
1681 }
1682
1683 static void connection_evt(mrp_transport_t *lt, void *user_data)
1684 {
1685     data_t *ctx = (data_t *) user_data;
1686
1687     mrp_log_info("AMB connection!");
1688
1689     if (ctx->t) {
1690         mrp_log_error("AMB: already connected");
1691     }
1692     else {
1693         ctx->t = mrp_transport_accept(lt, ctx, 0);
1694
1695         /* amb murphy plugin is now connected to us */
1696     }
1697
1698     /* close the listening socket, since we only have one client */
1699
1700     mrp_transport_destroy(lt);
1701     ctx->lt = NULL;
1702 }
1703
1704
1705 static int create_transport(mrp_mainloop_t *ml, data_t *ctx)
1706 {
1707     socklen_t alen;
1708     mrp_sockaddr_t addr;
1709     int flags = MRP_TRANSPORT_REUSEADDR | MRP_TRANSPORT_MODE_MSG;
1710     const char *atype;
1711     struct stat statbuf;
1712
1713     static mrp_transport_evt_t evt; /* static members are initialized to zero */
1714
1715     evt.closed = closed_evt;
1716     evt.connection = connection_evt;
1717     evt.recvdata = recvdata_evt;
1718     evt.recvdatafrom = recvdatafrom_evt;
1719
1720     alen = mrp_transport_resolve(NULL, ctx->tport_addr, &addr, sizeof(addr),
1721             &atype);
1722     if (alen <= 0) {
1723         mrp_log_error("AMB: failed to resolve address");
1724         goto error;
1725     }
1726
1727     /* remove the old socket if present */
1728
1729     if (strcmp(atype, "unxs") == 0) {
1730         char *path = addr.unx.sun_path;
1731         if (path[0] == '/') {
1732             /* if local socket and file exists, remove it */
1733             if (stat(path, &statbuf) == 0) {
1734                 if (S_ISSOCK(statbuf.st_mode)) {
1735                     if (unlink(path) < 0) {
1736                         mrp_log_error("error removing the socket");
1737                         goto error;
1738                     }
1739                 }
1740                 else {
1741                     mrp_log_error("AMB: a file where the socket should be");
1742                     goto error;
1743                 }
1744             }
1745         }
1746     }
1747
1748
1749     ctx->lt = mrp_transport_create(ml, atype, &evt, ctx, flags);
1750     if (ctx->lt == NULL) {
1751         mrp_log_error("AMB: failed to create transport");
1752         goto error;
1753     }
1754
1755     if (!mrp_transport_bind(ctx->lt, &addr, alen)) {
1756         mrp_log_error("AMB: failed to bind transport to address");
1757         goto error;
1758     }
1759
1760     if (!mrp_transport_listen(ctx->lt, 1)) {
1761         mrp_log_error("AMB: failed to listen on transport");
1762         goto error;
1763     }
1764
1765     return 0;
1766
1767 error:
1768     if (ctx->lt)
1769         mrp_transport_destroy(ctx->lt);
1770
1771     return -1;
1772 }
1773
1774
1775 static void htbl_free_obj(void *key, void *object)
1776 {
1777     dbus_property_object_t *o = (dbus_property_object_t *) object;
1778
1779     MRP_UNUSED(key);
1780
1781     free_dbus_property_object(o);
1782 }
1783
1784
1785 /* plugin init and deinit */
1786
1787 static int amb_init(mrp_plugin_t *plugin)
1788 {
1789     data_t *ctx;
1790     mrp_plugin_arg_t *args = plugin->args;
1791     mrp_htbl_config_t obj_conf;
1792
1793     ctx = (data_t *) mrp_allocz(sizeof(data_t));
1794
1795     if (!ctx)
1796         return FALSE;
1797
1798     mrp_list_init(&ctx->lua_properties);
1799
1800     plugin->data = ctx;
1801
1802     ctx->ml = plugin->ctx->ml;
1803
1804     ctx->amb_addr = args[ARG_AMB_DBUS_ADDRESS].str;
1805     ctx->config_file = args[ARG_AMB_CONFIG_FILE].str;
1806     ctx->amb_id = args[ARG_AMB_ID].str;
1807     ctx->tport_addr = args[ARG_AMB_TPORT_ADDRESS].str;
1808     ctx->amb_startup_delay = args[ARG_AMB_STARTUP_DELAY].u32;
1809     ctx->force_subscription = args[ARG_AMB_FORCE_SUBSCRIPTION].bln;
1810
1811     mrp_log_info("AMB: D-Bus address: %s", ctx->amb_addr);
1812     mrp_log_info("AMB: config file: %s", ctx->config_file);
1813     mrp_log_info("AMB: transport address: %s", ctx->tport_addr);
1814     mrp_log_info("AMB: %s D-Bus subscription",
1815             ctx->force_subscription ? "forced" : "didn't force");
1816
1817     ctx->dbus = mrp_dbus_connect(plugin->ctx->ml, args[ARG_AMB_DBUS_BUS].str,
1818             NULL);
1819
1820     if (!ctx->dbus)
1821         goto error;
1822
1823     /* initialize transport towards ambd */
1824
1825     if (create_transport(plugin->ctx->ml, ctx) < 0)
1826         goto error;
1827
1828     /* create hash table */
1829
1830     obj_conf.comp = mrp_string_comp;
1831     obj_conf.hash = mrp_string_hash;
1832     obj_conf.free = htbl_free_obj;
1833     obj_conf.nbucket = 0;
1834     obj_conf.nentry = 10;
1835
1836     ctx->dbus_property_objects = mrp_htbl_create(&obj_conf);
1837
1838     if (!ctx->dbus_property_objects)
1839         goto error;
1840
1841     /* initialize lua support */
1842
1843     global_ctx = ctx;
1844
1845     ctx->L = mrp_lua_get_lua_state();
1846
1847     if (!ctx->L)
1848         goto error;
1849
1850     /* functions to handle the direct property updates */
1851
1852     mrp_funcbridge_create_cfunc(ctx->L, "amb_initiate", "o",
1853                                 initiate_func, (void *)ctx);
1854     mrp_funcbridge_create_cfunc(ctx->L, "amb_update", "o",
1855                                 update_func, (void *)ctx);
1856
1857     /* custom class for configuration */
1858
1859     mrp_lua_create_object_class(ctx->L, MRP_LUA_CLASS(amb, property));
1860
1861     /* TODO: create here a "manager" lua object and put that to the global
1862      * lua table? This one then has a pointer to the C context. */
1863
1864     /* 1. read the configuration file. The configuration must tell
1865             - target object (/org/automotive/runningstatus/vehicleSpeed)
1866             - target interface (org.automotive.vehicleSpeed)
1867             - target member (VehicleSpeed)
1868             - target type (int32_t)
1869             - destination table
1870      */
1871
1872     if (!load_config(ctx->L, ctx->config_file))
1873         goto error;
1874
1875     mrp_process_set_state("murphy-amb", MRP_PROCESS_STATE_READY);
1876
1877     if (mrp_process_set_watch(ctx->amb_id, plugin->ctx->ml, amb_watch, ctx) < 0) {
1878         /* let's not quit yet? */
1879         mrp_log_error("AMB: setting the ambd status watch failed");
1880     }
1881
1882     ctx->amb_state = mrp_process_query_state(ctx->amb_id);
1883
1884     /* query amb properties after amb has had time to ready the interface */
1885
1886     if (ctx->force_subscription || ctx->amb_state == MRP_PROCESS_STATE_READY) {
1887         mrp_log_info("added the ambd timer after %u ms", ctx->amb_startup_delay);
1888         mrp_add_timer(ctx->ml, ctx->amb_startup_delay, amb_startup_timer, ctx);
1889     }
1890
1891     return TRUE;
1892
1893 error:
1894     {
1895         mrp_list_hook_t *p, *n;
1896
1897         mrp_list_foreach(&ctx->lua_properties, p, n) {
1898             dbus_property_watch_t *w =
1899                     mrp_list_entry(p, dbus_property_watch_t, hook);
1900
1901             destroy_prop(ctx, w);
1902         }
1903     }
1904
1905     if (ctx->dbus) {
1906         mrp_dbus_unref(ctx->dbus);
1907         ctx->dbus = NULL;
1908     }
1909
1910     if (ctx->t) {
1911         mrp_transport_destroy(ctx->t);
1912         ctx->t = NULL;
1913     }
1914
1915     if (ctx->dbus_property_objects) {
1916         mrp_htbl_destroy(ctx->dbus_property_objects, FALSE);
1917     }
1918
1919     mrp_process_remove_watch(ctx->amb_id);
1920
1921     mrp_free(ctx);
1922
1923     return FALSE;
1924 }
1925
1926
1927 static void amb_exit(mrp_plugin_t *plugin)
1928 {
1929     data_t *ctx = (data_t *) plugin->data;
1930     mrp_list_hook_t *p, *n;
1931
1932     mrp_process_remove_watch(ctx->amb_id);
1933
1934     mrp_process_set_state("murphy-amb", MRP_PROCESS_STATE_NOT_READY);
1935
1936     /* for all subscribed properties, unsubscribe and free memory */
1937
1938     mrp_list_foreach(&ctx->lua_properties, p, n) {
1939         dbus_property_watch_t *w =
1940                 mrp_list_entry(p, dbus_property_watch_t, hook);
1941
1942         destroy_prop(ctx, w);
1943     }
1944
1945     if (ctx->dbus_property_objects) {
1946         mrp_htbl_destroy(ctx->dbus_property_objects, FALSE);
1947     }
1948
1949     mrp_transport_destroy(ctx->t);
1950     ctx->t = NULL;
1951
1952     global_ctx = NULL;
1953
1954     mrp_free(ctx);
1955 }
1956
1957 #define AMB_DESCRIPTION "A plugin for Automotive Message Broker D-Bus API."
1958 #define AMB_HELP        "Access Automotive Message Broker."
1959 #define AMB_VERSION     MRP_VERSION_INT(0, 0, 2)
1960 #define AMB_AUTHORS     "Ismo Puustinen <ismo.puustinen@intel.com>"
1961
1962 static mrp_plugin_arg_t args[] = {
1963     MRP_PLUGIN_ARGIDX(ARG_AMB_DBUS_ADDRESS, STRING, "amb_address",
1964             "org.automotive.message.broker"),
1965     MRP_PLUGIN_ARGIDX(ARG_AMB_DBUS_BUS, STRING, "dbus_bus", "system"),
1966     MRP_PLUGIN_ARGIDX(ARG_AMB_CONFIG_FILE, STRING, "config_file",
1967             "/etc/murphy/plugins/amb/config.lua"),
1968     MRP_PLUGIN_ARGIDX(ARG_AMB_ID, STRING, "amb_id", "ambd"),
1969     MRP_PLUGIN_ARGIDX(ARG_AMB_TPORT_ADDRESS, STRING, "transport_address",
1970             "unxs:/tmp/murphy/amb"),
1971     MRP_PLUGIN_ARGIDX(ARG_AMB_STARTUP_DELAY, UINT32, "amb_startup_delay",
1972             2000),
1973     MRP_PLUGIN_ARGIDX(ARG_AMB_FORCE_SUBSCRIPTION, BOOL,
1974             "force_subscription", false),
1975 };
1976
1977 MURPHY_REGISTER_PLUGIN("amb",
1978                        AMB_VERSION, AMB_DESCRIPTION,
1979                        AMB_AUTHORS, AMB_HELP,
1980                        MRP_SINGLETON, amb_init, amb_exit,
1981                        args, MRP_ARRAY_SIZE(args),
1982                        NULL, 0, NULL, 0, NULL);