Move deregistration of shutdown consumers into LAHandlerService
[profile/ivi/node-startup-controller.git] / boot-manager / la-handler-service.c
1 /* vi:set et ai sw=2 sts=2 ts=2: */
2 /* -
3  * Copyright (c) 2012 GENIVI.
4  *
5  * This Source Code Form is subject to the terms of the Mozilla Public
6  * License, v. 2.0. If a copy of the MPL was not distributed with this
7  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8  */
9
10 #ifdef HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13
14 #include <glib-object.h>
15 #include <gio/gio.h>
16
17 #include <dlt/dlt.h>
18
19 #include <common/la-handler-dbus.h>
20 #include <common/nsm-consumer-dbus.h>
21 #include <common/nsm-enum-types.h>
22 #include <common/shutdown-client.h>
23 #include <common/shutdown-consumer-dbus.h>
24
25 #include <boot-manager/job-manager.h>
26 #include <boot-manager/la-handler-service.h>
27
28
29
30 DLT_IMPORT_CONTEXT (la_handler_context);
31
32
33
34 /* property identifiers */
35 enum
36 {
37   PROP_0,
38   PROP_CONNECTION,
39   PROP_JOB_MANAGER,
40 };
41
42
43
44 typedef struct _LAHandlerServiceData LAHandlerServiceData;
45
46
47
48 static void                  la_handler_service_constructed                              (GObject               *object);
49 static void                  la_handler_service_finalize                                 (GObject               *object);
50 static void                  la_handler_service_get_property                             (GObject               *object,
51                                                                                           guint                  prop_id,
52                                                                                           GValue                *value,
53                                                                                           GParamSpec            *pspec);
54 static void                  la_handler_service_set_property                             (GObject               *object,
55                                                                                           guint                  prop_id,
56                                                                                           const GValue          *value,
57                                                                                           GParamSpec            *pspec);
58 static gboolean              la_handler_service_handle_register                          (LAHandler             *interface,
59                                                                                           GDBusMethodInvocation *invocation,
60                                                                                           const gchar           *unit,
61                                                                                           NSMShutdownType        mode,
62                                                                                           guint                  timeout,
63                                                                                           LAHandlerService      *service);
64 static void                  la_handler_service_handle_register_finish                   (GObject               *object,
65                                                                                           GAsyncResult          *res,
66                                                                                           gpointer               user_data);
67 static gboolean              la_handler_service_handle_consumer_lifecycle_request        (ShutdownConsumer      *consumer,
68                                                                                           GDBusMethodInvocation *invocation,
69                                                                                           guint                  request,
70                                                                                           guint                  request_id,
71                                                                                           ShutdownClient        *client);
72 static void                  la_handler_service_handle_consumer_lifecycle_request_finish (JobManager            *manager,
73                                                                                           const gchar           *unit,
74                                                                                           const gchar           *result,
75                                                                                           GError                *error,
76                                                                                           gpointer               user_data);
77 static LAHandlerServiceData *la_handler_service_data_new                                 (LAHandlerService      *service,
78                                                                                           GDBusMethodInvocation *invocation,
79                                                                                           guint                  request_id);
80 static void                  la_handler_service_data_unref                               (LAHandlerServiceData  *data);
81
82
83
84 struct _LAHandlerServiceClass
85 {
86   GObjectClass __parent__;
87 };
88
89 struct _LAHandlerService
90 {
91   GObject          __parent__;
92
93   GDBusConnection *connection;
94   LAHandler       *interface;
95   JobManager      *job_manager;
96
97   /* Associations of shutdown clients and their units */
98   GHashTable      *units_to_clients;
99   GHashTable      *clients_to_units;
100
101   const gchar     *prefix;
102   guint            index;
103   guint            bus_name_id;
104
105   /* connection to the NSM consumer interface */
106   NSMConsumer     *nsm_consumer;
107 };
108
109 struct _LAHandlerServiceData
110 {
111   GDBusMethodInvocation *invocation;
112   LAHandlerService      *service;
113   guint                  request_id;
114 };
115
116
117
118 G_DEFINE_TYPE (LAHandlerService, la_handler_service, G_TYPE_OBJECT);
119
120
121
122 static void
123 la_handler_service_class_init (LAHandlerServiceClass *klass)
124 {
125   GObjectClass *gobject_class;
126
127   gobject_class = G_OBJECT_CLASS (klass);
128   gobject_class->constructed = la_handler_service_constructed;
129   gobject_class->finalize = la_handler_service_finalize;
130   gobject_class->get_property = la_handler_service_get_property;
131   gobject_class->set_property = la_handler_service_set_property;
132
133   g_object_class_install_property (gobject_class,
134                                    PROP_CONNECTION,
135                                    g_param_spec_object ("connection",
136                                                         "connection",
137                                                         "connection",
138                                                         G_TYPE_DBUS_CONNECTION,
139                                                         G_PARAM_READWRITE |
140                                                         G_PARAM_CONSTRUCT_ONLY |
141                                                         G_PARAM_STATIC_STRINGS));
142   g_object_class_install_property (gobject_class,
143                                    PROP_JOB_MANAGER,
144                                    g_param_spec_object ("job-manager",
145                                                         "Job Manager",
146                                                         "The internal handler of Start()"
147                                                         " and Stop() jobs",
148                                                         TYPE_JOB_MANAGER,
149                                                         G_PARAM_READWRITE |
150                                                         G_PARAM_CONSTRUCT_ONLY |
151                                                         G_PARAM_STATIC_STRINGS));
152 }
153
154
155
156 static void
157 la_handler_service_constructed (GObject *object)
158 {
159   LAHandlerService *service = LA_HANDLER_SERVICE (object);
160   GError           *error = NULL;
161   gchar            *log_text;
162
163   /* connect to the node state manager */
164   service->nsm_consumer =
165     nsm_consumer_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE,
166                                          "com.contiautomotive.NodeStateManager",
167                                          "/com/contiautomotive/NodeStateManager/Consumer",
168                                           NULL, &error);
169   if (error != NULL)
170     {
171       log_text = g_strdup_printf ("Error occurred connecting to NSM Consumer: %s",
172                                   error->message);
173       DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
174       g_free (log_text);
175       g_error_free (error);
176     }
177 }
178
179
180
181 static void
182 la_handler_service_init (LAHandlerService *service)
183 {
184   service->interface = la_handler_skeleton_new ();
185
186   /* the number that follows the prefix in the shutdown client's object path,
187    * making every shutdown client unique */
188   service->index = 1;
189
190   /* the string that precedes the index in the shutdown client's object path */
191   service->prefix = "/org/genivi/BootManager1/ShutdownConsumer";
192
193   /* initialize the association of shutdown client to units */
194   service->units_to_clients = g_hash_table_new_full (g_str_hash, g_str_equal,
195                                                      (GDestroyNotify) g_free,
196                                                      (GDestroyNotify) g_object_unref);
197   service->clients_to_units = g_hash_table_new_full (g_direct_hash, g_direct_equal,
198                                                      (GDestroyNotify) g_object_unref,
199                                                      (GDestroyNotify) g_free);
200
201   /* implement the Register() handler */
202   g_signal_connect (service->interface, "handle-register",
203                     G_CALLBACK (la_handler_service_handle_register),
204                     service);
205 }
206
207
208
209 static void
210 la_handler_service_finalize (GObject *object)
211 {
212   LAHandlerService *service = LA_HANDLER_SERVICE (object);
213
214   /* release the bus name */
215   g_bus_unown_name (service->bus_name_id);
216
217   /* release the D-Bus connection object */
218   if (service->connection != NULL)
219     g_object_unref (service->connection);
220
221   /* release the interface skeleton */
222   g_signal_handlers_disconnect_matched (service->interface,
223                                         G_SIGNAL_MATCH_DATA,
224                                         0, 0, NULL, NULL, service);
225   g_object_unref (service->interface);
226
227   /* release the job manager skeleton */
228   g_object_unref (service->job_manager);
229
230   /* release the shutdown clients */
231   g_hash_table_unref (service->units_to_clients);
232   g_hash_table_unref (service->clients_to_units);
233
234   (*G_OBJECT_CLASS (la_handler_service_parent_class)->finalize) (object);
235 }
236
237
238
239 static void
240 la_handler_service_get_property (GObject    *object,
241                                  guint       prop_id,
242                                  GValue     *value,
243                                  GParamSpec *pspec)
244 {
245   LAHandlerService *service = LA_HANDLER_SERVICE (object);
246
247   switch (prop_id)
248     {
249     case PROP_CONNECTION:
250       g_value_set_object (value, service->connection);
251       break;
252     case PROP_JOB_MANAGER:
253       g_value_set_object (value, service->job_manager);
254       break;
255     default:
256       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
257       break;
258     }
259 }
260
261
262
263 static void
264 la_handler_service_set_property (GObject      *object,
265                                  guint         prop_id,
266                                  const GValue *value,
267                                  GParamSpec   *pspec)
268 {
269   LAHandlerService *service = LA_HANDLER_SERVICE (object);
270
271   switch (prop_id)
272     {
273     case PROP_CONNECTION:
274       service->connection = g_value_dup_object (value);
275       break;
276     case PROP_JOB_MANAGER:
277       service->job_manager = g_value_dup_object (value);
278       break;
279     default:
280       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
281       break;
282     }
283 }
284
285
286
287 static gboolean
288 la_handler_service_handle_register (LAHandler             *interface,
289                                     GDBusMethodInvocation *invocation,
290                                     const gchar           *unit,
291                                     NSMShutdownType        shutdown_mode,
292                                     guint                  timeout,
293                                     LAHandlerService      *service)
294 {
295   ShutdownConsumer *consumer;
296   ShutdownClient   *client;
297   GError           *error = NULL;
298   const gchar      *existing_bus_name;
299   const gchar      *existing_object_path;
300   gchar            *bus_name;
301   gchar            *log_text;
302   gchar            *object_path;
303
304   g_return_val_if_fail (IS_LA_HANDLER (interface), FALSE);
305   g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);
306   g_return_val_if_fail (unit != NULL && *unit != '\0', FALSE);
307   g_return_val_if_fail (LA_HANDLER_IS_SERVICE (service), FALSE);
308
309   /* find out if we have a shutdown client for this unit already */
310   client = g_hash_table_lookup (service->units_to_clients, unit);
311   if (client != NULL)
312     {
313       /* there already is a shutdown client for the unit, so simply
314        * re-register its client with the new shutdown mode and timeout */
315
316       /* extract information from the client */
317       consumer = shutdown_client_get_consumer (client);
318       existing_bus_name = shutdown_client_get_bus_name (client);
319       existing_object_path = shutdown_client_get_object_path (client);
320
321       /* temporarily store a reference to the legacy app handler service object
322        * in the invocation object */
323       g_object_set_data_full (G_OBJECT (invocation), "la-handler-service",
324                               g_object_ref (service), (GDestroyNotify) g_object_unref);
325
326       /* re-register the shutdown consumer with the NSM Consumer */
327       nsm_consumer_call_register_shutdown_client (service->nsm_consumer,
328                                                   existing_bus_name, existing_object_path,
329                                                   shutdown_mode, timeout, NULL,
330                                                   la_handler_service_handle_register_finish,
331                                                   invocation);
332     }
333   else
334     {
335       /* create a new shutdown client and consumer for the unit */
336       bus_name = "org.genivi.BootManager1";
337       object_path = g_strdup_printf ("%s/%u", service->prefix, service->index);
338       client = shutdown_client_new (bus_name, object_path, shutdown_mode, timeout);
339       consumer = shutdown_consumer_skeleton_new ();
340       shutdown_client_set_consumer (client, consumer);
341
342       /* remember the legacy app handler service object in shutdown client */
343       g_object_set_data_full (G_OBJECT (client), "la-handler-service",
344                               g_object_ref (service), (GDestroyNotify) g_object_unref);
345
346       /* implement the LifecycleRequest method of the shutdown consumer */
347       g_signal_connect (consumer, "handle-lifecycle-request",
348                         G_CALLBACK (la_handler_service_handle_consumer_lifecycle_request),
349                         client);
350
351       /* associate the shutdown client with the unit name */
352       g_hash_table_insert (service->units_to_clients, g_strdup (unit),
353                            g_object_ref (client));
354       g_hash_table_insert (service->clients_to_units, g_object_ref (client),
355                            g_strdup (unit));
356
357       /* export the shutdown consumer on the bus */
358       g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (consumer),
359                                         service->connection, object_path, &error);
360       if (error != NULL)
361         {
362           log_text = g_strdup_printf ("Failed to export shutdown consumer on the bus: %s",
363                                       error->message);
364           DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
365           g_free (log_text);
366           g_error_free (error);
367         }
368
369       /* temporarily store a reference to the legacy app handler service object
370        * in the invocation object */
371       g_object_set_data_full (G_OBJECT (invocation), "la-handler-service",
372                               g_object_ref (service), (GDestroyNotify) g_object_unref);
373
374       /* register the shutdown consumer with the NSM Consumer */
375       nsm_consumer_call_register_shutdown_client (service->nsm_consumer,
376                                                   bus_name, object_path,
377                                                   shutdown_mode, timeout, NULL,
378                                                   la_handler_service_handle_register_finish,
379                                                   invocation);
380
381       /* free strings and release the shutdown consumer */
382       g_free (object_path);
383       g_object_unref (consumer);
384
385       /* increment the counter for our shutdown consumer object paths */
386       service->index++;
387     }
388
389   return TRUE;
390 }
391
392
393
394 static void
395 la_handler_service_handle_register_finish (GObject      *object,
396                                            GAsyncResult *res,
397                                            gpointer      user_data)
398 {
399   GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (user_data);
400   LAHandlerService      *service;
401   NSMConsumer           *nsm_consumer = NSM_CONSUMER (object);
402   GError                *error = NULL;
403   gchar                 *log_text;
404   gint                   error_code;
405
406   g_return_if_fail (IS_NSM_CONSUMER (nsm_consumer));
407   g_return_if_fail (G_IS_ASYNC_RESULT (res));
408   g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation));
409
410   /* finish registering the shutdown client */
411   nsm_consumer_call_register_shutdown_client_finish (nsm_consumer, &error_code, res,
412                                                      &error);
413   if (error != NULL)
414     {
415       log_text = g_strdup_printf ("Failed to register a shutdown consumer: %s",
416                                   error->message);
417       DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
418       g_free (log_text);
419       g_error_free (error);
420     }
421
422   /* retrieve the LAHandlerService from the invocation object */
423   service = g_object_get_data (G_OBJECT (invocation), "la-handler-service");
424
425   /* notify the caller that we have handled the registration request */
426   la_handler_complete_register (service->interface, invocation);
427 }
428
429
430
431 static gboolean
432 la_handler_service_handle_consumer_lifecycle_request (ShutdownConsumer      *consumer,
433                                                       GDBusMethodInvocation *invocation,
434                                                       guint                  request,
435                                                       guint                  request_id,
436                                                       ShutdownClient        *client)
437 {
438   LAHandlerServiceData *data;
439   LAHandlerService     *service;
440   gchar                *unit_name;
441
442   g_return_val_if_fail (IS_SHUTDOWN_CONSUMER (consumer), FALSE);
443   g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);
444   g_return_val_if_fail (IS_SHUTDOWN_CLIENT (client), FALSE);
445
446   /* get the service from the shutdown client */
447   service = g_object_get_data (G_OBJECT (client), "la-handler-service");
448
449   /* look up the unit name associated with this shutdown client */
450   unit_name = g_hash_table_lookup (service->clients_to_units, client);
451
452   if (unit_name != NULL)
453     {
454       data = la_handler_service_data_new (service, NULL, request_id);
455
456       /* stop this unit now */
457       job_manager_stop (service->job_manager, unit_name, NULL,
458                         la_handler_service_handle_consumer_lifecycle_request_finish,
459                         data);
460
461       /* let the NSM know that we are working on this request */
462       shutdown_consumer_complete_lifecycle_request (consumer, invocation,
463                                                     NSM_ERROR_STATUS_RESPONSE_PENDING);
464     }
465   else
466     {
467       /* NSM asked us to shutdown a shutdown consumer we did not register;
468        * make it aware by returning an error */
469       shutdown_consumer_complete_lifecycle_request (consumer, invocation,
470                                                     NSM_ERROR_STATUS_ERROR);
471     }
472
473   return TRUE;
474 }
475
476
477
478 static void
479 la_handler_service_handle_consumer_lifecycle_request_finish (JobManager  *manager,
480                                                              const gchar *unit,
481                                                              const gchar *result,
482                                                              GError      *error,
483                                                              gpointer     user_data)
484 {
485   LAHandlerServiceData *data = (LAHandlerServiceData *)user_data;
486   GError               *err = NULL;
487   gchar                *log_text;
488   gint                  status = NSM_ERROR_STATUS_OK;
489
490   g_return_if_fail (IS_JOB_MANAGER (manager));
491   g_return_if_fail (unit != NULL && *unit != '\0');
492   g_return_if_fail (result != NULL && *result != '\0');
493   g_return_if_fail (data != NULL);
494
495   /* log an error if shutting down the consumer has failed */
496   if (error != NULL)
497     {
498       log_text = g_strdup_printf ("Failed to shutdown a shutdown consumer: %s",
499                                   error->message);
500       DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
501       g_free (log_text);
502
503       /* send an error back to the NSM */
504       status = NSM_ERROR_STATUS_ERROR;
505     }
506
507   /* log an error if systemd failed to stop the consumer */
508   if (g_strcmp0 (result, "failed") == 0)
509     {
510       DLT_LOG (la_handler_context, DLT_LOG_ERROR,
511                DLT_STRING ("Failed to shutdown a shutdown consumer"));
512
513       /* send an error back to the NSM */
514       status = NSM_ERROR_STATUS_ERROR;
515     }
516
517   /* let the NSM know that we have handled the lifecycle request */
518   if (!nsm_consumer_call_lifecycle_request_complete_sync (data->service->nsm_consumer,
519                                                           data->request_id, status,
520                                                           NULL, NULL, &err))
521     {
522       log_text = g_strdup_printf ("Failed to notify Node State Manager about completed "
523                                   "lifecycle request: %s", err->message);
524       DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
525       g_free (log_text);
526       g_error_free (err);
527     }
528
529   la_handler_service_data_unref (data);
530 }
531
532
533
534 static LAHandlerServiceData *
535 la_handler_service_data_new (LAHandlerService      *service,
536                              GDBusMethodInvocation *invocation,
537                              guint                  request_id)
538 {
539   LAHandlerServiceData *data;
540
541   data = g_slice_new0 (LAHandlerServiceData);
542   if (service != NULL)
543     data->service = g_object_ref (service);
544   if (invocation != NULL)
545     data->invocation = g_object_ref (invocation);
546   data->request_id = request_id;
547
548   return data;
549 }
550
551
552
553 static void
554 la_handler_service_data_unref (LAHandlerServiceData *data)
555 {
556   if (data == NULL)
557     return;
558
559   if (data->invocation != NULL)
560     g_object_unref (data->invocation);
561   if (data->service != NULL)
562     g_object_unref (data->service);
563   g_slice_free (LAHandlerServiceData, data);
564 }
565
566
567
568 LAHandlerService *
569 la_handler_service_new (GDBusConnection *connection,
570                         JobManager      *job_manager)
571 {
572   g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
573   g_return_val_if_fail (IS_JOB_MANAGER (job_manager), NULL);
574
575   return g_object_new (LA_HANDLER_TYPE_SERVICE,
576                        "connection", connection,
577                        "job-manager", job_manager,
578                        NULL);
579 }
580
581
582 gboolean
583 la_handler_service_start (LAHandlerService *service,
584                            GError         **error)
585 {
586   g_return_val_if_fail (LA_HANDLER_IS_SERVICE (service), FALSE);
587   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
588
589   /* announce the org.genivi.BootManager1.LegacyAppHandler service on the bus */
590   return g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (service->interface),
591                                            service->connection,
592                                            "/org/genivi/BootManager1/LegacyAppHandler",
593                                            error);
594 }
595
596
597
598 NSMConsumer *
599 la_handler_service_get_nsm_consumer (LAHandlerService *service)
600 {
601   g_return_val_if_fail (LA_HANDLER_IS_SERVICE (service), NULL);
602
603   return service->nsm_consumer;
604 }
605
606
607
608 void
609 la_handler_service_deregister_consumers (LAHandlerService *service)
610 {
611   GHashTableIter  iter;
612   ShutdownClient *client;
613   const gchar    *bus_name;
614   const gchar    *object_path;
615   const gchar    *unit;
616   GError         *error = NULL;
617   gchar          *log_text;
618   gint            error_code;
619   gint            shutdown_mode;
620
621   g_return_if_fail (LA_HANDLER_IS_SERVICE (service));
622
623   g_hash_table_iter_init (&iter, service->clients_to_units);
624   while (g_hash_table_iter_next (&iter, (gpointer *)&client, (gpointer *)&unit))
625     {
626       /* extract data from the current client */
627       bus_name = shutdown_client_get_bus_name (client);
628       object_path = shutdown_client_get_object_path (client);
629       shutdown_mode = shutdown_client_get_shutdown_mode (client);
630
631       /* unregister the shutdown client */
632       nsm_consumer_call_un_register_shutdown_client_sync (service->nsm_consumer,
633                                                           bus_name, object_path,
634                                                           shutdown_mode, &error_code,
635                                                           NULL, &error);
636
637       if (error != NULL)
638         {
639           log_text = g_strdup_printf ("Failed to unregister shutdown client %s "
640                                       "for unit %s: %s", object_path, unit,
641                                       error->message);
642           DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
643           g_free (log_text);
644           g_error_free (error);
645         }
646       else if (error_code != NSM_ERROR_STATUS_OK)
647         {
648           log_text = g_strdup_printf ("Failed to unregister shutdown client %s "
649                                       "for unit %s: error code %d", object_path, unit,
650                                       error_code);
651           DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
652           g_free (log_text);
653         }
654     }
655 }