1 /* vi:set et ai sw=2 sts=2 ts=2: */
3 * Copyright (c) 2012 GENIVI.
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/.
14 #include <glib-object.h>
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>
25 #include <boot-manager/job-manager.h>
26 #include <boot-manager/la-handler-service.h>
30 DLT_IMPORT_CONTEXT (la_handler_context);
34 /* property identifiers */
44 typedef struct _LAHandlerServiceData LAHandlerServiceData;
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,
54 static void la_handler_service_set_property (GObject *object,
58 static gboolean la_handler_service_handle_register (LAHandler *interface,
59 GDBusMethodInvocation *invocation,
63 LAHandlerService *service);
64 static void la_handler_service_handle_register_finish (GObject *object,
67 static gboolean la_handler_service_handle_consumer_lifecycle_request (ShutdownConsumer *consumer,
68 GDBusMethodInvocation *invocation,
71 ShutdownClient *client);
72 static void la_handler_service_handle_consumer_lifecycle_request_finish (JobManager *manager,
77 static LAHandlerServiceData *la_handler_service_data_new (LAHandlerService *service,
78 GDBusMethodInvocation *invocation,
80 static void la_handler_service_data_unref (LAHandlerServiceData *data);
84 struct _LAHandlerServiceClass
86 GObjectClass __parent__;
89 struct _LAHandlerService
93 GDBusConnection *connection;
95 JobManager *job_manager;
97 /* Associations of shutdown clients and their units */
98 GHashTable *units_to_clients;
99 GHashTable *clients_to_units;
105 /* connection to the NSM consumer interface */
106 NSMConsumer *nsm_consumer;
109 struct _LAHandlerServiceData
111 GDBusMethodInvocation *invocation;
112 LAHandlerService *service;
118 G_DEFINE_TYPE (LAHandlerService, la_handler_service, G_TYPE_OBJECT);
123 la_handler_service_class_init (LAHandlerServiceClass *klass)
125 GObjectClass *gobject_class;
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;
133 g_object_class_install_property (gobject_class,
135 g_param_spec_object ("connection",
138 G_TYPE_DBUS_CONNECTION,
140 G_PARAM_CONSTRUCT_ONLY |
141 G_PARAM_STATIC_STRINGS));
142 g_object_class_install_property (gobject_class,
144 g_param_spec_object ("job-manager",
146 "The internal handler of Start()"
150 G_PARAM_CONSTRUCT_ONLY |
151 G_PARAM_STATIC_STRINGS));
157 la_handler_service_constructed (GObject *object)
159 LAHandlerService *service = LA_HANDLER_SERVICE (object);
160 GError *error = NULL;
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",
171 log_text = g_strdup_printf ("Error occurred connecting to NSM Consumer: %s",
173 DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
175 g_error_free (error);
182 la_handler_service_init (LAHandlerService *service)
184 service->interface = la_handler_skeleton_new ();
186 /* the number that follows the prefix in the shutdown client's object path,
187 * making every shutdown client unique */
190 /* the string that precedes the index in the shutdown client's object path */
191 service->prefix = "/org/genivi/BootManager1/ShutdownConsumer";
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);
201 /* implement the Register() handler */
202 g_signal_connect (service->interface, "handle-register",
203 G_CALLBACK (la_handler_service_handle_register),
210 la_handler_service_finalize (GObject *object)
212 LAHandlerService *service = LA_HANDLER_SERVICE (object);
214 /* release the bus name */
215 g_bus_unown_name (service->bus_name_id);
217 /* release the D-Bus connection object */
218 if (service->connection != NULL)
219 g_object_unref (service->connection);
221 /* release the interface skeleton */
222 g_signal_handlers_disconnect_matched (service->interface,
224 0, 0, NULL, NULL, service);
225 g_object_unref (service->interface);
227 /* release the job manager skeleton */
228 g_object_unref (service->job_manager);
230 /* release the shutdown clients */
231 g_hash_table_unref (service->units_to_clients);
232 g_hash_table_unref (service->clients_to_units);
234 (*G_OBJECT_CLASS (la_handler_service_parent_class)->finalize) (object);
240 la_handler_service_get_property (GObject *object,
245 LAHandlerService *service = LA_HANDLER_SERVICE (object);
249 case PROP_CONNECTION:
250 g_value_set_object (value, service->connection);
252 case PROP_JOB_MANAGER:
253 g_value_set_object (value, service->job_manager);
256 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
264 la_handler_service_set_property (GObject *object,
269 LAHandlerService *service = LA_HANDLER_SERVICE (object);
273 case PROP_CONNECTION:
274 service->connection = g_value_dup_object (value);
276 case PROP_JOB_MANAGER:
277 service->job_manager = g_value_dup_object (value);
280 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
288 la_handler_service_handle_register (LAHandler *interface,
289 GDBusMethodInvocation *invocation,
291 NSMShutdownType shutdown_mode,
293 LAHandlerService *service)
295 ShutdownConsumer *consumer;
296 ShutdownClient *client;
297 GError *error = NULL;
298 const gchar *existing_bus_name;
299 const gchar *existing_object_path;
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);
309 /* find out if we have a shutdown client for this unit already */
310 client = g_hash_table_lookup (service->units_to_clients, unit);
313 /* there already is a shutdown client for the unit, so simply
314 * re-register its client with the new shutdown mode and timeout */
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);
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);
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,
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);
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);
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),
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),
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);
362 log_text = g_strdup_printf ("Failed to export shutdown consumer on the bus: %s",
364 DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
366 g_error_free (error);
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);
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,
381 /* free strings and release the shutdown consumer */
382 g_free (object_path);
383 g_object_unref (consumer);
385 /* increment the counter for our shutdown consumer object paths */
395 la_handler_service_handle_register_finish (GObject *object,
399 GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (user_data);
400 LAHandlerService *service;
401 NSMConsumer *nsm_consumer = NSM_CONSUMER (object);
402 GError *error = NULL;
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));
410 /* finish registering the shutdown client */
411 nsm_consumer_call_register_shutdown_client_finish (nsm_consumer, &error_code, res,
415 log_text = g_strdup_printf ("Failed to register a shutdown consumer: %s",
417 DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
419 g_error_free (error);
422 /* retrieve the LAHandlerService from the invocation object */
423 service = g_object_get_data (G_OBJECT (invocation), "la-handler-service");
425 /* notify the caller that we have handled the registration request */
426 la_handler_complete_register (service->interface, invocation);
432 la_handler_service_handle_consumer_lifecycle_request (ShutdownConsumer *consumer,
433 GDBusMethodInvocation *invocation,
436 ShutdownClient *client)
438 LAHandlerServiceData *data;
439 LAHandlerService *service;
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);
446 /* get the service from the shutdown client */
447 service = g_object_get_data (G_OBJECT (client), "la-handler-service");
449 /* look up the unit name associated with this shutdown client */
450 unit_name = g_hash_table_lookup (service->clients_to_units, client);
452 if (unit_name != NULL)
454 data = la_handler_service_data_new (service, NULL, request_id);
456 /* stop this unit now */
457 job_manager_stop (service->job_manager, unit_name, NULL,
458 la_handler_service_handle_consumer_lifecycle_request_finish,
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);
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);
479 la_handler_service_handle_consumer_lifecycle_request_finish (JobManager *manager,
485 LAHandlerServiceData *data = (LAHandlerServiceData *)user_data;
488 gint status = NSM_ERROR_STATUS_OK;
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);
495 /* log an error if shutting down the consumer has failed */
498 log_text = g_strdup_printf ("Failed to shutdown a shutdown consumer: %s",
500 DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
503 /* send an error back to the NSM */
504 status = NSM_ERROR_STATUS_ERROR;
507 /* log an error if systemd failed to stop the consumer */
508 if (g_strcmp0 (result, "failed") == 0)
510 DLT_LOG (la_handler_context, DLT_LOG_ERROR,
511 DLT_STRING ("Failed to shutdown a shutdown consumer"));
513 /* send an error back to the NSM */
514 status = NSM_ERROR_STATUS_ERROR;
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,
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));
529 la_handler_service_data_unref (data);
534 static LAHandlerServiceData *
535 la_handler_service_data_new (LAHandlerService *service,
536 GDBusMethodInvocation *invocation,
539 LAHandlerServiceData *data;
541 data = g_slice_new0 (LAHandlerServiceData);
543 data->service = g_object_ref (service);
544 if (invocation != NULL)
545 data->invocation = g_object_ref (invocation);
546 data->request_id = request_id;
554 la_handler_service_data_unref (LAHandlerServiceData *data)
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);
569 la_handler_service_new (GDBusConnection *connection,
570 JobManager *job_manager)
572 g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
573 g_return_val_if_fail (IS_JOB_MANAGER (job_manager), NULL);
575 return g_object_new (LA_HANDLER_TYPE_SERVICE,
576 "connection", connection,
577 "job-manager", job_manager,
583 la_handler_service_start (LAHandlerService *service,
586 g_return_val_if_fail (LA_HANDLER_IS_SERVICE (service), FALSE);
587 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
589 /* announce the org.genivi.BootManager1.LegacyAppHandler service on the bus */
590 return g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (service->interface),
592 "/org/genivi/BootManager1/LegacyAppHandler",
599 la_handler_service_get_nsm_consumer (LAHandlerService *service)
601 g_return_val_if_fail (LA_HANDLER_IS_SERVICE (service), NULL);
603 return service->nsm_consumer;
609 la_handler_service_deregister_consumers (LAHandlerService *service)
612 ShutdownClient *client;
613 const gchar *bus_name;
614 const gchar *object_path;
616 GError *error = NULL;
621 g_return_if_fail (LA_HANDLER_IS_SERVICE (service));
623 g_hash_table_iter_init (&iter, service->clients_to_units);
624 while (g_hash_table_iter_next (&iter, (gpointer *)&client, (gpointer *)&unit))
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);
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,
639 log_text = g_strdup_printf ("Failed to unregister shutdown client %s "
640 "for unit %s: %s", object_path, unit,
642 DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
644 g_error_free (error);
646 else if (error_code != NSM_ERROR_STATUS_OK)
648 log_text = g_strdup_printf ("Failed to unregister shutdown client %s "
649 "for unit %s: error code %d", object_path, unit,
651 DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));