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