Adjusted dbus interface names
[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
208    // enable locking of data structures in the D-Bus library for multi threading.
209    dbus_threads_init_default();
210
211    dbus_error_init(&err);
212
213    // Connect to the bus and check for errors
214    if(pAddress != NULL)
215    {
216       printf("Use specific dbus address: %s\n !", pAddress);
217       gDbusConn = dbus_connection_open(pAddress, &err);
218
219       if(gDbusConn != NULL)
220       {
221          if(!dbus_bus_register(gDbusConn, &err))
222          {
223             printf("dbus_bus_register() Error %s\n", err.message);
224             dbus_error_free (&err);
225             return -1;
226          }
227          else
228          {
229             printf("Registered connection successfully !\n");
230          }
231       }
232       else
233       {
234          printf("dbus_connection_open() Error %s\n",err.message);
235          dbus_error_free(&err);
236       }
237    }
238    else
239    {
240       printf("Use default dbus bus (DBUS_BUS_SYSTEM) !!!!!!\n");
241       gDbusConn = dbus_bus_get(DBUS_BUS_SYSTEM, &err);
242    }
243
244    // wain until dbus main loop has been setup and running
245    pthread_mutex_lock(&gDbusInitializedMtx);
246
247    // create here the dbus connection and pass to main loop
248    rval = pthread_create(&thread, NULL, run_mainloop, gDbusConn);
249    if(rval)
250    {
251      fprintf(stderr, "Server: - ERROR! pthread_create( run_mainloop ) returned: %d\n", rval);
252    }
253
254    // wait for condition variable
255    pthread_cond_wait(&gDbusInitializedCond, &gDbusInitializedMtx);
256
257    pthread_mutex_unlock(&gDbusInitializedMtx);
258    return rval;
259 }
260
261
262
263
264
265 static dbus_bool_t addWatch(DBusWatch *watch, void *data)
266 {
267    dbus_bool_t result = FALSE;
268
269    //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':'-');
270
271    if (ARRAY_SIZE(gPollInfo.fds)>gPollInfo.nfds)
272    {
273       int flags = dbus_watch_get_flags(watch);
274
275       tObjectEntry * const pEntry = &gPollInfo.objects[gPollInfo.nfds];
276       pEntry->objtype = OT_WATCH;
277       pEntry->watch = watch;
278
279       gPollInfo.fds[gPollInfo.nfds].fd = dbus_watch_get_unix_fd(watch);
280
281       if (TRUE==dbus_watch_get_enabled(watch))
282       {
283          if (flags&DBUS_WATCH_READABLE)
284          {
285             gPollInfo.fds[gPollInfo.nfds].events |= POLLIN;
286          }
287          if (flags&DBUS_WATCH_WRITABLE)
288          {
289             gPollInfo.fds[gPollInfo.nfds].events |= POLLOUT;
290          }
291
292          ++gPollInfo.nfds;
293       }
294
295       result = TRUE;
296    }
297
298    return result;
299 }
300
301
302
303 static void removeWatch(DBusWatch *watch, void *data)
304 {
305    printf("removeWatch called @0x%08x\n", (int)watch);
306 }
307
308
309
310 static void watchToggled(DBusWatch *watch, void *data)
311 {
312    printf("watchToggled called @0x%08x\n", (int)watch);
313 }
314
315
316
317 static dbus_bool_t addTimeout(DBusTimeout *timeout, void *data)
318 {
319    dbus_bool_t ret = FALSE;
320
321    if (ARRAY_SIZE(gPollInfo.fds)>gPollInfo.nfds)
322    {
323       const int interval = dbus_timeout_get_interval(timeout);
324       if ((0<interval)&&(TRUE==dbus_timeout_get_enabled(timeout)))
325       {
326          const int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
327          if (-1!=tfd)
328          {
329             const struct itimerspec its = { .it_value= {interval/1000, interval%1000} };
330             if (-1!=timerfd_settime(tfd, 0, &its, NULL))
331             {
332                tObjectEntry * const pEntry = &gPollInfo.objects[gPollInfo.nfds];
333                pEntry->objtype = OT_TIMEOUT;
334                pEntry->timeout = timeout;
335                gPollInfo.fds[gPollInfo.nfds].fd = tfd;
336                gPollInfo.fds[gPollInfo.nfds].events |= POLLIN;
337                ++gPollInfo.nfds;
338                ret = TRUE;
339             }
340             else
341             {
342                fprintf(stderr, "timerfd_settime() failed %d '%s'\n", errno, strerror(errno));
343             }
344          }
345          else
346          {
347             fprintf(stderr, "timerfd_create() failed %d '%s'\n", errno, strerror(errno));
348          }
349       }
350    }
351    else
352    {
353       fprintf(stderr, "cannot create another fd to be poll()'ed\n");
354    }
355
356    return ret;
357 }
358
359
360
361 static void removeTimeout(DBusTimeout *timeout, void *data)
362 {
363
364    int i = gPollInfo.nfds;
365    while ((0<i--)&&(timeout!=gPollInfo.objects[i].timeout));
366
367    if (0<i)
368    {
369       if (-1==close(gPollInfo.fds[i].fd))
370       {
371          fprintf(stderr, "close() timerfd #%d failed %d '%s'\n", gPollInfo.fds[i].fd, errno, strerror(errno));
372       }
373
374       --gPollInfo.nfds;
375       while (gPollInfo.nfds>i)
376       {
377          gPollInfo.fds[i] = gPollInfo.fds[i+1];
378          gPollInfo.objects[i] = gPollInfo.objects[i+1];
379          ++i;
380       }
381
382       gPollInfo.fds[gPollInfo.nfds].fd = -1;
383       gPollInfo.objects[gPollInfo.nfds].objtype = OT_NONE;
384    }
385 }
386
387
388
389 /** callback for libdbus' when timeout changed */
390 static void timeoutToggled(DBusTimeout *timeout, void *data)
391 {
392    int i = gPollInfo.nfds;
393    while ((0<i--)&&(timeout!=gPollInfo.objects[i].timeout));
394    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);
395    if (0<i)
396    {
397       const int interval = (TRUE==dbus_timeout_get_enabled(timeout))?dbus_timeout_get_interval(timeout):0;
398       const struct itimerspec its = { .it_value= {interval/1000, interval%1000} };
399       if (-1!=timerfd_settime(gPollInfo.fds[i].fd, 0, &its, NULL))
400       {
401          fprintf(stderr, "timerfd_settime() %d failed %d '%s'\n", interval, errno, strerror(errno));
402       }
403    }
404 }
405
406
407
408 int mainLoop(DBusObjectPathVTable vtable, DBusObjectPathVTable vtable2,
409              DBusObjectPathVTable vtableFallback, void* userData)
410 {
411    DBusError err;
412    // lock mutex to make sure dbus main loop is running
413    pthread_mutex_lock(&gDbusInitializedMtx);
414
415    signal(SIGTERM, sigHandler);
416    signal(SIGQUIT, sigHandler);
417    signal(SIGINT,  sigHandler);
418
419    DBusConnection* conn = (DBusConnection*)userData;
420    dbus_error_init(&err);
421
422    if (dbus_error_is_set(&err))
423    {
424       printf("Connection Error (%s)\n", err.message);
425       dbus_error_free(&err);
426    }
427    else if (NULL != conn)
428    {
429       dbus_connection_set_exit_on_disconnect (conn, FALSE);
430       //printf("connected as '%s'\n", dbus_bus_get_unique_name(conn));
431       if (-1 == (gEfds = eventfd(0, 0)))
432       {
433          printf("eventfd() failed w/ errno %d\n", errno);
434       }
435       else
436       {
437          int ret;
438          int bContinue = 0;
439          memset(&gPollInfo, 0 , sizeof(gPollInfo));
440
441          gPollInfo.nfds = 1;
442          gPollInfo.fds[0].fd = gEfds;
443          gPollInfo.fds[0].events = POLLIN;
444
445          dbus_bus_add_match(conn, "type='signal',interface='org.genivi.persistence.admin',member='PersistenceModeChanged',path='/org/genivi/persistence/admin'", &err);
446
447          // register for messages
448          if (   (TRUE==dbus_connection_register_object_path(conn, "/org/genivi/persistence/adminconsumer", &vtable, userData))
449              && (TRUE==dbus_connection_register_object_path(conn, "/org/genivi/NodeStateManager/LifeCycleConsumer", &vtable2, userData))
450              && (TRUE==dbus_connection_register_fallback(conn, "/", &vtableFallback, userData)) )
451          {
452             if(   (TRUE!=dbus_connection_set_watch_functions(conn, addWatch, removeWatch, watchToggled, NULL, NULL))
453                || (TRUE!=dbus_connection_set_timeout_functions(conn, addTimeout, removeTimeout, timeoutToggled, NULL, NULL)) )
454             {
455                printf("dbus_connection_set_watch_functions() failed\n");
456             }
457             else
458             {
459                pthread_cond_signal(&gDbusInitializedCond);
460                pthread_mutex_unlock(&gDbusInitializedMtx);
461                do
462                {
463                   bContinue = 0; /* assume error */
464
465                   while (DBUS_DISPATCH_DATA_REMAINS==dbus_connection_dispatch(conn));
466
467                   while ((-1==(ret=poll(gPollInfo.fds, gPollInfo.nfds, -1)))&&(EINTR==errno));
468
469                   if (0>ret)
470                   {
471                      fprintf(stderr, "poll() failed w/ errno %d\n", errno);
472                   }
473                   else if (0==ret)
474                   {
475                      /* poll time-out */
476                   }
477                   else
478                   {
479                      int i;
480
481                      for (i=0; gPollInfo.nfds>i; ++i)
482                      {
483                         /* anything to do */
484                         if (0!=gPollInfo.fds[i].revents)
485                         {
486                            //fprintf(stderr, "\t[%d] revents 0x%04x\n", i, gPollInfo.fds[i].revents);
487
488                            if (OT_TIMEOUT==gPollInfo.objects[i].objtype)
489                            {
490                               /* time-out occured */
491                               unsigned long long nExpCount = 0;
492                               if ((ssize_t)sizeof(nExpCount)!=read(gPollInfo.fds[i].fd, &nExpCount, sizeof(nExpCount)))
493                               {
494                                  fprintf(stderr, "read failed!?\n");
495                               }
496                               fprintf(stderr, "timeout %x #%d!\n", (int)gPollInfo.objects[i].timeout, (int)nExpCount);
497                               if (FALSE==dbus_timeout_handle(gPollInfo.objects[i].timeout))
498                               {
499                                  fprintf(stderr, "dbus_timeout_handle() failed!?\n");
500                               }
501                               bContinue = TRUE;
502                            }
503                            else if (gPollInfo.fds[i].fd==gEfds)
504                            {
505                               /* internal command */
506                               if (0!=(gPollInfo.fds[i].revents & POLLIN))
507                               {
508                                  uint16_t buf[64];
509                                  bContinue = TRUE;
510                                  while ((-1==(ret=read(gPollInfo.fds[i].fd, buf, 64)))&&(EINTR==errno));
511                                  if (0>ret)
512                                  {
513                                     printf("read() failed w/ errno %d | %s\n", errno, strerror(errno));
514                                  }
515                                  else if (ret != -1)
516                                  {
517                                     switch (buf[0])
518                                     {
519                                        case CMD_PAS_BLOCK_AND_WRITE_BACK:
520                                           process_block_and_write_data_back((buf[2]), buf[1]);
521                                           break;
522                                        case CMD_LC_PREPARE_SHUTDOWN:
523                                           process_prepare_shutdown((buf[2]), buf[1]);
524                                           break;
525                                        case CMD_QUIT:
526                                           bContinue = FALSE;
527                                           break;
528                                        default:
529                                           printf("command %d not handled!\n", buf[0]);
530                                           break;
531                                     }
532                                  }
533                               }
534                            }
535                            else
536                            {
537                               int flags = 0;
538                               if (0!=(gPollInfo.fds[i].revents & POLLIN))
539                               {
540                                  flags |= DBUS_WATCH_READABLE;
541                               }
542                               if (0!=(gPollInfo.fds[i].revents & POLLOUT))
543                               {
544                                  flags |= DBUS_WATCH_WRITABLE;
545                               }
546                               if (0!=(gPollInfo.fds[i].revents & POLLERR))
547                               {
548                                  flags |= DBUS_WATCH_ERROR;
549                               }
550                               if (0!=(gPollInfo.fds[i].revents & POLLHUP))
551                               {
552                                  flags |= DBUS_WATCH_HANGUP;
553                               }
554
555                               bContinue = dbus_watch_handle(gPollInfo.objects[i].watch, flags);
556                            }
557                         }
558                      }
559                   }
560                   if(endLoop == 1)
561                      break;
562                }
563                while (0!=bContinue);
564             }
565             dbus_connection_unregister_object_path(conn, "/org/genivi/persistence/adminconsumer");
566             dbus_connection_unregister_object_path(conn, "/org/genivi/NodeStateManager/LifeCycleConsumer");
567             dbus_connection_unregister_object_path(conn, "/");
568          }
569          close(gEfds);
570       }
571       dbus_connection_unref(conn);
572       dbus_shutdown();
573    }
574
575    pthread_cond_signal(&gDbusInitializedCond);
576    pthread_mutex_unlock(&gDbusInitializedMtx);
577    return 0;
578 }
579