Move from mulit-threaded to single-threaded dbus communication
[profile/ivi/persistence-client-library.git] / test / persistence_lifeCycle_mockup.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_admin_service_mockup.c
13  * @ingroup        Persistence client library test
14  * @author         Ingo Huerner
15  * @brief          Persistence Administration Serivce mockup
16  * @see            
17  */
18
19 #include <stdio.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <errno.h>
23 #include <unistd.h>     /* exit */
24 #include <check.h>
25 #include <time.h>
26 #include <fcntl.h>
27 #include <sys/mman.h>
28 #include <dbus/dbus.h>
29 #include <poll.h>
30 #include <pthread.h>
31 #include <sys/eventfd.h>
32
33
34 /// command definitions for main loop
35 typedef enum ECmd
36 {
37    CMD_NONE = 0,                    /// command none
38    CMD_PAS_BLOCK_AND_WRITE_BACK,    /// command block access and write data back
39    CMD_LC_PREPARE_SHUTDOWN,         /// command to prepare shutdown
40    CMD_QUIT,                         /// quit command
41    CMD_REQUEST_NAME
42 } tCmd;
43
44
45 /// pipe file descriptors
46 int gEfds;
47
48
49
50 pthread_mutex_t gDbusInitializedMtx  = PTHREAD_MUTEX_INITIALIZER;
51 pthread_cond_t  gDbusInitializedCond = PTHREAD_COND_INITIALIZER;
52
53 /// polling structure
54 typedef struct SPollInfo
55 {
56    int nfds;
57    struct pollfd fds[10];
58    DBusWatch * watches[10];
59 } tPollInfo;
60
61
62 /// polling information
63 static tPollInfo gPollInfo;
64
65
66 /// dbus connection
67 DBusConnection* gDbusConn = NULL;
68
69
70 DBusConnection* get_dbus_connection(void)
71 {
72    return gDbusConn;
73 }
74
75 //------------------------------------------------------------------------
76 // debugging only until "correct" exit of main loop is possible!!!!!
77 //------------------------------------------------------------------------
78 #include "signal.h"
79 static int endLoop = 0;
80
81 void sigHandler(int signo)
82 {
83    endLoop = 1;
84 }
85 //------------------------------------------------------------------------
86
87
88
89 #define ARRAY_SIZE(a) (sizeof(a)/sizeof(a[0]))
90
91
92
93 int checkAdminMsg(DBusConnection *connection, DBusMessage *message, int reg)
94 {
95    char* busName   = NULL;
96    char* objName = NULL;
97    int32_t  notificationFlag = 0;
98    uint32_t gTimeoutMs = 0;
99    int msgReturn = 123321;
100
101    DBusMessage *reply;
102    DBusError error;
103    dbus_error_init (&error);
104
105    if(reg == 1)
106    {
107       if (!dbus_message_get_args (message, &error, DBUS_TYPE_STRING, &busName,  // bus name
108                                                    DBUS_TYPE_STRING, &objName,
109                                                    DBUS_TYPE_UINT32,  &notificationFlag,
110                                                    DBUS_TYPE_UINT32, &gTimeoutMs,
111                                                    DBUS_TYPE_INVALID))
112       {
113          reply = dbus_message_new_error(message, error.name, error.message);
114
115          if (reply == 0)
116          {
117             //DLT_LOG(mgrContext, DLT_LOG_ERROR, DLT_STRING("DBus No memory"));
118             printf("DBus No memory\n");
119          }
120
121          if (!dbus_connection_send(connection, reply, 0))
122          {
123             //DLT_LOG(mgrContext, DLT_LOG_ERROR, DLT_STRING("DBus No memory"));
124             printf("DBus No memory\n");
125          }
126
127          dbus_message_unref(reply);
128
129          return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
130       }
131    }
132    else if(reg == 0)
133    {
134       if (!dbus_message_get_args (message, &error, DBUS_TYPE_STRING, &busName,  // bus name
135                                                    DBUS_TYPE_STRING, &objName,
136                                                    DBUS_TYPE_UINT32,  &notificationFlag,
137                                                    DBUS_TYPE_INVALID))
138       {
139          reply = dbus_message_new_error(message, error.name, error.message);
140
141          if (reply == 0)
142          {
143             //DLT_LOG(mgrContext, DLT_LOG_ERROR, DLT_STRING("DBus No memory"));
144             printf("DBus No memory\n");
145          }
146
147          if (!dbus_connection_send(connection, reply, 0))
148          {
149             //DLT_LOG(mgrContext, DLT_LOG_ERROR, DLT_STRING("DBus No memory"));
150             printf("DBus No memory\n");
151          }
152
153          dbus_message_unref(reply);
154
155          return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
156       }
157    }
158
159
160    printf("   checkAdminMsg ==> busName: %s | objName: %s | notificationFlag: %u | gTimeoutMs: %d\n\n", busName, objName, notificationFlag, gTimeoutMs);
161    reply = dbus_message_new_method_return(message);
162
163    if (reply == 0)
164    {
165      //DLT_LOG(mgrContext, DLT_LOG_ERROR, DLT_STRING("DBus No memory"));
166       printf("DBus No memory\n");
167    }
168
169    if (!dbus_message_append_args(reply, DBUS_TYPE_INT32, &msgReturn, DBUS_TYPE_INVALID))
170    {
171      //DLT_LOG(mgrContext, DLT_LOG_ERROR, DLT_STRING("DBus No memory"));
172       printf("DBus No memory\n");
173    }
174
175    if (!dbus_connection_send(connection, reply, NULL))
176    {
177      //DLT_LOG(mgrContext, DLT_LOG_ERROR, DLT_STRING("DBus No memory"));
178       printf("DBus No memory\n");
179    }
180
181    dbus_connection_flush(connection);
182    dbus_message_unref(reply);
183
184    return DBUS_HANDLER_RESULT_HANDLED;
185 }
186
187
188
189 DBusHandlerResult checkPersAdminMsg(DBusConnection * connection, DBusMessage * message, void * user_data)
190 {
191    DBusHandlerResult result = DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
192
193    //printf("checkPersAdminMsg '%s' -> '%s'\n", dbus_message_get_interface(message), dbus_message_get_member(message));
194    if((0==strcmp("org.genivi.NodeStateManager.LifeCycleConsumer", dbus_message_get_interface(message))))
195    {
196       if((0==strcmp("RegisterShutdownClient", dbus_message_get_member(message))))
197       {
198          printf(" ==> org.genivi.NodeStateManager.LifeCycleConsumer - received - ==> RegisterShutdownClient \n");
199
200          result = checkAdminMsg(connection, message, 1);
201       }
202       else if((0==strcmp("UnRegisterShutdownClient", dbus_message_get_member(message))))
203       {
204          printf(" ==> org.genivi.NodeStateManager.LifeCycleConsumer - received - ==> UnRegisterShutdownClient \n");
205
206          result = checkAdminMsg(connection, message, 0);
207       }
208       else if((0==strcmp("LifecycleRequestComplete", dbus_message_get_member(message))))
209       {
210          printf(" ==> org.genivi.NodeStateManager.LifeCycleConsumer - received - ==> LifecycleRequestComplete \n");
211
212          result = checkAdminMsg(connection, message, 0);
213       }
214
215       else
216       {
217          printf(" ==> org.genivi.NodeStateManager.LifeCycleConsumer - received U N KN O W N-'%s'\n", dbus_message_get_interface(message));
218       }
219    }
220    else
221    {
222       printf(" ==> org.genivi.NodeStateManager - received U N KN O W N-'%s'\n", dbus_message_get_interface(message));
223    }
224    return result;
225 }
226
227
228 /* function to unregister ojbect path message handler */
229 static void unregisterMessageHandler(DBusConnection *connection, void *user_data)
230 {
231    printf("unregisterObjectPath\n");
232 }
233
234 /* catches messages not directed to any registered object path ("garbage collector") */
235 static DBusHandlerResult handleObjectPathMessageFallback(DBusConnection * connection, DBusMessage * message, void * user_data)
236 {
237    DBusHandlerResult result = DBUS_HANDLER_RESULT_HANDLED;
238
239    printf("handleObjectPathMessageFallback Object: '%s' -> Interface: '%s' -> Message: '%s'\n",
240           dbus_message_get_sender(message), dbus_message_get_interface(message), dbus_message_get_member(message) );
241
242    return result;
243 }
244
245
246
247 static void  unregisterObjectPathFallback(DBusConnection *connection, void *user_data)
248 {
249    printf("unregisterObjectPathFallback\n");
250 }
251
252
253
254
255
256
257 static dbus_bool_t addWatch(DBusWatch *watch, void *data)
258 {
259    dbus_bool_t result = FALSE;
260
261    //printf("addWatch called @%08x flags: %08x enabled: %c\n", (unsigned int)watch, dbus_watch_get_flags(watch), TRUE==dbus_watch_get_enabled(watch)?'x':'-');
262
263    if (ARRAY_SIZE(gPollInfo.fds)>gPollInfo.nfds)
264    {
265       int flags = dbus_watch_get_flags(watch);
266
267       gPollInfo.watches[gPollInfo.nfds] = watch;
268
269       gPollInfo.fds[gPollInfo.nfds].fd = dbus_watch_get_unix_fd(watch);
270
271       if (TRUE==dbus_watch_get_enabled(watch))
272       {
273          if (flags&DBUS_WATCH_READABLE)
274          {
275             gPollInfo.fds[gPollInfo.nfds].events |= POLLIN;
276          }
277          if (flags&DBUS_WATCH_WRITABLE)
278          {
279             gPollInfo.fds[gPollInfo.nfds].events |= POLLOUT;
280          }
281
282          ++gPollInfo.nfds;
283          /* wakeup main-loop, just in case */
284          static const uint64_t cmd = CMD_REQUEST_NAME;
285          if (sizeof(uint64_t)!=write(gEfds, &cmd, sizeof(uint64_t)))
286          {
287             fprintf(stderr, "write failed w/ errno %d\n", errno);
288          }
289       }
290
291       result = TRUE;
292    }
293
294    return result;
295 }
296
297
298 static void removeWatch(DBusWatch *watch, void *data)
299 {
300    void* w_data = dbus_watch_get_data(watch);
301
302    printf("removeWatch called @0x%08x\n", (int)watch);
303
304    if(w_data)
305       free(w_data);
306
307    dbus_watch_set_data(watch, NULL, NULL);
308 }
309
310
311 static void watchToggled(DBusWatch *watch, void *data)
312 {
313    printf("watchToggled called @0x%08x\n", (int)watch);
314
315    if(dbus_watch_get_enabled(watch))
316       addWatch(watch, data);
317    else
318       removeWatch(watch, data);
319 }
320
321
322
323 int mainLoop(DBusObjectPathVTable vtable, DBusObjectPathVTable vtableFallback, void* userData)
324 {
325    DBusError err;
326    // lock mutex to make sure dbus main loop is running
327    pthread_mutex_lock(&gDbusInitializedMtx);
328
329    signal(SIGTERM, sigHandler);
330    signal(SIGQUIT, sigHandler);
331    signal(SIGINT,  sigHandler);
332
333    DBusConnection* conn = (DBusConnection*)userData;
334    dbus_error_init(&err);
335
336    if (dbus_error_is_set(&err))
337    {
338       printf("Connection Error (%s)\n", err.message);
339       dbus_error_free(&err);
340    }
341    else if (NULL != conn)
342    {
343       dbus_connection_set_exit_on_disconnect (conn, FALSE);
344       printf("connected as '%s'\n", dbus_bus_get_unique_name(conn));
345       if (-1 == (gEfds = eventfd(0, 0)))
346       {
347          printf("eventfd() failed w/ errno %d\n", errno);
348       }
349       else
350       {
351          int ret;
352          int bContinue = 0;
353          memset(&gPollInfo, 0 , sizeof(gPollInfo));
354
355          gPollInfo.nfds = 1;
356          gPollInfo.fds[0].fd = gEfds;
357          gPollInfo.fds[0].events = POLLIN;
358
359          // register for messages
360          if (   (TRUE==dbus_connection_register_object_path(conn, "/org/genivi/NodeStateManager", &vtable, userData))
361              && (TRUE==dbus_connection_register_fallback(conn, "/", &vtableFallback, userData)) )
362          {
363             if (TRUE!=dbus_connection_set_watch_functions(conn, addWatch, removeWatch, watchToggled, NULL, NULL))
364             {
365                printf("dbus_connection_set_watch_functions() failed\n");
366             }
367             else
368             {
369                uint16_t buf[64];
370
371                pthread_cond_signal(&gDbusInitializedCond);
372                pthread_mutex_unlock(&gDbusInitializedMtx);
373                do
374                {
375                   bContinue = 0; /* assume error */
376
377                   while(DBUS_DISPATCH_DATA_REMAINS==dbus_connection_dispatch(conn));
378
379                   while((-1==(ret=poll(gPollInfo.fds, gPollInfo.nfds, 500)))&&(EINTR==errno));
380
381                   if(0>ret)
382                   {
383                      printf("poll() failed w/ errno %d\n", errno);
384                   }
385                   else
386                   {
387                      int i;
388                      bContinue = 1;
389
390                      for (i=0; gPollInfo.nfds>i; ++i)
391                      {
392                         if (0!=gPollInfo.fds[i].revents)
393                         {
394                            if (gPollInfo.fds[i].fd==gEfds)
395                            {
396                               if (0!=(gPollInfo.fds[i].revents & POLLIN))
397                               {
398                                  bContinue = TRUE;
399                                  while ((-1==(ret=read(gPollInfo.fds[i].fd, buf, 64)))&&(EINTR==errno));
400                                  if (0>ret)
401                                  {
402                                     printf("read() failed w/ errno %d | %s\n", errno, strerror(errno));
403                                  }
404                                  else if (ret != -1)
405                                  {
406                                     switch (buf[0])
407                                     {
408                                     case CMD_REQUEST_NAME:
409                                        if (DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER !=dbus_bus_request_name(conn, "org.genivi.NodeStateManager", DBUS_NAME_FLAG_DO_NOT_QUEUE, &err))
410                                        {
411                                           fprintf(stderr, "Cannot acquire name 'org.genivi.NodeStateManager': \n    \"(%s)\". Bailing out!\n", err.message);
412                                           dbus_error_free(&err);
413                                           bContinue = FALSE;
414                                        }
415                                        break;
416                                        case CMD_QUIT:
417                                           bContinue = FALSE;
418                                           break;
419                                        default:
420                                           printf("command %d not handled!\n", buf[0]);
421                                           break;
422                                     }
423                                  }
424                               }
425                            }
426                            else
427                            {
428                               int flags = 0;
429                               if (0!=(gPollInfo.fds[i].revents & POLLIN))
430                               {
431                                  flags |= DBUS_WATCH_READABLE;
432                               }
433                               if (0!=(gPollInfo.fds[i].revents & POLLOUT))
434                               {
435                                  flags |= DBUS_WATCH_WRITABLE;
436                               }
437                               if (0!=(gPollInfo.fds[i].revents & POLLERR))
438                               {
439                                  flags |= DBUS_WATCH_ERROR;
440                               }
441                               if (0!=(gPollInfo.fds[i].revents & POLLHUP))
442                               {
443                                  flags |= DBUS_WATCH_HANGUP;
444                               }
445                               //printf("handle watch @0x%08x flags: %04x\n", (int)gPollInfo.watches[i], flags);
446                               bContinue = dbus_watch_handle(gPollInfo.watches[i], flags);
447                            }
448                         }
449                      }
450                   }
451                   if(endLoop == 1)
452                      break;
453                }
454                while (0!=bContinue);
455             }
456             dbus_connection_unregister_object_path(conn, "/org/genivi/NodeStateManager");
457             dbus_connection_unregister_object_path(conn, "/");
458          }
459          close(gEfds);
460       }
461       dbus_connection_close(conn);
462       dbus_connection_unref(conn);
463       dbus_shutdown();
464    }
465
466    pthread_cond_signal(&gDbusInitializedCond);
467    pthread_mutex_unlock(&gDbusInitializedMtx);
468    return 0;
469 }
470
471
472 void* run_mainloop(void* dataPtr)
473 {
474    // persistence admin message
475    static const struct DBusObjectPathVTable vtablePersAdmin
476       = {unregisterMessageHandler, checkPersAdminMsg, NULL, };
477
478    // fallback
479    static const struct DBusObjectPathVTable vtableFallback
480       = {unregisterObjectPathFallback, handleObjectPathMessageFallback, NULL, };
481
482    // setup the dbus
483    mainLoop(vtablePersAdmin, vtableFallback, dataPtr);
484
485    printf("Exit dbus main loop!!!!\n");
486
487    return NULL;
488 }
489
490
491 int setup_dbus_mainloop(void)
492 {
493    int rval = 0;
494    pthread_t thread;
495    DBusError err;
496    const char *pAddress = getenv("PERS_CLIENT_DBUS_ADDRESS");
497    dbus_error_init(&err);
498
499    // enable locking of data structures in the D-Bus library for multi threading.
500    dbus_threads_init_default();
501
502    // Connect to the bus and check for errors
503    if(pAddress != NULL)
504    {
505       printf("Use specific dbus address: %s\n !", pAddress);
506       gDbusConn = dbus_connection_open_private(pAddress, &err);
507
508       if(gDbusConn != NULL)
509       {
510          if(!dbus_bus_register(gDbusConn, &err))
511          {
512             printf("dbus_bus_register() Error %s\n", err.message);
513             dbus_error_free (&err);
514             return -1;
515          }
516          else
517          {
518             printf("Registered connection successfully !\n");
519          }
520       }
521       else
522       {
523          printf("dbus_connection_open() Error %s\n",err.message);
524          dbus_error_free(&err);
525       }
526    }
527    else
528    {
529       printf("Use default dbus bus!!!!!!\n");
530       gDbusConn = dbus_bus_get_private(DBUS_BUS_SYSTEM, &err);
531    }
532
533    // wain until dbus main loop has been setup and running
534    pthread_mutex_lock(&gDbusInitializedMtx);
535
536    // create here the dbus connection and pass to main loop
537    rval = pthread_create(&thread, NULL, run_mainloop, gDbusConn);
538    if(rval)
539    {
540      fprintf(stderr, "Server: - ERROR! pthread_create( run_mainloop ) returned: %d\n", rval);
541    }
542
543    // wait for condition variable
544    pthread_cond_wait(&gDbusInitializedCond, &gDbusInitializedMtx);
545
546    pthread_mutex_unlock(&gDbusInitializedMtx);
547    return rval;
548 }
549
550
551
552 int main(int argc, char *argv[])
553 {
554    setup_dbus_mainloop();
555
556    printf("Wait, press enter to exit!!\n");
557    getchar();
558    printf("Exiting Persistence Lifecycle mockup!!\n");
559
560    return 0;
561 }
562