f5362a5d3657e365493b9805f10c8d7ccdd2a3b5
[profile/ivi/persistence-client-library.git] / src / persistence_client_library_dbus_service.c
1 /******************************************************************************
2  * Project         Persistency
3  * (c) copyright   2012
4  * Company         XS Embedded GmbH
5  *****************************************************************************/
6 /******************************************************************************
7  * This Source Code Form is subject to the terms of the
8  * Mozilla Public License, v. 2.0. If a  copy of the MPL was not distributed
9  * with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
10 ******************************************************************************/
11  /**
12  * @file           persistence_client_library_dbus_service.c
13  * @ingroup        Persistence client library
14  * @author         Ingo Huerner
15  * @brief          Implementation of the persistence client library dbus service.
16  * @see
17  */
18
19
20 #include "persistence_client_library_dbus_service.h"
21 #include "persistence_client_library_lc_interface.h"
22 #include "persistence_client_library_pas_interface.h"
23 #include "../include_protected/persistence_client_library_data_organization.h"
24
25 #include <stdio.h>
26 #include <errno.h>
27 #include <string.h>
28 #include <unistd.h>
29 #include <stdlib.h>
30
31 pthread_mutex_t gDbusInitializedMtx  = PTHREAD_MUTEX_INITIALIZER;
32 pthread_cond_t  gDbusInitializedCond = PTHREAD_COND_INITIALIZER;
33
34 typedef enum EDBusObjectType
35 {
36    OT_NONE = 0,
37    OT_WATCH,
38    OT_TIMEOUT
39 } tDBusObjectType;
40
41
42 typedef struct SObjectEntry
43 {
44    tDBusObjectType objtype; /** libdbus' object */
45    union
46    {
47       DBusWatch * watch;      /** watch "object" */
48       DBusTimeout * timeout;  /** timeout "object" */
49    };
50 } tObjectEntry;
51
52
53
54 /// polling structure
55 typedef struct SPollInfo
56 {
57    int nfds;
58    struct pollfd fds[10];
59    tObjectEntry objects[10];
60 } tPollInfo;
61
62
63 /// polling information
64 static tPollInfo gPollInfo;
65
66 /// dbus connection
67 DBusConnection* gDbusConn = NULL;
68
69
70 DBusConnection* get_dbus_connection(void)
71 {
72    return gDbusConn;
73 }
74
75 int bContinue = 0;
76
77 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
78
79
80 /* function to unregister ojbect path message handler */
81 static void unregisterMessageHandler(DBusConnection *connection, void *user_data)
82 {
83    DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("unregisterObjectPath\n"));
84 }
85
86 /* catches messages not directed to any registered object path ("garbage collector") */
87 static DBusHandlerResult handleObjectPathMessageFallback(DBusConnection * connection, DBusMessage * message, void * user_data)
88 {
89    DBusHandlerResult result = DBUS_HANDLER_RESULT_HANDLED;
90
91    // org.genivi.persistence.admin  S I G N A L
92    if((0==strcmp("org.genivi.persistence.admin", dbus_message_get_interface(message))))
93    {
94       if(dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL)
95       {
96          if((0==strcmp("PersistenceModeChanged", dbus_message_get_member(message))))
97          {
98             // to do handle signal
99             result = signal_persModeChange(connection, message);
100          }
101          else
102          {
103             DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("handleObjectPathMessageFallback -> unknown signal:"), DLT_STRING(dbus_message_get_interface(message)) );
104          }
105       }
106    }
107    // org.genivi.persistence.admin  S I G N A L
108    else if((0==strcmp("org.genivi.persistence.adminconsumer", dbus_message_get_interface(message))))
109    {
110       if(dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL)
111       {
112          pclNotification_s notifyStruct;
113          int validMessage = 0;
114
115          if((0==strcmp("PersistenceResChange", dbus_message_get_member(message))))
116          {
117             notifyStruct.pclKeyNotify_Status = pclNotifyStatus_changed;
118             validMessage = 1;
119          }
120          else if((0==strcmp("PersistenceResDelete", dbus_message_get_member(message))))
121          {
122             notifyStruct.pclKeyNotify_Status = pclNotifyStatus_deleted;
123             validMessage = 1;
124          }
125          else if((0==strcmp("PersistenceRes", dbus_message_get_member(message))))
126          {
127             notifyStruct.pclKeyNotify_Status = pclNotifyStatus_created;
128             validMessage = 1;
129          }
130
131          if(validMessage == 1)
132          {
133             DBusError error;
134             DBusMessage *reply;
135             dbus_error_init (&error);
136             char* ldbid;
137             char* user_no;
138             char* seat_no;
139
140             if (!dbus_message_get_args (message, &error, DBUS_TYPE_STRING, &notifyStruct.resource_id,
141                                                          DBUS_TYPE_STRING, &ldbid,
142                                                          DBUS_TYPE_STRING, &user_no,
143                                                          DBUS_TYPE_STRING, &seat_no,
144                                                          DBUS_TYPE_INVALID))
145             {
146                reply = dbus_message_new_error(message, error.name, error.message);
147
148                if (reply == 0)
149                {
150                   DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("handleObjectPathMessageFallback => DBus No memory"), DLT_STRING(dbus_message_get_interface(message)) );
151                }
152
153                if (!dbus_connection_send(connection, reply, 0))
154                {
155                   DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("handleObjectPathMessageFallback => DBus No memory"), DLT_STRING(dbus_message_get_interface(message)) );
156                }
157
158                result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;;
159                dbus_message_unref(reply);
160             }
161             else
162             {
163                notifyStruct.ldbid       = atoi(ldbid);
164                notifyStruct.user_no     = atoi(user_no);
165                notifyStruct.seat_no     = atoi(seat_no);
166
167                // call the registered callback function
168                gChangeNotifyCallback(&notifyStruct);
169
170                result = DBUS_HANDLER_RESULT_HANDLED;
171             }
172             dbus_connection_flush(connection);
173          }
174       }
175    }
176    // org.genivi.persistence.admin  P R O P E R T Y
177    else  if((0==strcmp("org.freedesktop.DBus.Properties", dbus_message_get_interface(message))))
178    {
179       if(dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL)
180       {
181          if((0==strcmp("EggDBusChanged", dbus_message_get_member(message))))
182          {
183             DBusMessageIter array;
184             DBusMessageIter dict;
185             DBusMessageIter variant;
186
187             char* dictString = NULL;
188             int value = 0;
189
190             dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, 0, &dict);
191             dbus_message_iter_get_basic(&dict, &dictString);
192
193             dbus_message_iter_open_container(&dict,DBUS_TYPE_VARIANT, NULL, &variant);
194             dbus_message_iter_get_basic(&dict, &value);
195
196             dbus_message_iter_close_container(&dict, &variant);
197             dbus_message_iter_close_container(&array, &dict);
198
199             // to do handle signal
200             result = DBUS_HANDLER_RESULT_HANDLED;
201          }
202          else
203          {
204             DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("handleObjectPathMessageFallback -> unknown property:"), DLT_STRING(dbus_message_get_interface(message)) );
205          }
206       }
207       else
208       {
209          DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("handleObjectPathMessageFallback -> not a signal:"), DLT_STRING(dbus_message_get_member(message)) );
210       }
211    }
212
213    return result;
214 }
215
216
217
218 static void  unregisterObjectPathFallback(DBusConnection *connection, void *user_data)
219 {
220    DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("unregisterObjectPathFallback\n"));
221 }
222
223
224
225 void* run_mainloop(void* dataPtr)
226 {
227    // persistence admin message
228    static const struct DBusObjectPathVTable vtablePersAdmin
229       = {unregisterMessageHandler, checkPersAdminMsg, NULL, };
230
231    // lifecycle message
232    static const struct DBusObjectPathVTable vtableLifecycle
233       = {unregisterMessageHandler, checkLifecycleMsg, NULL, };
234
235    // fallback
236    static const struct DBusObjectPathVTable vtableFallback
237       = {unregisterObjectPathFallback, handleObjectPathMessageFallback, NULL, };
238
239    // setup the dbus
240    mainLoop(vtablePersAdmin, vtableLifecycle, vtableFallback, dataPtr);
241
242    return NULL;
243 }
244
245
246
247 int setup_dbus_mainloop(void)
248 {
249    int rval = 0;
250    pthread_t thread;
251    DBusError err;
252    const char *pAddress = getenv("PERS_CLIENT_DBUS_ADDRESS");
253
254    // enable locking of data structures in the D-Bus library for multi threading.
255    dbus_threads_init_default();
256
257    dbus_error_init(&err);
258
259    // wain until dbus main loop has been setup and running
260    pthread_mutex_lock(&gDbusInitializedMtx);
261
262    // Connect to the bus and check for errors
263    if(pAddress != NULL)
264    {
265       DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("setup_dbus_mainloop -> Use specific dbus address:"), DLT_STRING(pAddress) );
266
267       gDbusConn = dbus_connection_open_private(pAddress, &err);
268
269       if(gDbusConn != NULL)
270       {
271          if(!dbus_bus_register(gDbusConn, &err))
272          {
273             DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("dbus_bus_register() Error :"), DLT_STRING(err.message) );
274             dbus_error_free (&err);
275             return -1;
276          }
277       }
278       else
279       {
280          DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("dbus_connection_open_private() Error :"), DLT_STRING(err.message) );
281          dbus_error_free(&err);
282          return -1;
283       }
284    }
285    else
286    {
287       DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("Use default dbus bus (DBUS_BUS_SYSTEM)"));
288
289       gDbusConn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
290    }
291
292    // create here the dbus connection and pass to main loop
293    rval = pthread_create(&thread, NULL, run_mainloop, gDbusConn);
294    if(rval)
295    {
296      DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("pthread_create( DBUS run_mainloop ) returned an error:"), DLT_INT(rval) );
297      return -1;
298    }
299
300    // wait for condition variable
301    pthread_cond_wait(&gDbusInitializedCond, &gDbusInitializedMtx);
302
303    pthread_mutex_unlock(&gDbusInitializedMtx);
304    return rval;
305 }
306
307
308
309
310
311 static dbus_bool_t addWatch(DBusWatch *watch, void *data)
312 {
313    dbus_bool_t result = FALSE;
314
315    if (ARRAY_SIZE(gPollInfo.fds)>gPollInfo.nfds)
316    {
317       int flags = dbus_watch_get_flags(watch);
318
319       tObjectEntry * const pEntry = &gPollInfo.objects[gPollInfo.nfds];
320       pEntry->objtype = OT_WATCH;
321       pEntry->watch = watch;
322
323       gPollInfo.fds[gPollInfo.nfds].fd = dbus_watch_get_unix_fd(watch);
324
325       if (TRUE==dbus_watch_get_enabled(watch))
326       {
327          if (flags&DBUS_WATCH_READABLE)
328          {
329             gPollInfo.fds[gPollInfo.nfds].events |= POLLIN;
330          }
331          if (flags&DBUS_WATCH_WRITABLE)
332          {
333             gPollInfo.fds[gPollInfo.nfds].events |= POLLOUT;
334          }
335
336          ++gPollInfo.nfds;
337       }
338
339       result = TRUE;
340    }
341
342    return result;
343 }
344
345
346
347 static void removeWatch(DBusWatch *watch, void *data)
348 {
349    void* w_data = dbus_watch_get_data(watch);
350
351    DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("removeWatch called "), DLT_INT( (int)watch) );
352
353    if(w_data)
354       free(w_data);
355
356    dbus_watch_set_data(watch, NULL, NULL);
357 }
358
359
360
361 static void watchToggled(DBusWatch *watch, void *data)
362 {
363    DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("watchToggled called "), DLT_INT( (int)watch) );
364
365    if(dbus_watch_get_enabled(watch))
366       addWatch(watch, data);
367    else
368       removeWatch(watch, data);
369 }
370
371
372
373 static dbus_bool_t addTimeout(DBusTimeout *timeout, void *data)
374 {
375    dbus_bool_t ret = FALSE;
376
377    if (ARRAY_SIZE(gPollInfo.fds)>gPollInfo.nfds)
378    {
379       const int interval = dbus_timeout_get_interval(timeout);
380       if ((0<interval)&&(TRUE==dbus_timeout_get_enabled(timeout)))
381       {
382          const int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
383          if (-1!=tfd)
384          {
385             const struct itimerspec its = { .it_value= {interval/1000, interval%1000} };
386             if (-1!=timerfd_settime(tfd, 0, &its, NULL))
387             {
388                tObjectEntry * const pEntry = &gPollInfo.objects[gPollInfo.nfds];
389                pEntry->objtype = OT_TIMEOUT;
390                pEntry->timeout = timeout;
391                gPollInfo.fds[gPollInfo.nfds].fd = tfd;
392                gPollInfo.fds[gPollInfo.nfds].events |= POLLIN;
393                ++gPollInfo.nfds;
394                ret = TRUE;
395             }
396             else
397             {
398                DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("addTimeout => timerfd_settime() failed"), DLT_STRING(strerror(errno)) );
399             }
400          }
401          else
402          {
403             DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("addTimeout => timerfd_create() failed"), DLT_STRING(strerror(errno)) );
404          }
405       }
406    }
407    else
408    {
409       DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("addTimeout => cannot create another fd to be poll()'ed"));
410    }
411
412    return ret;
413 }
414
415
416
417 static void removeTimeout(DBusTimeout *timeout, void *data)
418 {
419
420    int i = gPollInfo.nfds;
421    while ((0<i--)&&(timeout!=gPollInfo.objects[i].timeout));
422
423    if (0<i)
424    {
425       if (-1==close(gPollInfo.fds[i].fd))
426       {
427          DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("removeTimeout => close() timerfd"), DLT_STRING(strerror(errno)) );
428       }
429
430       --gPollInfo.nfds;
431       while (gPollInfo.nfds>i)
432       {
433          gPollInfo.fds[i] = gPollInfo.fds[i+1];
434          gPollInfo.objects[i] = gPollInfo.objects[i+1];
435          ++i;
436       }
437
438       gPollInfo.fds[gPollInfo.nfds].fd = -1;
439       gPollInfo.objects[gPollInfo.nfds].objtype = OT_NONE;
440    }
441 }
442
443
444
445 /** callback for libdbus' when timeout changed */
446 static void timeoutToggled(DBusTimeout *timeout, void *data)
447 {
448    int i = gPollInfo.nfds;
449    while ((0<i--)&&(timeout!=gPollInfo.objects[i].timeout));
450    DLT_LOG(gDLTContext, DLT_LOG_INFO, DLT_STRING("timeoutToggled") );
451    if (0<i)
452    {
453       const int interval = (TRUE==dbus_timeout_get_enabled(timeout))?dbus_timeout_get_interval(timeout):0;
454       const struct itimerspec its = { .it_value= {interval/1000, interval%1000} };
455       if (-1!=timerfd_settime(gPollInfo.fds[i].fd, 0, &its, NULL))
456       {
457          DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("timeoutToggled => timerfd_settime()"), DLT_STRING(strerror(errno)) );
458       }
459    }
460 }
461
462
463
464 int mainLoop(DBusObjectPathVTable vtable, DBusObjectPathVTable vtable2,
465              DBusObjectPathVTable vtableFallback, void* userData)
466 {
467    DBusError err;
468    // lock mutex to make sure dbus main loop is running
469    pthread_mutex_lock(&gDbusInitializedMtx);
470
471
472    DBusConnection* conn = (DBusConnection*)userData;
473    dbus_error_init(&err);
474
475    if (dbus_error_is_set(&err))
476    {
477       DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("mainLoop => Connection Error:"), DLT_STRING(err.message) );
478       dbus_error_free(&err);
479    }
480    else if (NULL != conn)
481    {
482       dbus_connection_set_exit_on_disconnect (conn, FALSE);
483       if (-1 == (gEfds = eventfd(0, 0)))
484       {
485          DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("mainLoop => eventfd() failed w/ errno:"), DLT_INT(errno) );
486       }
487       else
488       {
489          int ret;
490          memset(&gPollInfo, 0 , sizeof(gPollInfo));
491
492          gPollInfo.nfds = 1;
493          gPollInfo.fds[0].fd = gEfds;
494          gPollInfo.fds[0].events = POLLIN;
495
496          dbus_bus_add_match(conn, "type='signal',interface='org.genivi.persistence.admin',member='PersistenceModeChanged',path='/org/genivi/persistence/admin'", &err);
497
498          // register for messages
499          if (   (TRUE==dbus_connection_register_object_path(conn, "/org/genivi/persistence/adminconsumer", &vtable, userData))
500              && (TRUE==dbus_connection_register_object_path(conn, "/org/genivi/NodeStateManager/LifeCycleConsumer", &vtable2, userData))
501              && (TRUE==dbus_connection_register_fallback(conn, "/", &vtableFallback, userData)) )
502          {
503             if(   (TRUE!=dbus_connection_set_watch_functions(conn, addWatch, removeWatch, watchToggled, NULL, NULL))
504                || (TRUE!=dbus_connection_set_timeout_functions(conn, addTimeout, removeTimeout, timeoutToggled, NULL, NULL)) )
505             {
506                DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("mainLoop => dbus_connection_set_watch_functions() failed"));
507             }
508             else
509             {
510                pthread_cond_signal(&gDbusInitializedCond);
511                pthread_mutex_unlock(&gDbusInitializedMtx);
512                do
513                {
514                   bContinue = 0; /* assume error */
515
516                   while (DBUS_DISPATCH_DATA_REMAINS==dbus_connection_dispatch(conn));
517
518                   while ((-1==(ret=poll(gPollInfo.fds, gPollInfo.nfds, -1)))&&(EINTR==errno));
519
520                   if (0>ret)
521                   {
522                      DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("mainLoop => poll() failed w/ errno "), DLT_INT(errno) );
523                   }
524                   else if (0==ret)
525                   {
526                      /* poll time-out */
527                   }
528                   else
529                   {
530                      int i;
531
532                      for (i=0; gPollInfo.nfds>i; ++i)
533                      {
534                         /* anything to do */
535                         if (0!=gPollInfo.fds[i].revents)
536                         {
537                            if (OT_TIMEOUT==gPollInfo.objects[i].objtype)
538                            {
539                               /* time-out occured */
540                               unsigned long long nExpCount = 0;
541                               if ((ssize_t)sizeof(nExpCount)!=read(gPollInfo.fds[i].fd, &nExpCount, sizeof(nExpCount)))
542                               {
543                                  DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("mainLoop => read failed"));
544                               }
545                               DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("mainLoop => timeout"));
546
547                               if (FALSE==dbus_timeout_handle(gPollInfo.objects[i].timeout))
548                               {
549                                  DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("mainLoop => dbus_timeout_handle() failed!?"));
550                               }
551                               bContinue = TRUE;
552                            }
553                            else if (gPollInfo.fds[i].fd==gEfds)
554                            {
555                               /* internal command */
556                               if (0!=(gPollInfo.fds[i].revents & POLLIN))
557                               {
558                                  uint16_t buf[64];
559                                  bContinue = TRUE;
560                                  while ((-1==(ret=read(gPollInfo.fds[i].fd, buf, 64)))&&(EINTR==errno));
561                                  if (0>ret)
562                                  {
563                                     DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("mainLoop => read() failed"), DLT_STRING(strerror(errno)) );
564                                  }
565                                  else if (ret != -1)
566                                  {
567                                     switch (buf[0])
568                                     {
569                                        case CMD_PAS_BLOCK_AND_WRITE_BACK:
570                                           process_block_and_write_data_back((buf[2]), buf[1]);
571                                           break;
572                                        case CMD_LC_PREPARE_SHUTDOWN:
573                                           process_prepare_shutdown((buf[2]), buf[1]);
574                                           break;
575                                        case CMD_QUIT:
576                                           bContinue = FALSE;
577                                           break;
578                                        default:
579                                           DLT_LOG(gDLTContext, DLT_LOG_ERROR, DLT_STRING("mainLoop => command not handled"), DLT_INT(buf[0]) );
580                                           break;
581                                     }
582                                  }
583                               }
584                            }
585                            else
586                            {
587                               int flags = 0;
588                               if (0!=(gPollInfo.fds[i].revents & POLLIN))
589                               {
590                                  flags |= DBUS_WATCH_READABLE;
591                               }
592                               if (0!=(gPollInfo.fds[i].revents & POLLOUT))
593                               {
594                                  flags |= DBUS_WATCH_WRITABLE;
595                               }
596                               if (0!=(gPollInfo.fds[i].revents & POLLERR))
597                               {
598                                  flags |= DBUS_WATCH_ERROR;
599                               }
600                               if (0!=(gPollInfo.fds[i].revents & POLLHUP))
601                               {
602                                  flags |= DBUS_WATCH_HANGUP;
603                               }
604
605                               bContinue = dbus_watch_handle(gPollInfo.objects[i].watch, flags);
606                            }
607                         }
608                      }
609                   }
610                }
611                while (0!=bContinue);
612             }
613             dbus_connection_unregister_object_path(conn, "/org/genivi/persistence/adminconsumer");
614             dbus_connection_unregister_object_path(conn, "/org/genivi/NodeStateManager/LifeCycleConsumer");
615             dbus_connection_unregister_object_path(conn, "/");
616          }
617          close(gEfds);
618       }
619       //dbus_connection_close(conn);
620       dbus_connection_unref(conn);
621       dbus_shutdown();
622    }
623
624    pthread_cond_signal(&gDbusInitializedCond);
625    pthread_mutex_unlock(&gDbusInitializedMtx);
626    return 0;
627 }
628