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