Drop GApplication, fix D-Bus activation, further NSM integration work
[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/nsm-consumer-dbus.h>
20 #include <common/shutdown-consumer-dbus.h>
21
22 #include <boot-manager/job-manager.h>
23 #include <boot-manager/la-handler-dbus.h>
24 #include <boot-manager/la-handler-service.h>
25
26
27
28 DLT_IMPORT_CONTEXT (la_handler_context);
29
30
31
32 /* property identifiers */
33 enum
34 {
35   PROP_0,
36   PROP_CONNECTION,
37   PROP_JOB_MANAGER,
38 };
39
40
41
42 typedef struct _LAHandlerServiceData LAHandlerServiceData;
43
44
45
46 static void                  la_handler_service_constructed                              (GObject               *object);
47 static void                  la_handler_service_finalize                                 (GObject               *object);
48 static void                  la_handler_service_get_property                             (GObject               *object,
49                                                                                           guint                  prop_id,
50                                                                                           GValue                *value,
51                                                                                           GParamSpec            *pspec);
52 static void                  la_handler_service_set_property                             (GObject               *object,
53                                                                                           guint                  prop_id,
54                                                                                           const GValue          *value,
55                                                                                           GParamSpec            *pspec);
56 static gboolean              la_handler_service_handle_register                          (LAHandler             *interface,
57                                                                                           GDBusMethodInvocation *invocation,
58                                                                                           const gchar           *unit,
59                                                                                           const gchar           *mode,
60                                                                                           guint                  timeout,
61                                                                                           LAHandlerService      *service);
62 static void                  la_handler_service_handle_register_finish                   (GObject               *object,
63                                                                                           GAsyncResult          *res,
64                                                                                           gpointer               user_data);
65 static void                  la_handler_service_handle_consumer_lifecycle_request        (ShutdownConsumer      *interface,
66                                                                                           GDBusMethodInvocation *invocation,
67                                                                                           guint                  request,
68                                                                                           guint                  request_id,
69                                                                                           LAHandlerService      *service);
70 static void                  la_handler_service_handle_consumer_lifecycle_request_finish (JobManager            *manager,
71                                                                                           const gchar           *unit,
72                                                                                           const gchar           *result,
73                                                                                           GError                *error,
74                                                                                           gpointer               user_data);
75 static LAHandlerServiceData *la_handler_service_data_new                                 (LAHandlerService      *service,
76                                                                                           GDBusMethodInvocation *invocation,
77                                                                                           guint                  request_id);
78 static void                  la_handler_service_data_unref                               (LAHandlerServiceData  *data);
79
80
81
82 struct _LAHandlerServiceClass
83 {
84   GObjectClass __parent__;
85 };
86
87 struct _LAHandlerService
88 {
89   GObject          __parent__;
90
91   GDBusConnection *connection;
92   LAHandler       *interface;
93   JobManager      *job_manager;
94
95   /* Associations of shutdown consumers and their units */
96   GHashTable      *units_to_consumers;
97   GHashTable      *consumers_to_units;
98
99   const gchar     *prefix;
100   guint            index;
101   guint            bus_name_id;
102
103   /* connection to the NSM consumer interface */
104   NSMConsumer     *nsm_consumer;
105 };
106
107 struct _LAHandlerServiceData
108 {
109   GDBusMethodInvocation *invocation;
110   LAHandlerService      *service;
111   guint                  request_id;
112 };
113
114
115
116 G_DEFINE_TYPE (LAHandlerService, la_handler_service, G_TYPE_OBJECT);
117
118
119
120 static void
121 la_handler_service_class_init (LAHandlerServiceClass *klass)
122 {
123   GObjectClass *gobject_class;
124
125   gobject_class = G_OBJECT_CLASS (klass);
126   gobject_class->constructed = la_handler_service_constructed;
127   gobject_class->finalize = la_handler_service_finalize;
128   gobject_class->get_property = la_handler_service_get_property;
129   gobject_class->set_property = la_handler_service_set_property;
130
131   g_object_class_install_property (gobject_class,
132                                    PROP_CONNECTION,
133                                    g_param_spec_object ("connection",
134                                                         "connection",
135                                                         "connection",
136                                                         G_TYPE_DBUS_CONNECTION,
137                                                         G_PARAM_READWRITE |
138                                                         G_PARAM_CONSTRUCT_ONLY |
139                                                         G_PARAM_STATIC_STRINGS));
140   g_object_class_install_property (gobject_class,
141                                    PROP_JOB_MANAGER,
142                                    g_param_spec_object ("job-manager",
143                                                         "Job Manager",
144                                                         "The internal handler of Start()"
145                                                         " and Stop() jobs",
146                                                         TYPE_JOB_MANAGER,
147                                                         G_PARAM_READWRITE |
148                                                         G_PARAM_CONSTRUCT_ONLY |
149                                                         G_PARAM_STATIC_STRINGS));
150 }
151
152
153
154 static void
155 la_handler_service_constructed (GObject *object)
156 {
157   LAHandlerService *service = LA_HANDLER_SERVICE (object);
158   GError           *error = NULL;
159   gchar            *log_text;
160
161   /* connect to the node state manager */
162   service->nsm_consumer =
163     nsm_consumer_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE,
164                                          "com.contiautomotive.NodeStateManager",
165                                          "/com/contiautomotive/NodeStateManager/Consumer",
166                                           NULL, &error);
167   if (error != NULL)
168     {
169       log_text = g_strdup_printf ("Error occurred connecting to NSM Consumer: %s",
170                                   error->message);
171       DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
172       g_free (log_text);
173       g_error_free (error);
174     }
175 }
176
177
178
179 static void
180 la_handler_service_init (LAHandlerService *service)
181 {
182   service->interface = la_handler_skeleton_new ();
183
184   /* the number that follows the prefix in the shutdown consumer's object path,
185    * making every shutdown consumer unique */
186   service->index = 1;
187
188   /* the string that precedes the index in the shutdown consumer's object path */
189   service->prefix = "/org/genivi/BootManager1/ShutdownConsumer";
190
191   /* initialize the association of shutdown consumers to units */
192   service->units_to_consumers = g_hash_table_new_full (g_str_hash, g_str_equal,
193                                                        (GDestroyNotify) g_free,
194                                                        (GDestroyNotify) g_object_unref);
195   service->consumers_to_units = g_hash_table_new_full (g_direct_hash, g_direct_equal,
196                                                        (GDestroyNotify) g_object_unref,
197                                                        (GDestroyNotify) g_free);
198
199   /* implement the Register() handler */
200   g_signal_connect (service->interface, "handle-register",
201                     G_CALLBACK (la_handler_service_handle_register),
202                     service);
203 }
204
205
206
207 static void
208 la_handler_service_finalize (GObject *object)
209 {
210   LAHandlerService *service = LA_HANDLER_SERVICE (object);
211
212   /* release the bus name */
213   g_bus_unown_name (service->bus_name_id);
214
215   /* release the D-Bus connection object */
216   if (service->connection != NULL)
217     g_object_unref (service->connection);
218
219   /* release the interface skeleton */
220   g_signal_handlers_disconnect_matched (service->interface,
221                                         G_SIGNAL_MATCH_DATA,
222                                         0, 0, NULL, NULL, service);
223   g_object_unref (service->interface);
224
225   /* release the job manager skeleton */
226   g_object_unref (service->job_manager);
227
228   /* release the shutdown consumers */
229   g_hash_table_unref (service->units_to_consumers);
230   g_hash_table_unref (service->consumers_to_units);
231
232   (*G_OBJECT_CLASS (la_handler_service_parent_class)->finalize) (object);
233 }
234
235
236
237 static void
238 la_handler_service_get_property (GObject    *object,
239                                  guint       prop_id,
240                                  GValue     *value,
241                                  GParamSpec *pspec)
242 {
243   LAHandlerService *service = LA_HANDLER_SERVICE (object);
244
245   switch (prop_id)
246     {
247     case PROP_CONNECTION:
248       g_value_set_object (value, service->connection);
249       break;
250     case PROP_JOB_MANAGER:
251       g_value_set_object (value, service->job_manager);
252       break;
253     default:
254       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
255       break;
256     }
257 }
258
259
260
261 static void
262 la_handler_service_set_property (GObject      *object,
263                                  guint         prop_id,
264                                  const GValue *value,
265                                  GParamSpec   *pspec)
266 {
267   LAHandlerService *service = LA_HANDLER_SERVICE (object);
268
269   switch (prop_id)
270     {
271     case PROP_CONNECTION:
272       service->connection = g_value_dup_object (value);
273       break;
274     case PROP_JOB_MANAGER:
275       service->job_manager = g_value_dup_object (value);
276       break;
277     default:
278       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
279       break;
280     }
281 }
282
283
284
285 static gboolean
286 la_handler_service_handle_register (LAHandler             *interface,
287                                     GDBusMethodInvocation *invocation,
288                                     const gchar           *unit,
289                                     const gchar           *mode,
290                                     guint                  timeout,
291                                     LAHandlerService      *service)
292 {
293   ShutdownConsumer *consumer;
294   GError           *error = NULL;
295   gchar            *log_text;
296   gchar            *object_path;
297
298   g_return_val_if_fail (IS_LA_HANDLER (interface), FALSE);
299   g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);
300   g_return_val_if_fail (unit != NULL && *unit != '\0', FALSE);
301   g_return_val_if_fail (mode != NULL && *mode != '\0', FALSE);
302   g_return_val_if_fail (LA_HANDLER_IS_SERVICE (service), FALSE);
303
304   /* find out if this unit is already registered with a shutdown consumer */
305   if (g_hash_table_lookup (service->units_to_consumers, unit))
306    {
307       /* there already is a shutdown consumer for the unit, so ignore this request */
308       la_handler_complete_register (interface, invocation);
309       return TRUE;
310    }
311
312   /* create a new ShutdownConsumer and implement its LifecycleRequest method */
313   consumer = shutdown_consumer_skeleton_new ();
314   g_signal_connect (consumer, "handle-lifecycle-request",
315                     G_CALLBACK (la_handler_service_handle_consumer_lifecycle_request),
316                     service);
317
318   /* associate the shutdown consumer with the unit name */
319   g_hash_table_insert (service->units_to_consumers, g_strdup (unit),
320                        g_object_ref (consumer));
321   g_hash_table_insert (service->consumers_to_units, g_object_ref (consumer),
322                        g_strdup (unit));
323
324   /* export the shutdown consumer on the bus */
325   object_path = g_strdup_printf ("%s/%u", service->prefix, service->index);
326   g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (consumer),
327                                     service->connection, object_path, &error);
328   if (error != NULL)
329     {
330       log_text =
331         g_strdup_printf ("Failed to export shutdown consumer on the bus: %s",
332                          error->message);
333       DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
334       g_free (log_text);
335       g_error_free (error);
336     }
337
338   /* temporarily store a reference to the LAHandlerService in the invocation object */
339   g_object_set_data_full (G_OBJECT (invocation), "la-handler-service",
340                           g_object_ref (service), (GDestroyNotify) g_object_unref);
341
342   /* register the shutdown consumer with the NSM Consumer */
343   nsm_consumer_call_register_shutdown_client (service->nsm_consumer,
344                                               "org.genivi.BootManager1", object_path, 0,
345                                               timeout, NULL,
346                                               la_handler_service_handle_register_finish,
347                                               invocation);
348
349   g_free (object_path);
350
351   /* increment the counter for our shutdown consumer object paths */
352   service->index++;
353
354   return TRUE;
355 }
356
357
358
359 static void
360 la_handler_service_handle_register_finish (GObject      *object,
361                                            GAsyncResult *res,
362                                            gpointer      user_data)
363 {
364   GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (user_data);
365   LAHandlerService      *service;
366   NSMConsumer           *nsm_consumer = NSM_CONSUMER (object);
367   GError                *error = NULL;
368   gchar                 *log_text;
369   gint                   error_code;
370
371   g_return_if_fail (IS_NSM_CONSUMER (nsm_consumer));
372   g_return_if_fail (G_IS_ASYNC_RESULT (res));
373   g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation));
374
375   /* finish registering the shutdown client */
376   nsm_consumer_call_register_shutdown_client_finish (nsm_consumer, &error_code, res,
377                                                      &error);
378   if (error != NULL)
379     {
380       log_text = g_strdup_printf ("Failed to register a shutdown consumer: %s",
381                                   error->message);
382       DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
383       g_free (log_text);
384       g_error_free (error);
385     }
386
387   /* retrieve the LAHandlerService from the invocation object */
388   service = g_object_get_data (G_OBJECT (invocation), "la-handler-service");
389
390   /* notify the caller that we have handled the registration request */
391   la_handler_complete_register (service->interface, invocation);
392 }
393
394
395
396 static void
397 la_handler_service_handle_consumer_lifecycle_request (ShutdownConsumer      *consumer,
398                                                       GDBusMethodInvocation *invocation,
399                                                       guint                  request,
400                                                       guint                  request_id,
401                                                       LAHandlerService      *service)
402 {
403   LAHandlerServiceData *data;
404   gchar                *unit_name;
405
406   g_return_if_fail (IS_SHUTDOWN_CONSUMER (consumer));
407   g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation));
408   g_return_if_fail (LA_HANDLER_IS_SERVICE (service));
409
410   /* look up the unit name associated with this shutdown consumer */
411   unit_name = g_hash_table_lookup (service->consumers_to_units, consumer);
412
413   if (unit_name != NULL)
414     {
415       data = la_handler_service_data_new (service, NULL, request_id);
416
417       /* stop this unit now */
418       job_manager_stop (service->job_manager, unit_name, NULL,
419                         la_handler_service_handle_consumer_lifecycle_request_finish,
420                         data);
421
422       /* let the NSM know that we are working on this request */
423       shutdown_consumer_complete_lifecycle_request (consumer, invocation, 7);
424     }
425   else
426     {
427       /* NSM asked us to shutdown a shutdown consumer we did not register;
428        * make it aware by returning an error */
429       shutdown_consumer_complete_lifecycle_request (consumer, invocation, 2);
430     }
431 }
432
433
434
435 static void
436 la_handler_service_handle_consumer_lifecycle_request_finish (JobManager  *manager,
437                                                              const gchar *unit,
438                                                              const gchar *result,
439                                                              GError      *error,
440                                                              gpointer     user_data)
441 {
442   LAHandlerServiceData *data = (LAHandlerServiceData *)user_data;
443   GError               *err = NULL;
444   gchar                *log_text;
445   gint                  status = 1;
446
447   g_return_if_fail (IS_JOB_MANAGER (manager));
448   g_return_if_fail (unit != NULL && *unit != '\0');
449   g_return_if_fail (result != NULL && *result != '\0');
450   g_return_if_fail (data != NULL);
451
452   /* log an error if shutting down the consumer has failed */
453   if (error != NULL)
454     {
455       log_text = g_strdup_printf ("Failed to shutdown a shutdown consumer: %s",
456                                   error->message);
457       DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
458       g_free (log_text);
459
460       /* send an error back to the NSM */
461       status = 2;
462     }
463
464   /* log an error if systemd failed to stop the consumer */
465   if (g_strcmp0 (result, "failed") == 0)
466     {
467       DLT_LOG (la_handler_context, DLT_LOG_ERROR,
468                DLT_STRING ("Failed to shutdown a shutdown consumer"));
469
470       /* send an error back to the NSM */
471       status = 2;
472     }
473
474   /* let the NSM know that we have handled the lifecycle request */
475   if (!nsm_consumer_call_lifecycle_request_complete_sync (data->service->nsm_consumer,
476                                                           data->request_id, status,
477                                                           NULL, NULL, &err))
478     {
479       log_text = g_strdup_printf ("Failed to notify Node State Manager about completed "
480                                   "lifecycle request: %s", err->message);
481       DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
482       g_free (log_text);
483       g_error_free (err);
484     }
485
486   la_handler_service_data_unref (data);
487 }
488
489
490
491 static LAHandlerServiceData *
492 la_handler_service_data_new (LAHandlerService      *service,
493                              GDBusMethodInvocation *invocation,
494                              guint                  request_id)
495 {
496   LAHandlerServiceData *data;
497
498   data = g_slice_new0 (LAHandlerServiceData);
499   if (service != NULL)
500     data->service = g_object_ref (service);
501   if (invocation != NULL)
502     data->invocation = g_object_ref (invocation);
503   data->request_id = request_id;
504
505   return data;
506 }
507
508
509
510 static void
511 la_handler_service_data_unref (LAHandlerServiceData *data)
512 {
513   if (data == NULL)
514     return;
515
516   if (data->invocation != NULL)
517     g_object_unref (data->invocation);
518   if (data->service != NULL)
519     g_object_unref (data->service);
520   g_slice_free (LAHandlerServiceData, data);
521 }
522
523
524
525 LAHandlerService *
526 la_handler_service_new (GDBusConnection *connection,
527                         JobManager      *job_manager)
528 {
529   g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
530   g_return_val_if_fail (IS_JOB_MANAGER (job_manager), NULL);
531
532   return g_object_new (LA_HANDLER_TYPE_SERVICE,
533                        "connection", connection,
534                        "job-manager", job_manager,
535                        NULL);
536 }
537
538
539 gboolean
540 la_handler_service_start (LAHandlerService *service,
541                            GError           **error)
542 {
543   g_return_val_if_fail (LA_HANDLER_IS_SERVICE (service), FALSE);
544   g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
545
546   /* announce the org.genivi.BootManager1.LegacyAppHandler service on the bus */
547   return g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (service->interface),
548                                            service->connection,
549                                            "/org/genivi/BootManager1/LegacyAppHandler",
550                                            error);
551 }