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