1 /******************************************************************************
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 ******************************************************************************/
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.
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 "persistence_client_library_dbus_cmd.h"
24 #include "persistence_client_library_data_organization.h"
34 pthread_cond_t gDbusInitializedCond = PTHREAD_COND_INITIALIZER;
35 pthread_mutex_t gDbusInitializedMtx = PTHREAD_MUTEX_INITIALIZER;
37 pthread_mutex_t gDbusPendingRegMtx = PTHREAD_MUTEX_INITIALIZER;
40 pthread_mutex_t gDeliverpMtx = PTHREAD_MUTEX_INITIALIZER;
42 pthread_mutex_t gMainCondMtx = PTHREAD_MUTEX_INITIALIZER;
43 pthread_cond_t gMainLoopCond = PTHREAD_COND_INITIALIZER;
45 pthread_t gMainLoopThread;
48 int gEfds; // communication channel int dbus mainloop
51 typedef enum EDBusObjectType
59 typedef struct SObjectEntry
61 tDBusObjectType objtype; /** libdbus' object */
64 DBusWatch * watch; /** watch "object" */
65 DBusTimeout * timeout; /** timeout "object" */
72 typedef struct SPollInfo
75 struct pollfd fds[10];
76 tObjectEntry objects[10];
80 /// polling information
81 static tPollInfo gPollInfo;
85 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
88 /* function to unregister ojbect path message handler */
89 static void unregisterMessageHandler(DBusConnection *connection, void *user_data)
93 DLT_LOG(gPclDLTContext, DLT_LOG_INFO, DLT_STRING("unregisterObjectPath\n"));
96 /* catches messages not directed to any registered object path ("garbage collector") */
97 static DBusHandlerResult handleObjectPathMessageFallback(DBusConnection * connection, DBusMessage * message, void * user_data)
99 DBusHandlerResult result = DBUS_HANDLER_RESULT_HANDLED;
102 // org.genivi.persistence.admin S I G N A L
103 if((0==strcmp("org.genivi.persistence.admin", dbus_message_get_interface(message))))
105 if(dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL)
107 if((0==strcmp("PersistenceModeChanged", dbus_message_get_member(message))))
109 // to do handle signal
110 result = signal_persModeChange(connection, message);
114 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("handleObjectPathMessageFallback -> unknown signal:"), DLT_STRING(dbus_message_get_interface(message)) );
118 // org.genivi.persistence.admin S I G N A L
119 else if((0==strcmp("org.genivi.persistence.adminconsumer", dbus_message_get_interface(message))))
121 if(dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL)
123 pclNotification_s notifyStruct;
124 int validMessage = 0;
126 if((0==strcmp("PersistenceResChange", dbus_message_get_member(message))))
128 notifyStruct.pclKeyNotify_Status = pclNotifyStatus_changed;
131 else if((0==strcmp("PersistenceResDelete", dbus_message_get_member(message))))
133 notifyStruct.pclKeyNotify_Status = pclNotifyStatus_deleted;
136 else if((0==strcmp("PersistenceRes", dbus_message_get_member(message))))
138 notifyStruct.pclKeyNotify_Status = pclNotifyStatus_created;
142 if(validMessage == 1)
146 dbus_error_init (&error);
151 if (!dbus_message_get_args(message, &error, DBUS_TYPE_STRING, ¬ifyStruct.resource_id,
152 DBUS_TYPE_STRING, &ldbid,
153 DBUS_TYPE_STRING, &user_no,
154 DBUS_TYPE_STRING, &seat_no,
157 reply = dbus_message_new_error(message, error.name, error.message);
161 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("handleObjectPathMessageFallback => DBus No memory"), DLT_STRING(dbus_message_get_interface(message)) );
164 if (!dbus_connection_send(connection, reply, 0))
166 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("handleObjectPathMessageFallback => DBus No memory"), DLT_STRING(dbus_message_get_interface(message)) );
169 result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;;
170 dbus_message_unref(reply);
174 notifyStruct.ldbid = atoi(ldbid);
175 notifyStruct.user_no = atoi(user_no);
176 notifyStruct.seat_no = atoi(seat_no);
178 // call the registered callback function
179 if(gChangeNotifyCallback != NULL )
181 gChangeNotifyCallback(¬ifyStruct);
185 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("handleObjectPathMessageFallback => gChangeNotifyCallback is not set (possibly NULL)") );
187 result = DBUS_HANDLER_RESULT_HANDLED;
189 dbus_connection_flush(connection);
193 // org.genivi.persistence.admin P R O P E R T Y
194 else if((0==strcmp("org.freedesktop.DBus.Properties", dbus_message_get_interface(message))))
196 if(dbus_message_get_type(message) == DBUS_MESSAGE_TYPE_SIGNAL)
198 if((0==strcmp("EggDBusChanged", dbus_message_get_member(message))))
200 DBusMessageIter array;
201 DBusMessageIter dict;
202 DBusMessageIter variant;
204 char* dictString = NULL;
207 dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, 0, &dict);
208 dbus_message_iter_get_basic(&dict, &dictString);
210 dbus_message_iter_open_container(&dict,DBUS_TYPE_VARIANT, NULL, &variant);
211 dbus_message_iter_get_basic(&dict, &value);
213 dbus_message_iter_close_container(&dict, &variant);
214 dbus_message_iter_close_container(&array, &dict);
216 // to do handle signal
217 result = DBUS_HANDLER_RESULT_HANDLED;
221 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("handleObjectPathMessageFallback -> unknown property:"), DLT_STRING(dbus_message_get_interface(message)) );
226 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("handleObjectPathMessageFallback -> not a signal:"), DLT_STRING(dbus_message_get_member(message)) );
234 static void unregisterObjectPathFallback(DBusConnection *connection, void *user_data)
238 DLT_LOG(gPclDLTContext, DLT_LOG_INFO, DLT_STRING("unregisterObjectPathFallback\n"));
243 void* run_mainloop(void* dataPtr)
245 // persistence admin message
246 static const struct DBusObjectPathVTable vtablePersAdmin
247 = {unregisterMessageHandler, checkPersAdminMsg, NULL, NULL, NULL, NULL};
250 static const struct DBusObjectPathVTable vtableLifecycle
251 = {unregisterMessageHandler, checkLifecycleMsg, NULL, NULL, NULL, NULL};
254 static const struct DBusObjectPathVTable vtableFallback
255 = {unregisterObjectPathFallback, handleObjectPathMessageFallback, NULL, NULL, NULL, NULL};
258 mainLoop(vtablePersAdmin, vtableLifecycle, vtableFallback, dataPtr);
265 int setup_dbus_mainloop(void)
269 DBusConnection* conn = NULL;
271 const char *pAddress = getenv("PERS_CLIENT_DBUS_ADDRESS");
273 dbus_error_init(&err);
275 // wait until dbus main loop has been setup and running
276 pthread_mutex_lock(&gDbusInitializedMtx);
278 // Connect to the bus and check for errors
281 DLT_LOG(gPclDLTContext, DLT_LOG_INFO, DLT_STRING("setup_dbus_mainloop -> Use specific dbus address:"), DLT_STRING(pAddress) );
283 conn = dbus_connection_open_private(pAddress, &err);
287 if(!dbus_bus_register(conn, &err))
289 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("dbus_bus_register() Error :"), DLT_STRING(err.message) );
290 dbus_error_free (&err);
291 pthread_mutex_unlock(&gDbusInitializedMtx);
297 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("dbus_connection_open_private() Error :"), DLT_STRING(err.message) );
298 dbus_error_free(&err);
299 pthread_mutex_unlock(&gDbusInitializedMtx);
305 DLT_LOG(gPclDLTContext, DLT_LOG_INFO, DLT_STRING("Use default dbus bus (DBUS_BUS_SYSTEM)"));
307 conn = dbus_bus_get_private(DBUS_BUS_SYSTEM, &err);
310 // create here the dbus connection and pass to main loop
311 rval = pthread_create(&gMainLoopThread, NULL, run_mainloop, conn);
314 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("pthread_create( DBUS run_mainloop ) returned an error:"), DLT_INT(rval) );
315 pthread_mutex_unlock(&gDbusInitializedMtx);
319 // wait for condition variable
320 pthread_cond_wait(&gDbusInitializedCond, &gDbusInitializedMtx);
322 pthread_mutex_unlock(&gDbusInitializedMtx);
330 static dbus_bool_t addWatch(DBusWatch *watch, void *data)
332 dbus_bool_t result = FALSE;
335 if (ARRAY_SIZE(gPollInfo.fds)>gPollInfo.nfds)
337 int flags = dbus_watch_get_flags(watch);
339 tObjectEntry * const pEntry = &gPollInfo.objects[gPollInfo.nfds];
340 pEntry->objtype = OT_WATCH;
341 pEntry->watch = watch;
343 gPollInfo.fds[gPollInfo.nfds].fd = dbus_watch_get_unix_fd(watch);
345 if (TRUE==dbus_watch_get_enabled(watch))
347 if (flags&DBUS_WATCH_READABLE)
349 gPollInfo.fds[gPollInfo.nfds].events |= POLLIN;
351 if (flags&DBUS_WATCH_WRITABLE)
353 gPollInfo.fds[gPollInfo.nfds].events |= POLLOUT;
367 static void removeWatch(DBusWatch *watch, void *data)
369 void* w_data = dbus_watch_get_data(watch);
373 DLT_LOG(gPclDLTContext, DLT_LOG_INFO, DLT_STRING("removeWatch called "), DLT_INT( (int)watch) );
378 dbus_watch_set_data(watch, NULL, NULL);
383 static void watchToggled(DBusWatch *watch, void *data)
386 DLT_LOG(gPclDLTContext, DLT_LOG_INFO, DLT_STRING("watchToggled called "), DLT_INT( (int)watch) );
388 if(dbus_watch_get_enabled(watch))
389 addWatch(watch, data);
391 removeWatch(watch, data);
396 static dbus_bool_t addTimeout(DBusTimeout *timeout, void *data)
399 dbus_bool_t ret = FALSE;
401 if (ARRAY_SIZE(gPollInfo.fds)>gPollInfo.nfds)
403 const int interval = dbus_timeout_get_interval(timeout);
404 if ((0<interval)&&(TRUE==dbus_timeout_get_enabled(timeout)))
406 const int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC);
409 const struct itimerspec its = { .it_value= {interval/1000, interval%1000} };
410 if (-1!=timerfd_settime(tfd, 0, &its, NULL))
412 tObjectEntry * const pEntry = &gPollInfo.objects[gPollInfo.nfds];
413 pEntry->objtype = OT_TIMEOUT;
414 pEntry->timeout = timeout;
415 gPollInfo.fds[gPollInfo.nfds].fd = tfd;
416 gPollInfo.fds[gPollInfo.nfds].events |= POLLIN;
422 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("addTimeout => timerfd_settime() failed"), DLT_STRING(strerror(errno)) );
427 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("addTimeout => timerfd_create() failed"), DLT_STRING(strerror(errno)) );
433 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("addTimeout => cannot create another fd to be poll()'ed"));
441 static void removeTimeout(DBusTimeout *timeout, void *data)
443 int i = gPollInfo.nfds;
446 while ((0<i--)&&(timeout!=gPollInfo.objects[i].timeout));
450 if (-1==close(gPollInfo.fds[i].fd))
452 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("removeTimeout => close() timerfd"), DLT_STRING(strerror(errno)) );
456 while (gPollInfo.nfds>i)
458 gPollInfo.fds[i] = gPollInfo.fds[i+1];
459 gPollInfo.objects[i] = gPollInfo.objects[i+1];
463 gPollInfo.fds[gPollInfo.nfds].fd = -1;
464 gPollInfo.objects[gPollInfo.nfds].objtype = OT_NONE;
470 /** callback for libdbus' when timeout changed */
471 static void timeoutToggled(DBusTimeout *timeout, void *data)
473 int i = gPollInfo.nfds;
476 while ((0<i--)&&(timeout!=gPollInfo.objects[i].timeout));
477 DLT_LOG(gPclDLTContext, DLT_LOG_INFO, DLT_STRING("timeoutToggled") );
480 const int interval = (TRUE==dbus_timeout_get_enabled(timeout))?dbus_timeout_get_interval(timeout):0;
481 const struct itimerspec its = { .it_value= {interval/1000, interval%1000} };
482 if (-1!=timerfd_settime(gPollInfo.fds[i].fd, 0, &its, NULL))
484 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("timeoutToggled => timerfd_settime()"), DLT_STRING(strerror(errno)) );
491 int mainLoop(DBusObjectPathVTable vtable, DBusObjectPathVTable vtable2,
492 DBusObjectPathVTable vtableFallback, void* userData)
495 // lock mutex to make sure dbus main loop is running
496 pthread_mutex_lock(&gDbusInitializedMtx);
498 DBusConnection* conn = (DBusConnection*)userData;
499 dbus_error_init(&err);
501 if (dbus_error_is_set(&err))
503 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("mainLoop => Connection Error:"), DLT_STRING(err.message) );
504 dbus_error_free(&err);
506 else if (NULL != conn)
508 dbus_connection_set_exit_on_disconnect(conn, FALSE);
509 if (-1 == (gEfds = eventfd(0, 0)))
511 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("mainLoop => eventfd() failed w/ errno:"), DLT_INT(errno) );
516 memset(&gPollInfo, 0 , sizeof(gPollInfo));
519 gPollInfo.fds[0].fd = gEfds;
520 gPollInfo.fds[0].events = POLLIN;
522 dbus_bus_add_match(conn, "type='signal',interface='org.genivi.persistence.admin',member='PersistenceModeChanged',path='/org/genivi/persistence/admin'", &err);
524 // register for messages
525 if ( (TRUE==dbus_connection_register_object_path(conn, "/org/genivi/NodeStateManager/LifeCycleConsumer", &vtable2, userData))
526 #if USE_PASINTERFACE == 1
527 && (TRUE==dbus_connection_register_object_path(conn, "/org/genivi/persistence/adminconsumer", &vtable, userData))
529 && (TRUE==dbus_connection_register_fallback(conn, "/", &vtableFallback, userData)) )
531 if( (TRUE!=dbus_connection_set_watch_functions(conn, addWatch, removeWatch, watchToggled, NULL, NULL))
532 || (TRUE!=dbus_connection_set_timeout_functions(conn, addTimeout, removeTimeout, timeoutToggled, NULL, NULL)) )
534 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("mainLoop => dbus_connection_set_watch_functions() failed"));
538 pthread_cond_signal(&gDbusInitializedCond);
539 pthread_mutex_unlock(&gDbusInitializedMtx);
542 bContinue = 0; /* assume error */
544 while (DBUS_DISPATCH_DATA_REMAINS==dbus_connection_dispatch(conn));
546 while ((-1==(ret=poll(gPollInfo.fds, gPollInfo.nfds, 750)))&&(EINTR==errno));
550 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("mainLoop => poll() failed w/ errno "), DLT_INT(errno) );
560 for (i=0; gPollInfo.nfds>i; ++i)
563 if (0!=gPollInfo.fds[i].revents)
565 if (OT_TIMEOUT==gPollInfo.objects[i].objtype)
567 /* time-out occured */
568 unsigned long long nExpCount = 0;
569 if ((ssize_t)sizeof(nExpCount)!=read(gPollInfo.fds[i].fd, &nExpCount, sizeof(nExpCount)))
571 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("mainLoop => read failed"));
573 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("mainLoop => timeout"));
575 if (FALSE==dbus_timeout_handle(gPollInfo.objects[i].timeout))
577 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("mainLoop => dbus_timeout_handle() failed!?"));
581 else if (gPollInfo.fds[i].fd == gEfds)
583 /* internal command */
584 if (0!=(gPollInfo.fds[i].revents & POLLIN))
588 while ((-1==(ret = read(gPollInfo.fds[i].fd, buf, 64)))&&(EINTR == errno));
591 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("mainLoop => read() failed"), DLT_STRING(strerror(errno)) );
595 pthread_mutex_lock(&gMainCondMtx);
598 case CMD_PAS_BLOCK_AND_WRITE_BACK:
599 process_block_and_write_data_back((buf[2]), buf[1]);
600 process_send_pas_request(conn, (buf[2]), buf[1]);
602 case CMD_LC_PREPARE_SHUTDOWN:
603 process_prepare_shutdown(Shutdown_Full);
604 process_send_lifecycle_request(conn, (buf[2]), buf[1]);
606 case CMD_SEND_NOTIFY_SIGNAL:
607 process_send_notification_signal(conn);
609 case CMD_REG_NOTIFY_SIGNAL:
610 process_reg_notification_signal(conn);
612 case CMD_SEND_PAS_REGISTER:
613 process_send_pas_register(conn, (buf[1]), buf[2]);
615 case CMD_SEND_LC_REGISTER:
616 process_send_lifecycle_register(conn, (buf[1]), buf[2]);
622 // ******************************************************
624 case CMD_SEND_LC_REQUEST: // remove
625 process_send_lifecycle_request(conn, (buf[2]), buf[1]);
627 case CMD_SEND_PAS_REQUEST: /// remove
628 process_send_pas_request(conn, (buf[2]), buf[1]);
631 // ******************************************************
633 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("mainLoop => command not handled"), DLT_INT(buf[0]) );
636 pthread_cond_signal(&gMainLoopCond);
637 pthread_mutex_unlock(&gMainCondMtx);
644 if (0!=(gPollInfo.fds[i].revents & POLLIN))
646 flags |= DBUS_WATCH_READABLE;
648 if (0!=(gPollInfo.fds[i].revents & POLLOUT))
650 flags |= DBUS_WATCH_WRITABLE;
652 if (0!=(gPollInfo.fds[i].revents & POLLERR))
654 flags |= DBUS_WATCH_ERROR;
656 if (0!=(gPollInfo.fds[i].revents & POLLHUP))
658 flags |= DBUS_WATCH_HANGUP;
661 bContinue = dbus_watch_handle(gPollInfo.objects[i].watch, flags);
667 while (0!=bContinue);
669 #if USE_PASINTERFACE == 1
670 dbus_connection_unregister_object_path(conn, "/org/genivi/persistence/adminconsumer");
672 dbus_connection_unregister_object_path(conn, "/org/genivi/NodeStateManager/LifeCycleConsumer");
673 dbus_connection_unregister_object_path(conn, "/");
677 dbus_connection_close(conn);
678 dbus_connection_unref(conn);
682 pthread_cond_signal(&gDbusInitializedCond);
683 pthread_mutex_unlock(&gDbusInitializedMtx);
689 int deliverToMainloop(tCmd mainloopCmd, unsigned int param1, unsigned int param2)
693 pthread_mutex_lock(&gDeliverpMtx);
696 pthread_mutex_lock(&gMainCondMtx);
698 deliverToMainloop_NM(mainloopCmd, param1, param2);
700 pthread_cond_wait(&gMainLoopCond, &gMainCondMtx);
701 pthread_mutex_unlock(&gMainCondMtx);
704 pthread_mutex_unlock(&gDeliverpMtx);
709 int deliverToMainloop_NM(tCmd mainloopCmd, unsigned int param1, unsigned int param2)
714 cmd = ( ((uint64_t)param2 << 32) | ((uint64_t)param1 << 16) | mainloopCmd);
716 if(-1 == write(gEfds, &cmd, (sizeof(uint64_t))))
718 DLT_LOG(gPclDLTContext, DLT_LOG_ERROR, DLT_STRING("deliverToMainloop => failed to write to pipe"), DLT_INT(errno));