summaryrefslogtreecommitdiff
path: root/node-startup-controller
diff options
context:
space:
mode:
authorJannis Pohlmann <jannis.pohlmann@codethink.co.uk>2012-07-31 17:45:10 +0100
committerJannis Pohlmann <jannis.pohlmann@codethink.co.uk>2012-07-31 17:45:10 +0100
commit44591b3d0baa3003bca0380fe3664fb4c210b79f (patch)
tree82feb7384ce96bb9133b7954ce68dad1f4d81362 /node-startup-controller
parent9d71065bfb59eb10fbd8ece605485d3864d08c08 (diff)
downloadnode-startup-controller-44591b3d0baa3003bca0380fe3664fb4c210b79f.tar.gz
First step of renaming the Boot Manager to Node Startup Controller
This includes mostly files, directories and the package/tarball name. The classes and D-Bus interfaces will be renamed in the next step.
Diffstat (limited to 'node-startup-controller')
-rw-r--r--node-startup-controller/Makefile.am133
-rw-r--r--node-startup-controller/boot-manager-application.c577
-rw-r--r--node-startup-controller/boot-manager-application.h42
-rw-r--r--node-startup-controller/boot-manager-dbus.xml61
-rw-r--r--node-startup-controller/boot-manager-service.c545
-rw-r--r--node-startup-controller/boot-manager-service.h45
-rw-r--r--node-startup-controller/boot-manager.service.in5
-rw-r--r--node-startup-controller/busconf/Makefile.am9
-rw-r--r--node-startup-controller/busconf/org.genivi.BootManager1.conf9
-rw-r--r--node-startup-controller/glib-extensions.c100
-rw-r--r--node-startup-controller/glib-extensions.h27
-rw-r--r--node-startup-controller/job-manager.c471
-rw-r--r--node-startup-controller/job-manager.h49
-rw-r--r--node-startup-controller/la-handler-service.c683
-rw-r--r--node-startup-controller/la-handler-service.h49
-rw-r--r--node-startup-controller/luc-starter.c663
-rw-r--r--node-startup-controller/luc-starter.h38
-rw-r--r--node-startup-controller/main.c195
-rw-r--r--node-startup-controller/org.genivi.BootManager1.service.in4
-rw-r--r--node-startup-controller/systemd-manager-dbus.xml36
-rw-r--r--node-startup-controller/systemd-unit-dbus.xml7
-rw-r--r--node-startup-controller/target-startup-monitor.c457
-rw-r--r--node-startup-controller/target-startup-monitor.h32
23 files changed, 4237 insertions, 0 deletions
diff --git a/node-startup-controller/Makefile.am b/node-startup-controller/Makefile.am
new file mode 100644
index 0000000..9d05213
--- /dev/null
+++ b/node-startup-controller/Makefile.am
@@ -0,0 +1,133 @@
+# vi:set ts=8 sw=8 noet ai nocindent:
+
+SUBDIRS = \
+ busconf
+
+boot_managerdir = \
+ $(libdir)/boot-manager-$(BOOT_MANAGER_VERSION_API)
+
+boot_manager_PROGRAMS = \
+ boot-manager
+
+boot_manager_built_sources = \
+ boot-manager-dbus.h \
+ boot-manager-dbus.c
+
+systemd_manager_built_sources = \
+ systemd-manager-dbus.h \
+ systemd-manager-dbus.c
+
+systemd_unit_built_sources = \
+ systemd-unit-dbus.h \
+ systemd-unit-dbus.c
+
+boot_manager_SOURCES = \
+ boot-manager-application.c \
+ boot-manager-application.h \
+ boot-manager-service.c \
+ boot-manager-service.h \
+ glib-extensions.c \
+ glib-extensions.h \
+ job-manager.c \
+ job-manager.h \
+ la-handler-service.c \
+ la-handler-service.h \
+ luc-starter.c \
+ luc-starter.h \
+ target-startup-monitor.c \
+ target-startup-monitor.h \
+ main.c \
+ $(boot_manager_built_sources) \
+ $(systemd_manager_built_sources) \
+ $(systemd_unit_built_sources)
+
+boot_manager_CFLAGS = \
+ -DLUC_PATH=\"$(sysconfdir)/boot-manager/last-user-context\" \
+ -DG_LOG_DOMAIN=\"boot-manager\" \
+ -I$(top_srcdir) \
+ $(DLT_CFLAGS) \
+ $(GIO_CFLAGS) \
+ $(GIO_UNIX_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ $(PLATFORM_CFLAGS) \
+ $(PLATFORM_CPPFLAGS) \
+ $(SYSTEMD_DAEMON_CFLAGS)
+
+boot_manager_LDFLAGS = \
+ -no-undefined \
+ $(PLATFORM_LDFLAGS)
+
+boot_manager_DEPENDENCIES = \
+ $(top_builddir)/common/libcommon.la
+
+boot_manager_LDADD = \
+ $(DLT_LIBS) \
+ $(GIO_LIBS) \
+ $(GIO_UNIX_LIBS) \
+ $(GLIB_LIBS) \
+ $(SYSTEMD_DAEMON_LIBS) \
+ $(top_builddir)/common/libcommon.la
+
+%.service: %.service.in
+ sed -e "s,\@libdir\@,$(libdir),g" \
+ -e "s,\@BOOT_MANAGER_VERSION_API\@,$(BOOT_MANAGER_VERSION_API),g" < $< > $@
+
+dbus_servicedir = $(datadir)/dbus-1/system-services
+
+dbus_service_in_files = \
+ org.genivi.BootManager1.service.in
+
+dbus_service_DATA = $(dbus_service_in_files:.service.in=.service)
+
+systemd_servicedir = $(libdir)/systemd/system
+
+systemd_service_in_files = \
+ boot-manager.service.in
+
+systemd_service_DATA = $(systemd_service_in_files:.service.in=.service)
+
+CLEANFILES = \
+ $(dbus_service_DATA) \
+ $(systemd_service_DATA) \
+ boot-manager-dbus-doc-*.xml
+
+EXTRA_DIST = \
+ boot-manager-dbus.xml \
+ systemd-manager-dbus.xml \
+ $(dbus_service_in_files) \
+ $(systemd_service_in_files)
+
+DISTCLEANFILES = \
+ $(boot_manager_built_sources) \
+ $(systemd_manager_built_sources) \
+ $(systemd_unit_built_sources)
+
+BUILT_SOURCES = \
+ $(boot_manager_built_sources) \
+ $(systemd_manager_built_sources) \
+ $(systemd_unit_built_sources)
+
+$(boot_manager_built_sources): boot-manager-dbus.xml Makefile
+ $(AM_V_GEN) $(GDBUS_CODEGEN) \
+ --interface-prefix org.genivi \
+ --c-namespace "" \
+ --generate-c-code boot-manager-dbus \
+ --generate-docbook boot-manager-dbus-doc \
+ --annotate org.genivi.BootManager1.BootManager org.gtk.GDBus.C.Name \
+ Boot_Manager $<
+
+$(systemd_manager_built_sources): systemd-manager-dbus.xml Makefile
+ $(AM_V_GEN) $(GDBUS_CODEGEN) \
+ --interface-prefix org.freedesktop.systemd1 \
+ --c-namespace "" \
+ --generate-c-code systemd-manager-dbus \
+ --annotate org.freedesktop.systemd1.Manager org.gtk.GDBus.C.Name \
+ SystemdManager $<
+
+$(systemd_unit_built_sources): systemd-unit-dbus.xml Makefile
+ $(AM_V_GEN) $(GDBUS_CODEGEN) \
+ --interface-prefix org.freedesktop.systemd1 \
+ --c-namespace "" \
+ --generate-c-code systemd-unit-dbus \
+ --annotate org.freedesktop.systemd1.Unit org.gtk.GDBus.C.Name \
+ SystemdUnit $<
diff --git a/node-startup-controller/boot-manager-application.c b/node-startup-controller/boot-manager-application.c
new file mode 100644
index 0000000..5fb7436
--- /dev/null
+++ b/node-startup-controller/boot-manager-application.c
@@ -0,0 +1,577 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/* -
+ * Copyright (c) 2012 GENIVI.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib-object.h>
+#include <gio/gio.h>
+#include <glib-unix.h>
+
+#include <systemd/sd-daemon.h>
+
+#include <dlt/dlt.h>
+
+#include <common/nsm-consumer-dbus.h>
+#include <common/nsm-enum-types.h>
+#include <common/shutdown-client.h>
+#include <common/shutdown-consumer-dbus.h>
+#include <common/watchdog-client.h>
+
+#include <node-startup-controller/boot-manager-application.h>
+#include <node-startup-controller/boot-manager-service.h>
+#include <node-startup-controller/job-manager.h>
+#include <node-startup-controller/la-handler-service.h>
+#include <node-startup-controller/luc-starter.h>
+
+
+
+
+DLT_IMPORT_CONTEXT (boot_manager_context);
+
+
+
+/* property identifiers */
+enum
+{
+ PROP_0,
+ PROP_CONNECTION,
+ PROP_JOB_MANAGER,
+ PROP_BOOT_MANAGER_SERVICE,
+ PROP_LUC_STARTER,
+ PROP_LA_HANDLER,
+ PROP_MAIN_LOOP,
+};
+
+
+
+static void boot_manager_application_finalize (GObject *object);
+static void boot_manager_application_constructed (GObject *object);
+static void boot_manager_application_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static gboolean boot_manager_application_handle_lifecycle_request (ShutdownConsumer *interface,
+ GDBusMethodInvocation *invocation,
+ NSMShutdownType request,
+ guint request_id,
+ BootManagerApplication *application);
+static void boot_manager_application_handle_register_finish (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data);
+static void boot_manager_application_handle_unregister_finish (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data);
+static void boot_manager_application_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void boot_manager_application_luc_groups_started (LUCStarter *starter,
+ BootManagerApplication *application);
+
+
+
+struct _BootManagerApplicationClass
+{
+ GObjectClass __parent__;
+};
+
+struct _BootManagerApplication
+{
+ GObject __parent__;
+
+ /* the connection to D-Bus */
+ GDBusConnection *connection;
+
+ /* systemd watchdog client that repeatedly asks systemd to update
+ * the watchdog timestamp */
+ WatchdogClient *watchdog_client;
+
+ /* manager of unit start and stop operations */
+ JobManager *job_manager;
+
+ /* boot manager service */
+ BootManagerService *boot_manager_service;
+
+ /* LUC starter to restore the LUC */
+ LUCStarter *luc_starter;
+
+ /* Legacy App Handler to register apps with the Node State Manager */
+ LAHandlerService *la_handler;
+
+ /* the application's main loop */
+ GMainLoop *main_loop;
+
+ /* identifier for the registered bus name */
+ guint bus_name_id;
+
+ /* shutdown client for the boot manager itself */
+ ShutdownClient *client;
+};
+
+
+
+G_DEFINE_TYPE (BootManagerApplication, boot_manager_application, G_TYPE_OBJECT);
+
+
+
+static void
+boot_manager_application_class_init (BootManagerApplicationClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = boot_manager_application_finalize;
+ gobject_class->constructed = boot_manager_application_constructed;
+ gobject_class->get_property = boot_manager_application_get_property;
+ gobject_class->set_property = boot_manager_application_set_property;
+
+ g_object_class_install_property (gobject_class,
+ PROP_CONNECTION,
+ g_param_spec_object ("connection",
+ "D-Bus Connection",
+ "The connection to D-Bus",
+ G_TYPE_DBUS_CONNECTION,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class,
+ PROP_JOB_MANAGER,
+ g_param_spec_object ("job-manager",
+ "Job Manager",
+ "The internal handler of Start()"
+ " and Stop() jobs",
+ TYPE_JOB_MANAGER,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class,
+ PROP_LA_HANDLER,
+ g_param_spec_object ("la-handler",
+ "LA Handler",
+ "Legacy Application Handler",
+ LA_HANDLER_TYPE_SERVICE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class,
+ PROP_BOOT_MANAGER_SERVICE,
+ g_param_spec_object ("boot-manager-service",
+ "boot-manager-service",
+ "boot-manager-service",
+ BOOT_MANAGER_TYPE_SERVICE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class,
+ PROP_LUC_STARTER,
+ g_param_spec_object ("luc-starter",
+ "luc-starter",
+ "luc-starter",
+ TYPE_LUC_STARTER,
+ G_PARAM_READABLE |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class,
+ PROP_MAIN_LOOP,
+ g_param_spec_boxed ("main-loop",
+ "main-loop",
+ "main-loop",
+ G_TYPE_MAIN_LOOP,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
+
+
+
+static void
+boot_manager_application_init (BootManagerApplication *application)
+{
+ const gchar *watchdog_str;
+ guint64 watchdog_usec = 0;
+ guint watchdog_sec = 0;
+ gchar *message;
+
+ /* read the WATCHDOG_USEC environment variable and parse it
+ * into an unsigned integer */
+ watchdog_str = g_getenv ("WATCHDOG_USEC");
+ if (watchdog_str != NULL)
+ watchdog_usec = g_ascii_strtoull (watchdog_str, NULL, 10);
+
+ /* only create the watchdog client if a timeout was specified */
+ if (watchdog_usec > 0)
+ {
+ /* halve the watchdog timeout because we need to notify systemd
+ * twice in every interval; also, convert it to seconds */
+ watchdog_sec = (guint) ((watchdog_usec / 2) / 1000000);
+
+ /* update systemd's watchdog timestamp in regular intervals */
+ application->watchdog_client = watchdog_client_new (watchdog_sec);
+
+ /* log information about the watchdog timeout using DLT */
+ message = g_strdup_printf ("Updating systemd's watchdog timestamp every %d seconds",
+ watchdog_sec);
+ DLT_LOG (boot_manager_context, DLT_LOG_INFO, DLT_STRING (message));
+ g_free (message);
+ }
+}
+
+
+
+static void
+boot_manager_application_finalize (GObject *object)
+{
+ BootManagerApplication *application = BOOT_MANAGER_APPLICATION (object);
+ ShutdownConsumer *consumer;
+
+ /* disconnect from the shutdown consumer */
+ consumer = shutdown_client_get_consumer (application->client);
+ g_signal_handlers_disconnect_matched (consumer, G_SIGNAL_MATCH_DATA, 0, 0, NULL, NULL,
+ application);
+
+ /* release the shutdown client */
+ g_object_unref (application->client);
+
+ /* release the bus name */
+ g_bus_unown_name (application->bus_name_id);
+
+ /* release the D-Bus connection object */
+ if (application->connection != NULL)
+ g_object_unref (application->connection);
+
+ /* release the watchdog client */
+ if (application->watchdog_client != NULL)
+ g_object_unref (application->watchdog_client);
+
+ /* release the boot manager */
+ g_object_unref (application->boot_manager_service);
+
+ /* release the LUC starter */
+ g_object_unref (application->luc_starter);
+
+ /* release the legacy app handler */
+ g_object_unref (application->la_handler);
+
+ /* release the job manager */
+ g_object_unref (application->job_manager);
+
+ /* release the main loop */
+ g_main_loop_unref (application->main_loop);
+
+ (*G_OBJECT_CLASS (boot_manager_application_parent_class)->finalize) (object);
+}
+
+
+
+static void
+boot_manager_application_constructed (GObject *object)
+{
+ BootManagerApplication *application = BOOT_MANAGER_APPLICATION (object);
+ ShutdownConsumer *consumer;
+ NSMShutdownType shutdown_mode;
+ NSMConsumer *nsm_consumer;
+ GError *error = NULL;
+ gchar *bus_name = "org.genivi.BootManager1";
+ gchar *log_text;
+ gchar *object_path;
+ gint timeout;
+
+ /* instantiate the LUC starter */
+ application->luc_starter = luc_starter_new (application->job_manager,
+ application->boot_manager_service);
+
+ /* be notified when LUC groups have started so that we can hand
+ * control over to systemd again */
+ g_signal_connect (application->luc_starter, "luc-groups-started",
+ G_CALLBACK (boot_manager_application_luc_groups_started),
+ application);
+
+ /* restore the LUC if desired */
+ luc_starter_start_groups (application->luc_starter);
+
+ /* get a bus name on the given connection */
+ application->bus_name_id =
+ g_bus_own_name_on_connection (application->connection, "org.genivi.BootManager1",
+ G_BUS_NAME_OWNER_FLAGS_NONE, NULL, NULL, NULL, NULL);
+
+ /* create a shutdown client for the boot manager itself */
+ object_path = "/org/genivi/BootManager1/ShutdownConsumer/0";
+ shutdown_mode = NSM_SHUTDOWN_TYPE_NORMAL;
+ timeout = 1000;
+ application->client = shutdown_client_new (bus_name, object_path, shutdown_mode,
+ timeout);
+
+ /* create a shutdown consumer and implement its LifecycleRequest method */
+ consumer = shutdown_consumer_skeleton_new ();
+ shutdown_client_set_consumer (application->client, consumer);
+ g_signal_connect (consumer, "handle-lifecycle-request",
+ G_CALLBACK (boot_manager_application_handle_lifecycle_request),
+ application);
+
+ /* export the shutdown consumer on the bus */
+ if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (consumer),
+ application->connection, object_path, &error))
+ {
+ log_text = g_strdup_printf ("Failed to export shutdown consumer on the bus: %s",
+ error->message);
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ g_clear_error (&error);
+ }
+
+ /* register boot manager as a shutdown consumer */
+ nsm_consumer = la_handler_service_get_nsm_consumer (application->la_handler);
+ nsm_consumer_call_register_shutdown_client (nsm_consumer, bus_name, object_path,
+ shutdown_mode, timeout, NULL,
+ boot_manager_application_handle_register_finish,
+ NULL);
+
+ /* release the shutdown consumer */
+ g_object_unref (consumer);
+}
+
+
+
+static void
+boot_manager_application_handle_register_finish (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ NSMConsumer *nsm_consumer = NSM_CONSUMER (object);
+ GError *error = NULL;
+ gchar *log_text;
+ gint error_code = NSM_ERROR_STATUS_OK;
+
+ g_return_if_fail (IS_NSM_CONSUMER (nsm_consumer));
+ g_return_if_fail (G_IS_ASYNC_RESULT (res));
+
+ /* finish registering boot manager as a shutdown client */
+ if (!nsm_consumer_call_register_shutdown_client_finish (nsm_consumer, &error_code, res,
+ &error))
+ {
+ log_text = g_strdup_printf ("Failed to register the boot manager as a shutdown "
+ "consumer: %s", error->message);
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ g_error_free (error);
+ }
+ else if (error_code == NSM_ERROR_STATUS_OK)
+ {
+ log_text = g_strdup_printf ("The boot manager has registered as a shutdown "
+ "consumer");
+ DLT_LOG (boot_manager_context, DLT_LOG_INFO, DLT_STRING (log_text));
+ g_free (log_text);
+ }
+ else
+ {
+ log_text = g_strdup_printf ("Failed to register the boot manager as a shutdown "
+ "consumer: error status %d", error_code);
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ }
+}
+
+
+
+static void
+boot_manager_application_handle_unregister_finish (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ BootManagerApplication *application = BOOT_MANAGER_APPLICATION (user_data);
+ NSMConsumer *nsm_consumer = NSM_CONSUMER (object);
+ GError *error = NULL;
+ gchar *log_text;
+ gint error_code = NSM_ERROR_STATUS_OK;
+
+ g_return_if_fail (IS_NSM_CONSUMER (nsm_consumer));
+ g_return_if_fail (BOOT_MANAGER_IS_APPLICATION (application));
+ g_return_if_fail (G_IS_ASYNC_RESULT (res));
+
+ /* finish unregistering boot manager as a shutdown client */
+ if (!nsm_consumer_call_un_register_shutdown_client_finish (nsm_consumer, &error_code,
+ res, &error))
+ {
+ log_text = g_strdup_printf ("Failed to unregister the boot manager as a shutdown "
+ "consumer: %s", error->message);
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ g_error_free (error);
+ }
+ else if (error_code == NSM_ERROR_STATUS_OK)
+ {
+ log_text = g_strdup_printf ("The boot manager has unregistered as a shutdown "
+ "consumer");
+ DLT_LOG (boot_manager_context, DLT_LOG_INFO, DLT_STRING (log_text));
+ g_free (log_text);
+ }
+ else
+ {
+ log_text = g_strdup_printf ("Failed to unregister the boot manager as a shutdown "
+ "consumer: error status %d", error_code);
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ }
+
+ /* quit the application */
+ g_main_loop_quit (application->main_loop);
+}
+
+
+
+static gboolean
+boot_manager_application_handle_lifecycle_request (ShutdownConsumer *consumer,
+ GDBusMethodInvocation *invocation,
+ NSMShutdownType request,
+ guint request_id,
+ BootManagerApplication *application)
+{
+ NSMConsumer *nsm_consumer;
+ const gchar *bus_name;
+ const gchar *object_path;
+ gint shutdown_mode;
+
+ g_return_val_if_fail (IS_SHUTDOWN_CONSUMER (consumer), FALSE);
+ g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);
+ g_return_val_if_fail (BOOT_MANAGER_IS_APPLICATION (application), FALSE);
+
+ /* cancel the LUC startup */
+ luc_starter_cancel (application->luc_starter);
+
+ /* deregister the shutdown consumers */
+ la_handler_service_deregister_consumers (application->la_handler);
+
+ /* let the NSM know that we have handled the lifecycle request */
+ shutdown_consumer_complete_lifecycle_request (consumer, invocation,
+ NSM_ERROR_STATUS_OK);
+
+ /* deregister the boot manager as a shutdown client itself */
+ nsm_consumer = la_handler_service_get_nsm_consumer (application->la_handler);
+ bus_name = shutdown_client_get_bus_name (application->client);
+ object_path = shutdown_client_get_object_path (application->client);
+ shutdown_mode = shutdown_client_get_shutdown_mode (application->client);
+ nsm_consumer_call_un_register_shutdown_client (nsm_consumer, bus_name,
+ object_path, shutdown_mode, NULL,
+ boot_manager_application_handle_unregister_finish,
+ application);
+ return TRUE;
+}
+
+
+
+static void
+boot_manager_application_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ BootManagerApplication *application = BOOT_MANAGER_APPLICATION (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONNECTION:
+ g_value_set_object (value, application->connection);
+ break;
+ case PROP_JOB_MANAGER:
+ g_value_set_object (value, application->job_manager);
+ break;
+ case PROP_BOOT_MANAGER_SERVICE:
+ g_value_set_object (value, application->boot_manager_service);
+ case PROP_LA_HANDLER:
+ g_value_set_object (value, application->la_handler);
+ break;
+ case PROP_LUC_STARTER:
+ g_value_set_object (value, application->luc_starter);
+ break;
+ case PROP_MAIN_LOOP:
+ g_value_set_boxed (value, application->main_loop);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+
+static void
+boot_manager_application_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BootManagerApplication *application = BOOT_MANAGER_APPLICATION (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONNECTION:
+ application->connection = g_value_dup_object (value);
+ break;
+ case PROP_JOB_MANAGER:
+ application->job_manager = g_value_dup_object (value);
+ break;
+ case PROP_BOOT_MANAGER_SERVICE:
+ application->boot_manager_service = g_value_dup_object (value);
+ break;
+ case PROP_LA_HANDLER:
+ application->la_handler = g_value_dup_object (value);
+ break;
+ case PROP_MAIN_LOOP:
+ application->main_loop = g_main_loop_ref (g_value_get_boxed (value));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+
+static void
+boot_manager_application_luc_groups_started (LUCStarter *starter,
+ BootManagerApplication *application)
+{
+ g_return_if_fail (IS_LUC_STARTER (starter));
+ g_return_if_fail (BOOT_MANAGER_IS_APPLICATION (application));
+
+ /* notify systemd that we have finished starting the LUC and
+ * that it can take over control to start unfocused.target,
+ * lazy.target etc. */
+ sd_notify (0, "READY=1");
+}
+
+
+
+BootManagerApplication *
+boot_manager_application_new (GMainLoop *main_loop,
+ GDBusConnection *connection,
+ JobManager *job_manager,
+ LAHandlerService *la_handler,
+ BootManagerService *boot_manager_service)
+{
+ g_return_val_if_fail (main_loop != NULL, NULL);
+ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (IS_JOB_MANAGER (job_manager), NULL);
+ g_return_val_if_fail (LA_HANDLER_IS_SERVICE (la_handler), NULL);
+ g_return_val_if_fail (BOOT_MANAGER_IS_SERVICE (boot_manager_service), NULL);
+
+ return g_object_new (BOOT_MANAGER_TYPE_APPLICATION,
+ "connection", connection,
+ "boot-manager-service", boot_manager_service,
+ "job-manager", job_manager,
+ "la-handler", la_handler,
+ "main-loop", main_loop,
+ NULL);
+}
diff --git a/node-startup-controller/boot-manager-application.h b/node-startup-controller/boot-manager-application.h
new file mode 100644
index 0000000..e7e5214
--- /dev/null
+++ b/node-startup-controller/boot-manager-application.h
@@ -0,0 +1,42 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/* -
+ * Copyright (c) 2012 GENIVI.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef __BOOT_MANAGER_APPLICATION_H__
+#define __BOOT_MANAGER_APPLICATION_H__
+
+#include <gio/gio.h>
+
+#include <node-startup-controller/boot-manager-service.h>
+#include <node-startup-controller/job-manager.h>
+#include <node-startup-controller/la-handler-service.h>
+
+G_BEGIN_DECLS
+
+#define BOOT_MANAGER_TYPE_APPLICATION (boot_manager_application_get_type ())
+#define BOOT_MANAGER_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BOOT_MANAGER_TYPE_APPLICATION, BootManagerApplication))
+#define BOOT_MANAGER_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), BOOT_MANAGER_TYPE_APPLICATION, BootManagerApplicationClass))
+#define BOOT_MANAGER_IS_APPLICATION(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BOOT_MANAGER_TYPE_APPLICATION))
+#define BOOT_MANAGER_IS_APPLICATION_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), BOOT_MANAGER_TYPE_APPLICATION))
+#define BOOT_MANAGER_APPLICATION_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), BOOT_MANAGER_TYPE_APPLICATION, BootManagerApplicationClass))
+
+typedef struct _BootManagerApplicationClass BootManagerApplicationClass;
+typedef struct _BootManagerApplication BootManagerApplication;
+
+GType boot_manager_application_get_type (void) G_GNUC_CONST;
+
+BootManagerApplication *boot_manager_application_new (GMainLoop *main_loop,
+ GDBusConnection *connection,
+ JobManager *job_manager,
+ LAHandlerService *la_handler,
+ BootManagerService *boot_manager_service) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+
+G_END_DECLS
+
+#endif /* !__BOOT_MANAGER_APPLICATION_H__ */
+
diff --git a/node-startup-controller/boot-manager-dbus.xml b/node-startup-controller/boot-manager-dbus.xml
new file mode 100644
index 0000000..c1e92a0
--- /dev/null
+++ b/node-startup-controller/boot-manager-dbus.xml
@@ -0,0 +1,61 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/org/genivi/BootManager1/BootManager">
+ <!--
+ org.genivi.BootManager1.BootManager:
+ @short_description: Interface for managing the GENIVI LUC (Last User Context)
+
+ The GENIVI Boot Manager remembers applications that were used in the last
+ session of a user. It is used in order to restore these applications on
+ the next start-up.
+
+ The Boot Manager is a passive component in the sense that it does not
+ remember applications on its own; instead, applications need to register
+ and deregister themselves proactively.
+
+ Applications can be registered for different LUC types, the standard ones
+ being "foreground", "background" and "audible".
+ -->
+ <interface name="org.genivi.BootManager1.BootManager">
+ <!--
+ BeginLUCRegistration:
+
+ Initialises the LUC registration sequence at shutdown in order to register
+ the applications which are running in the LUC in this moment.
+ -->
+ <method name="BeginLUCRegistration">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+ </method>
+
+ <!--
+ RegisterWithLUC:
+ @apps: A dictionary that maps LUC types to arrays of application unit names.
+
+ Registers one or more applications for certain LUC types. Applications may
+ be listed multiple times. For LUC types where only a single application may
+ be registered at a time, the last application in the corresponding list wins.
+
+ An example for the apps parameter would be:
+
+ {
+ 0 : [ "app1.unit" ],
+ 1 : [ "app2.unit", "app3.unit" ],
+ 2 : [ "app3.unit" ]
+ }
+
+ where 0 = "foreground", 1 = background , 2 = "audible"
+ -->
+ <method name="RegisterWithLUC">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+ <arg name="apps" type="a{ias}" direction="in"/>
+ </method>
+
+ <!--
+ FinishLUCRegistration:
+
+ Finishes the LUC registration sequence and atomically replaces the previous LUC.
+ -->
+ <method name="FinishLUCRegistration">
+ <annotation name="org.freedesktop.DBus.GLib.Async" value="true"/>
+ </method>
+ </interface>
+</node>
diff --git a/node-startup-controller/boot-manager-service.c b/node-startup-controller/boot-manager-service.c
new file mode 100644
index 0000000..c775d44
--- /dev/null
+++ b/node-startup-controller/boot-manager-service.c
@@ -0,0 +1,545 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/* -
+ * Copyright (c) 2012 GENIVI.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <dlt/dlt.h>
+
+#include <node-startup-controller/glib-extensions.h>
+#include <node-startup-controller/boot-manager-dbus.h>
+#include <node-startup-controller/boot-manager-service.h>
+
+
+
+DLT_IMPORT_CONTEXT (boot_manager_context);
+
+
+
+/* property identifiers */
+enum
+{
+ PROP_0,
+ PROP_CONNECTION,
+};
+
+
+
+static void boot_manager_service_finalize (GObject *object);
+static void boot_manager_service_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void boot_manager_service_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static gboolean boot_manager_service_handle_begin_luc_registration (BootManager *interface,
+ GDBusMethodInvocation *invocation,
+ BootManagerService *service);
+static gboolean boot_manager_service_handle_finish_luc_registration (BootManager *interface,
+ GDBusMethodInvocation *invocation,
+ BootManagerService *service);
+static gboolean boot_manager_service_handle_register_with_luc (BootManager *interface,
+ GDBusMethodInvocation *invocation,
+ GVariant *apps,
+ BootManagerService *service);
+
+
+
+struct _BootManagerServiceClass
+{
+ GObjectClass __parent__;
+};
+
+struct _BootManagerService
+{
+ GObject __parent__;
+
+ GDBusConnection *connection;
+ BootManager *interface;
+
+ GVariant *current_user_context;
+ gboolean started_registration;
+};
+
+
+
+G_DEFINE_TYPE (BootManagerService, boot_manager_service, G_TYPE_OBJECT);
+
+
+
+static void
+boot_manager_service_class_init (BootManagerServiceClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = boot_manager_service_finalize;
+ gobject_class->get_property = boot_manager_service_get_property;
+ gobject_class->set_property = boot_manager_service_set_property;
+
+ g_object_class_install_property (gobject_class,
+ PROP_CONNECTION,
+ g_param_spec_object ("connection",
+ "connection",
+ "connection",
+ G_TYPE_DBUS_CONNECTION,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
+
+
+
+static void
+boot_manager_service_init (BootManagerService *service)
+{
+ service->interface = boot_manager_skeleton_new ();
+
+ /* initially, no registration is assumed to have been started */
+ service->started_registration = FALSE;
+
+ /* reset current user context */
+ service->current_user_context = NULL;
+
+ /* implement the RegisterWithLUC() handler */
+ g_signal_connect (service->interface, "handle-register-with-luc",
+ G_CALLBACK (boot_manager_service_handle_register_with_luc),
+ service);
+
+ /* implement the BeginLUCRegistration() handler */
+ g_signal_connect (service->interface, "handle-begin-lucregistration",
+ G_CALLBACK (boot_manager_service_handle_begin_luc_registration),
+ service);
+
+ /* implement the FinishLUCRegistration() handler */
+ g_signal_connect (service->interface, "handle-finish-lucregistration",
+ G_CALLBACK (boot_manager_service_handle_finish_luc_registration),
+ service);
+}
+
+
+
+static void
+boot_manager_service_finalize (GObject *object)
+{
+ BootManagerService *service = BOOT_MANAGER_SERVICE (object);
+
+ /* release the D-Bus connection object */
+ g_object_unref (service->connection);
+
+ /* release the interface skeleton */
+ g_signal_handlers_disconnect_matched (service->interface,
+ G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, service);
+ g_object_unref (service->interface);
+
+ /* release the current user context */
+ if (service->current_user_context != NULL)
+ g_variant_unref (service->current_user_context);
+
+ (*G_OBJECT_CLASS (boot_manager_service_parent_class)->finalize) (object);
+}
+
+
+
+static void
+boot_manager_service_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ BootManagerService *service = BOOT_MANAGER_SERVICE (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONNECTION:
+ g_value_set_object (value, service->connection);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+
+static void
+boot_manager_service_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ BootManagerService *service = BOOT_MANAGER_SERVICE (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONNECTION:
+ service->connection = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+
+static gboolean
+boot_manager_service_handle_begin_luc_registration (BootManager *interface,
+ GDBusMethodInvocation *invocation,
+ BootManagerService *service)
+{
+ GVariantBuilder builder;
+
+ g_return_val_if_fail (IS_BOOT_MANAGER (interface), FALSE);
+ g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);
+ g_return_val_if_fail (BOOT_MANAGER_IS_SERVICE (service), FALSE);
+
+ /* mark the last user context registration as started */
+ service->started_registration = TRUE;
+
+ /* initialize the current user context */
+ g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{ias}"));
+ service->current_user_context = g_variant_builder_end (&builder);
+
+ /* notify the caller that we have handled the method call */
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ return TRUE;
+}
+
+
+
+static gboolean
+boot_manager_service_handle_finish_luc_registration (BootManager *interface,
+ GDBusMethodInvocation *invocation,
+ BootManagerService *service)
+{
+ GError *error = NULL;
+ gchar *log_text;
+
+ g_return_val_if_fail (IS_BOOT_MANAGER (interface), FALSE);
+ g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);
+ g_return_val_if_fail (BOOT_MANAGER_IS_SERVICE (service), FALSE);
+
+ /* check if last user context registration started */
+ if (!service->started_registration)
+ {
+ log_text = g_strdup_printf ("Failed to finish LUC registration: "
+ "the registration sequence has not been started yet");
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+
+ /* notify the caller that we have handled the method call */
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ return TRUE;
+ }
+
+ /* write the last user context in a file */
+ boot_manager_service_write_luc (service, &error);
+ if (error != NULL)
+ {
+ log_text = g_strdup_printf ("Failed to finish LUC registration: %s", error->message);
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ g_error_free (error);
+ }
+
+ /* mark the last user context registration as finished */
+ service->started_registration = FALSE;
+
+ /* clear the current user context */
+ g_variant_unref (service->current_user_context);
+ service->current_user_context = NULL;
+
+ /* notify the caller that we have handled the register request */
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ return TRUE;
+}
+
+
+
+static gboolean
+boot_manager_service_handle_register_with_luc (BootManager *interface,
+ GDBusMethodInvocation *invocation,
+ GVariant *apps,
+ BootManagerService *service)
+{
+ GVariantBuilder dict_builder;
+ GHashTableIter hiter;
+ GVariantIter viter;
+ GHashTable *table;
+ GPtrArray *apps_array;
+ GVariant *current_context;
+ GVariant *current_apps;
+ GVariant *new_apps;
+ gpointer key;
+ GList *lp;
+ GList *luc_types;
+ gchar *app;
+ gchar *debug_text = NULL;
+ gchar *log_text = NULL;
+ guint n;
+ gint luc_type;
+
+ g_return_val_if_fail (IS_BOOT_MANAGER (interface), FALSE);
+ g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);
+ g_return_val_if_fail (BOOT_MANAGER_IS_SERVICE (service), FALSE);
+
+ /* check if last user context registration started */
+ if (!service->started_registration)
+ {
+ log_text = g_strdup_printf ("Failed to register apps with the LUC: "
+ "The registration sequence has not been started yet");
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+
+ /* notify the caller that we have handled the register request */
+ g_dbus_method_invocation_return_value (invocation, NULL);
+ return TRUE;
+ }
+
+ /* create a hash table to merge the current context and the newly registered apps */
+ table = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) g_ptr_array_unref);
+
+ /* obtain the current content of the last user context */
+ current_context = g_variant_ref (service->current_user_context);
+
+ /* prepare app lists for all LUC types present in the current context */
+ g_variant_iter_init (&viter, current_context);
+ while (g_variant_iter_loop (&viter, "{ias}", &luc_type, NULL))
+ {
+ g_hash_table_insert (table, GINT_TO_POINTER (luc_type),
+ g_ptr_array_new_with_free_func (g_free));
+ }
+
+ /* add app lists for LUC types that are needed for the newly registered apps */
+ g_variant_iter_init (&viter, apps);
+ while (g_variant_iter_loop (&viter, "{ias}", &luc_type, NULL))
+ {
+ g_hash_table_insert (table, GINT_TO_POINTER (luc_type),
+ g_ptr_array_new_with_free_func (g_free));
+ }
+
+ /* we now have a hash table that has all LUC types involved in the
+ * current context and in the newly registered apps */
+
+ /* fill the app lists for each LUC type involved, make sure that newly registered
+ * apps are added at the end so that they are "prioritized" */
+ g_hash_table_iter_init (&hiter, table);
+ while (g_hash_table_iter_next (&hiter, (gpointer) &key, (gpointer) &apps_array))
+ {
+ /* get apps currently registered for the LUC type */
+ current_apps = g_variant_lookup_value_with_int_key (current_context,
+ GPOINTER_TO_INT (key),
+ G_VARIANT_TYPE_STRING_ARRAY);
+
+ /* get apps to be registered for the LUC type now */
+ new_apps = g_variant_lookup_value_with_int_key (apps,
+ GPOINTER_TO_INT (key),
+ G_VARIANT_TYPE_STRING_ARRAY);
+
+ /* add all currently registered apps unless they are to be registered now.
+ * this is because we want apps to be registered now to be moved to the end
+ * of the lists */
+ for (n = 0; current_apps != NULL && n < g_variant_n_children (current_apps); n++)
+ {
+ g_variant_get_child (current_apps, n, "&s", &app);
+ if (!g_variant_string_array_has_string (new_apps, app))
+ g_ptr_array_add (apps_array, g_strdup (app));
+ }
+
+ /* add all newly registered apps at the end now */
+ for (n = 0; new_apps != NULL && n < g_variant_n_children (new_apps); n++)
+ {
+ g_variant_get_child (new_apps, n, "&s", &app);
+ g_ptr_array_add (apps_array, g_strdup (app));
+ }
+
+ /* release app lists for this LUC type */
+ if (current_apps != NULL)
+ g_variant_unref (current_apps);
+ if (new_apps != NULL)
+ g_variant_unref (new_apps);
+ }
+
+ /* construct a new dictionary variant for the new LUC */
+ g_variant_builder_init (&dict_builder, G_VARIANT_TYPE ("a{ias}"));
+
+ /* copy LUC types and corresponding apps over to the new context.
+ * make sure the order in which we add LUC types to the context
+ * dict is always the same. this is helpful for testing */
+ luc_types = g_hash_table_get_keys (table);
+ luc_types = g_list_sort (luc_types, (GCompareFunc) g_int_pointer_compare);
+ for (lp = luc_types; lp != NULL; lp = lp->next)
+ {
+ /* get the apps list registered for this LUC type */
+ apps_array = g_hash_table_lookup (table, lp->data);
+
+ /* NULL-terminate the pointer so that we can treat it as a gchar ** */
+ g_ptr_array_add (apps_array, NULL);
+
+ /* add the LUC type and its apps to the new context */
+ g_variant_builder_add (&dict_builder, "{i^as}",
+ GPOINTER_TO_INT (lp->data), apps_array->pdata);
+ }
+
+ /* free the LUC types and our LUC type to apps mapping */
+ g_list_free (luc_types);
+ g_hash_table_unref (table);
+
+ /* free the last user context */
+ g_variant_unref (service->current_user_context);
+
+ /* apply the new last user context */
+ service->current_user_context = g_variant_builder_end (&dict_builder);
+
+ /* log the new last user context */
+ debug_text = g_variant_print (service->current_user_context, TRUE);
+ log_text = g_strdup_printf ("The new context is: %s", debug_text);
+ DLT_LOG (boot_manager_context, DLT_LOG_DEBUG, DLT_STRING (log_text));
+ g_free (debug_text);
+ g_free (log_text);
+
+ /* release the current context */
+ g_variant_unref (current_context);
+
+ /* notify the caller that we have handled the register request */
+ g_dbus_method_invocation_return_value (invocation, NULL);
+
+ return TRUE;
+}
+
+
+
+BootManagerService *
+boot_manager_service_new (GDBusConnection *connection)
+{
+ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+
+ return g_object_new (BOOT_MANAGER_TYPE_SERVICE,
+ "connection", connection,
+ NULL);
+}
+
+
+
+gboolean
+boot_manager_service_start_up (BootManagerService *service,
+ GError **error)
+{
+ g_return_val_if_fail (BOOT_MANAGER_IS_SERVICE (service), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* announce the org.genivi.BootManager1.BootManager service on the bus */
+ return g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (service->interface),
+ service->connection,
+ "/org/genivi/BootManager1/BootManager",
+ error);
+}
+
+
+
+GVariant *
+boot_manager_service_read_luc (BootManagerService *service,
+ GError **error)
+{
+ const gchar *luc_path;
+ GVariant *context;
+ GFile *luc_file;
+ char *data;
+ gsize data_len;
+
+ g_return_val_if_fail (BOOT_MANAGER_IS_SERVICE (service), NULL);
+ g_return_val_if_fail ((error == NULL || *error == NULL), NULL);
+
+ /* check which configuration file to use; the LUC_PATH environment variable
+ * has priority over the build-time LUC_PATH definition */
+ luc_path = g_getenv ("LUC_PATH");
+ if (luc_path == NULL)
+ luc_path = LUC_PATH;
+
+ /* initialize the GFile */
+ luc_file = g_file_new_for_path (luc_path);
+
+ /* read the contents of the file */
+ if (!g_file_load_contents (luc_file, NULL, &data, &data_len, NULL, error))
+ {
+ g_object_unref (luc_file);
+ return NULL;
+ }
+
+ /* store the contents of the file in a GVariant */
+ context = g_variant_new_from_data (G_VARIANT_TYPE ("a{ias}"), data, data_len,
+ TRUE, g_free, data);
+
+ g_object_unref (luc_file);
+
+ return context;
+}
+
+
+
+void
+boot_manager_service_write_luc (BootManagerService *service,
+ GError **error)
+{
+ const gchar *luc_path;
+ GError *err = NULL;
+ GFile *luc_file;
+ GFile *luc_dir;
+
+ g_return_if_fail (BOOT_MANAGER_IS_SERVICE (service));
+ g_return_if_fail (error == NULL || *error == NULL);
+
+ /* check which configuration file to use; the LUC_PATH environment variable
+ * has priority over the build-time LUC_PATH definition */
+ luc_path = g_getenv ("LUC_PATH");
+ if (luc_path == NULL)
+ luc_path = LUC_PATH;
+
+ /* initialize the GFiles */
+ luc_file = g_file_new_for_path (luc_path);
+ luc_dir = g_file_get_parent (luc_file);
+
+ /* make sure the last user context's directory exists */
+ if (!g_file_make_directory_with_parents (luc_dir, NULL, &err))
+ {
+ if (err->domain == G_IO_ERROR && err->code == G_IO_ERROR_EXISTS)
+ {
+ /* clear the error for reuse */
+ g_clear_error (&err);
+ }
+ else
+ {
+ /* let the caller know there was a problem */
+ g_propagate_error (error, err);
+ g_object_unref (luc_file);
+ g_object_unref (luc_dir);
+ return;
+ }
+ }
+
+ /* replace the contents with that of the file. g_file_replace_contents
+ * guarantees atomic overwriting and can make backups */
+ g_file_replace_contents (luc_file, g_variant_get_data (service->current_user_context),
+ g_variant_get_size (service->current_user_context), NULL,
+ TRUE, G_FILE_CREATE_NONE, NULL, NULL, error);
+
+ /* release the GFiles */
+ g_object_unref (luc_file);
+ g_object_unref (luc_dir);
+}
diff --git a/node-startup-controller/boot-manager-service.h b/node-startup-controller/boot-manager-service.h
new file mode 100644
index 0000000..7b4231c
--- /dev/null
+++ b/node-startup-controller/boot-manager-service.h
@@ -0,0 +1,45 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/* -
+ * Copyright (c) 2012 GENIVI.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef __BOOT_MANAGER_SERVICE_H__
+#define __BOOT_MANAGER_SERVICE_H__
+
+#include <gio/gio.h>
+
+#include <node-startup-controller/systemd-manager-dbus.h>
+
+G_BEGIN_DECLS
+
+#define BOOT_MANAGER_TYPE_SERVICE (boot_manager_service_get_type ())
+#define BOOT_MANAGER_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), BOOT_MANAGER_TYPE_SERVICE, BootManagerService))
+#define BOOT_MANAGER_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), BOOT_MANAGER_TYPE_SERVICE, BootManagerServiceClass))
+#define BOOT_MANAGER_IS_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), BOOT_MANAGER_TYPE_SERVICE))
+#define BOOT_MANAGER_IS_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), BOOT_MANAGER_TYPE_SERVICE))
+#define BOOT_MANAGER_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), BOOT_MANAGER_TYPE_SERVICE, BootManagerServiceClass))
+
+typedef struct _BootManagerServiceClass BootManagerServiceClass;
+typedef struct _BootManagerService BootManagerService;
+
+
+
+GType boot_manager_service_get_type (void) G_GNUC_CONST;
+
+BootManagerService *boot_manager_service_new (GDBusConnection *connection) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+gboolean boot_manager_service_start_up (BootManagerService *service,
+ GError **error);
+GVariant *boot_manager_service_read_luc (BootManagerService *service,
+ GError **error);
+void boot_manager_service_write_luc (BootManagerService *service,
+ GError **error);
+
+
+G_END_DECLS
+
+#endif /* !__BOOT_MANAGER_SERVICE_H__ */
+
diff --git a/node-startup-controller/boot-manager.service.in b/node-startup-controller/boot-manager.service.in
new file mode 100644
index 0000000..e255295
--- /dev/null
+++ b/node-startup-controller/boot-manager.service.in
@@ -0,0 +1,5 @@
+[Service]
+Type = dbus
+BusName = org.genivi.BootManager1
+ExecStart = @libdir@/boot-manager-@BOOT_MANAGER_VERSION_API@/boot-manager
+WatchdogSec = 5
diff --git a/node-startup-controller/busconf/Makefile.am b/node-startup-controller/busconf/Makefile.am
new file mode 100644
index 0000000..6a03e55
--- /dev/null
+++ b/node-startup-controller/busconf/Makefile.am
@@ -0,0 +1,9 @@
+# vi:set ts=8 sw=8 noet ai nocindent:
+
+boot_manager_confdir = $(sysconfdir)/dbus-1/system.d
+
+boot_manager_conf_DATA = \
+ org.genivi.BootManager1.conf
+
+EXTRA_DIST = \
+ org.genivi.BootManager1.conf
diff --git a/node-startup-controller/busconf/org.genivi.BootManager1.conf b/node-startup-controller/busconf/org.genivi.BootManager1.conf
new file mode 100644
index 0000000..b8c422f
--- /dev/null
+++ b/node-startup-controller/busconf/org.genivi.BootManager1.conf
@@ -0,0 +1,9 @@
+<!DOCTYPE busconfig PUBLIC "-//freedesktop//DTD D-BUS Bus Configuration 1.0//EN"
+ "http://www.freedesktop.org/standards/dbus/1.0/busconfig.dtd">
+<busconfig>
+ <policy user="root">
+ <allow own="org.genivi.BootManager1"/>
+ <allow send_destination="org.genivi.BootManager1" />
+ <allow receive_sender="org.genivi.BootManager1" />
+ </policy>
+</busconfig>
diff --git a/node-startup-controller/glib-extensions.c b/node-startup-controller/glib-extensions.c
new file mode 100644
index 0000000..d3f161f
--- /dev/null
+++ b/node-startup-controller/glib-extensions.c
@@ -0,0 +1,100 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/* -
+ * Copyright (c) 2012 GENIVI.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib.h>
+
+#include <node-startup-controller/glib-extensions.h>
+
+
+
+GVariant *
+g_variant_lookup_value_with_int_key (GVariant *dictionary,
+ const gint key,
+ const GVariantType *expected_type)
+{
+ GVariantIter iter;
+ GVariant *entry;
+ GVariant *entry_key;
+ GVariant *tmp;
+ GVariant *value;
+ gboolean matches;
+
+ g_return_val_if_fail (g_variant_is_of_type (dictionary, G_VARIANT_TYPE ("a{i*}")),
+ NULL);
+
+ g_variant_iter_init (&iter, dictionary);
+
+ while ((entry = g_variant_iter_next_value (&iter)))
+ {
+ entry_key = g_variant_get_child_value (entry, 0);
+ matches = (g_variant_get_int32(entry_key) == key);
+ g_variant_unref (entry_key);
+
+ if (matches)
+ break;
+
+ g_variant_unref (entry);
+ }
+
+ if (entry == NULL)
+ return NULL;
+
+ value = g_variant_get_child_value (entry, 1);
+ g_variant_unref (entry);
+
+ if (g_variant_is_of_type (value, G_VARIANT_TYPE_VARIANT))
+ {
+ tmp = g_variant_get_variant (value);
+ g_variant_unref (value);
+
+ if (expected_type && !g_variant_is_of_type (tmp, expected_type))
+ {
+ g_variant_unref (tmp);
+ tmp = NULL;
+ }
+
+ value = tmp;
+ }
+
+ g_return_val_if_fail (expected_type == NULL || value == NULL ||
+ g_variant_is_of_type (value, expected_type), NULL);
+
+ return value;
+}
+
+
+
+gboolean
+g_variant_string_array_has_string (GVariant *array,
+ const gchar *str)
+{
+ gboolean found = FALSE;
+ gchar *current_str;
+ guint n;
+
+ for (n = 0; array != NULL && !found && n < g_variant_n_children (array); n++)
+ {
+ g_variant_get_child (array, n, "&s", &current_str);
+ if (g_strcmp0 (str, current_str) == 0)
+ found = TRUE;
+ }
+
+ return found;
+}
+
+
+gint
+g_int_pointer_compare (gconstpointer a, gconstpointer b)
+{
+ return GPOINTER_TO_INT (a) - GPOINTER_TO_INT (b);
+}
diff --git a/node-startup-controller/glib-extensions.h b/node-startup-controller/glib-extensions.h
new file mode 100644
index 0000000..8602841
--- /dev/null
+++ b/node-startup-controller/glib-extensions.h
@@ -0,0 +1,27 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/* -
+ * Copyright (c) 2012 GENIVI.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef __GLIB_EXTENSIONS_H__
+#define __GLIB_EXTENSIONS_H__
+
+#include <glib.h>
+
+G_BEGIN_DECLS
+
+GVariant *g_variant_lookup_value_with_int_key (GVariant *dictionary,
+ const gint key,
+ const GVariantType *expected_type);
+gboolean g_variant_string_array_has_string (GVariant *array,
+ const gchar *str);
+gint g_int_pointer_compare (gconstpointer a,
+ gconstpointer b);
+
+G_END_DECLS
+
+#endif /* !__GLIB_EXTENSION_H__ */
diff --git a/node-startup-controller/job-manager.c b/node-startup-controller/job-manager.c
new file mode 100644
index 0000000..a3f7318
--- /dev/null
+++ b/node-startup-controller/job-manager.c
@@ -0,0 +1,471 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/* -
+ * Copyright (c) 2012 GENIVI.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <node-startup-controller/job-manager.h>
+#include <node-startup-controller/systemd-manager-dbus.h>
+
+
+
+typedef struct _JobManagerJob JobManagerJob;
+
+
+
+/* property identifiers */
+enum
+{
+ PROP_0,
+ PROP_CONNECTION,
+ PROP_SYSTEMD_MANAGER,
+};
+
+
+
+static void job_manager_constructed (GObject *object);
+static void job_manager_finalize (GObject *object);
+static void job_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void job_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void job_manager_start_unit_reply (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data);
+static void job_manager_stop_unit_reply (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data);
+static void job_manager_job_removed (SystemdManager *systemd_manager,
+ guint id,
+ const gchar *job_name,
+ const gchar *unit,
+ const gchar *result,
+ JobManager *job_manager);
+static JobManagerJob *job_manager_job_new (JobManager *manager,
+ const gchar *unit,
+ GCancellable *cancellable,
+ JobManagerCallback callback,
+ gpointer user_data);
+static void job_manager_job_unref (JobManagerJob *job);
+static void job_manager_remember_job (JobManager *manager,
+ const gchar *job_name,
+ JobManagerJob *job);
+static void job_manager_forget_job (JobManager *manager,
+ const gchar *job_name);
+
+
+
+struct _JobManagerClass
+{
+ GObjectClass __parent__;
+};
+
+struct _JobManager
+{
+ GObject __parent__;
+
+ GDBusConnection *connection;
+ SystemdManager *systemd_manager;
+
+ GHashTable *jobs;
+};
+
+struct _JobManagerJob
+{
+ JobManager *manager;
+ gchar *unit;
+ GCancellable *cancellable;
+ JobManagerCallback callback;
+ gpointer user_data;
+};
+
+
+
+G_DEFINE_TYPE (JobManager, job_manager, G_TYPE_OBJECT);
+
+
+
+static void
+job_manager_class_init (JobManagerClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = job_manager_finalize;
+ gobject_class->constructed = job_manager_constructed;
+ gobject_class->get_property = job_manager_get_property;
+ gobject_class->set_property = job_manager_set_property;
+
+ g_object_class_install_property (gobject_class,
+ PROP_CONNECTION,
+ g_param_spec_object ("connection",
+ "connection",
+ "connection",
+ G_TYPE_DBUS_CONNECTION,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class,
+ PROP_SYSTEMD_MANAGER,
+ g_param_spec_object ("systemd-manager",
+ "systemd-manager",
+ "systemd-manager",
+ TYPE_SYSTEMD_MANAGER,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
+
+
+
+static void
+job_manager_init (JobManager *manager)
+{
+ /* create a mapping of systemd job names to job objects; we will use this
+ * to remember jobs that we started */
+ manager->jobs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
+ (GDestroyNotify) job_manager_job_unref);
+
+}
+
+
+
+static void
+job_manager_finalize (GObject *object)
+{
+ JobManager *manager = JOB_MANAGER (object);
+
+ /* release all the jobs we have remembered */
+ g_hash_table_unref (manager->jobs);
+
+ /* release the D-Bus connection */
+ g_object_unref (manager->connection);
+
+ /* release the systemd manager */
+ g_signal_handlers_disconnect_matched (manager->systemd_manager,
+ G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, manager);
+ g_object_unref (manager->systemd_manager);
+
+ /* chain up to finalize parent class */
+ (*G_OBJECT_CLASS (job_manager_parent_class)->finalize) (object);
+}
+
+
+
+static void
+job_manager_constructed (GObject *object)
+{
+ JobManager *manager = JOB_MANAGER (object);
+
+ /* connect to systemd's "JobRemoved" signal so that we are notified
+ * whenever a job is finished */
+ g_signal_connect (manager->systemd_manager, "job-removed",
+ G_CALLBACK (job_manager_job_removed), manager);
+}
+
+
+
+static void
+job_manager_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ JobManager *manager = JOB_MANAGER (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONNECTION:
+ g_value_set_object (value, manager->connection);
+ break;
+ case PROP_SYSTEMD_MANAGER:
+ g_value_set_object (value, manager->systemd_manager);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+
+static void
+job_manager_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ JobManager *manager = JOB_MANAGER (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONNECTION:
+ manager->connection = g_value_dup_object (value);
+ break;
+ case PROP_SYSTEMD_MANAGER:
+ manager->systemd_manager = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+
+static void
+job_manager_start_unit_reply (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ JobManagerJob *job = user_data;
+ GError *error = NULL;
+ gchar *job_name = NULL;
+
+ g_return_if_fail (IS_SYSTEMD_MANAGER (object));
+ g_return_if_fail (G_IS_ASYNC_RESULT (result));
+ g_return_if_fail (user_data != NULL);
+
+ /* finish the start unit call */
+ if (!systemd_manager_call_start_unit_finish (job->manager->systemd_manager,
+ &job_name, result, &error))
+ {
+ /* there was an error. notify the caller */
+ job->callback (job->manager, job->unit, "failed", error, job->user_data);
+ g_error_free (error);
+ g_free (job_name);
+
+ /* finish the job immediately */
+ job_manager_job_unref (job);
+ }
+ else
+ {
+ /* remember the job so that we can finish it in the "job-removed" signal handler.
+ * the service takes ownership of the job so we don't need to unref it here */
+ job_manager_remember_job (job->manager, job_name, job);
+ }
+}
+
+
+
+static void
+job_manager_stop_unit_reply (GObject *object,
+ GAsyncResult *result,
+ gpointer user_data)
+{
+ JobManagerJob *job = user_data;
+ GError *error = NULL;
+ gchar *job_name = NULL;
+
+ g_return_if_fail (IS_SYSTEMD_MANAGER (object));
+ g_return_if_fail (G_IS_ASYNC_RESULT (result));
+ g_return_if_fail (user_data != NULL);
+
+ /* finish the stop unit call */
+ if (!systemd_manager_call_stop_unit_finish (job->manager->systemd_manager,
+ &job_name, result, &error))
+ {
+ /* there was an error. notify the caller */
+ job->callback (job->manager, job->unit, "failed", error, job->user_data);
+ g_error_free (error);
+ g_free (job_name);
+
+ /* finish the job immediately */
+ job_manager_job_unref (job);
+ }
+ else
+ {
+ /* remember the job so that we can finish it in the "job-removed" signal handler.
+ * the service takes ownership of the job so we don't need to unref it here */
+ job_manager_remember_job (job->manager, job_name, job);
+ }
+}
+
+
+
+static void
+job_manager_job_removed (SystemdManager *systemd_manager,
+ guint id,
+ const gchar *job_name,
+ const gchar *unit,
+ const gchar *result,
+ JobManager *job_manager)
+{
+ JobManagerJob *job;
+
+ g_return_if_fail (IS_SYSTEMD_MANAGER (systemd_manager));
+ g_return_if_fail (job_name != NULL && *job_name != '\0');
+ g_return_if_fail (result != NULL && *result != '\0');
+ g_return_if_fail (unit != NULL && *unit != '\0');
+ g_return_if_fail (IS_JOB_MANAGER (job_manager));
+
+ /* look up the remembered job for this job name */
+ job = g_hash_table_lookup (job_manager->jobs, job_name);
+
+ /* if no job is found, ignore this job-removed signal */
+ if (job == NULL)
+ return;
+
+ /* finish the job by notifying the caller */
+ job->callback (job_manager, job->unit, result, NULL, job->user_data);
+
+ /* forget about this job */
+ job_manager_forget_job (job_manager, job_name);
+}
+
+
+
+static JobManagerJob *
+job_manager_job_new (JobManager *manager,
+ const gchar *unit,
+ GCancellable *cancellable,
+ JobManagerCallback callback,
+ gpointer user_data)
+{
+ JobManagerJob *job;
+
+ g_return_val_if_fail (IS_JOB_MANAGER (manager), NULL);
+ g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
+
+ /* allocate a new job struct */
+ job = g_slice_new0 (JobManagerJob);
+ job->manager = g_object_ref (manager);
+ job->unit = g_strdup(unit);
+ if (cancellable != NULL)
+ job->cancellable = g_object_ref (cancellable);
+ job->callback = callback;
+ job->user_data = user_data;
+
+ return job;
+}
+
+
+static void
+job_manager_job_unref (JobManagerJob *job)
+{
+ if (job == NULL)
+ return;
+
+ /* release all memory and references held by job */
+ if (job->cancellable != NULL)
+ g_object_unref (job->cancellable);
+ g_free (job->unit);
+ g_object_unref (job->manager);
+ g_slice_free (JobManagerJob, job);
+}
+
+
+
+static void
+job_manager_remember_job (JobManager *manager,
+ const char *job_name,
+ JobManagerJob *job)
+{
+ JobManagerJob *existing_job;
+
+ g_return_if_fail (IS_JOB_MANAGER (manager));
+ g_return_if_fail (job_name != NULL && *job_name != '\0');
+ g_return_if_fail (job != NULL);
+
+ /* if the job is already being remembered, there is a programming error that should be
+ * notified */
+ existing_job = g_hash_table_lookup (manager->jobs, job_name);
+ if (existing_job != NULL)
+ {
+ g_critical ("Trying to remember the same job twice.");
+ return;
+ }
+ /* associate the job name with the job */
+ g_hash_table_insert (manager->jobs, g_strdup (job_name), job);
+}
+
+
+
+static void
+job_manager_forget_job (JobManager *manager,
+ const gchar *job_name)
+{
+ g_return_if_fail (IS_JOB_MANAGER (manager));
+ g_return_if_fail (job_name != NULL && *job_name != '\0');
+
+ g_hash_table_remove (manager->jobs, job_name);
+}
+
+
+
+JobManager *
+job_manager_new (GDBusConnection *connection,
+ SystemdManager *systemd_manager)
+{
+ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (IS_SYSTEMD_MANAGER (systemd_manager), NULL);
+
+ return g_object_new (TYPE_JOB_MANAGER,
+ "connection", connection,
+ "systemd-manager", systemd_manager,
+ NULL);
+}
+
+
+
+void
+job_manager_start (JobManager *manager,
+ const gchar *unit,
+ GCancellable *cancellable,
+ JobManagerCallback callback,
+ gpointer user_data)
+{
+ JobManagerJob *job;
+
+ g_return_if_fail (IS_JOB_MANAGER (manager));
+ g_return_if_fail (unit != NULL);
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+ g_return_if_fail (callback != NULL);
+
+ /* create a new job object */
+ job = job_manager_job_new (manager, unit, cancellable, callback, user_data);
+
+ /* ask systemd to start the unit asynchronously */
+ systemd_manager_call_start_unit (manager->systemd_manager, unit, "fail", cancellable,
+ job_manager_start_unit_reply, job);
+}
+
+
+
+void
+job_manager_stop (JobManager *manager,
+ const gchar *unit,
+ GCancellable *cancellable,
+ JobManagerCallback callback,
+ gpointer user_data)
+{
+ JobManagerJob *job;
+
+ g_return_if_fail (IS_JOB_MANAGER (manager));
+ g_return_if_fail (unit != NULL);
+ g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
+ g_return_if_fail (callback != NULL);
+
+ /* create a new job object */
+ job = job_manager_job_new (manager, unit, cancellable, callback, user_data);
+
+ /* ask systemd to stop the unit asynchronously */
+ systemd_manager_call_stop_unit (manager->systemd_manager, unit, "fail", cancellable,
+ job_manager_stop_unit_reply, job);
+}
diff --git a/node-startup-controller/job-manager.h b/node-startup-controller/job-manager.h
new file mode 100644
index 0000000..9eed6b2
--- /dev/null
+++ b/node-startup-controller/job-manager.h
@@ -0,0 +1,49 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/* -
+ * Copyright (c) 2012 GENIVI.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef __JOB_MANAGER_H__
+#define __JOB_MANAGER_H__
+
+#include <node-startup-controller/systemd-manager-dbus.h>
+
+G_BEGIN_DECLS
+
+#define TYPE_JOB_MANAGER (job_manager_get_type())
+#define JOB_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_JOB_MANAGER, JobManager))
+#define JOB_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_JOB_MANAGER, JobManagerClass))
+#define IS_JOB_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_JOB_MANAGER))
+#define IS_JOB_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass, TYPE_JOB_MANAGER))
+#define JOB_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj, TYPE_JOB_MANAGER, JobManagerClass))
+
+typedef struct _JobManagerClass JobManagerClass;
+typedef struct _JobManager JobManager;
+
+typedef void (*JobManagerCallback) (JobManager *manager,
+ const gchar *unit,
+ const gchar *result,
+ GError *error,
+ gpointer user_data);
+
+GType job_manager_get_type (void) G_GNUC_CONST;
+JobManager *job_manager_new (GDBusConnection *connection,
+ SystemdManager *systemd_manager) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+void job_manager_start (JobManager *manager,
+ const gchar *unit,
+ GCancellable *cancellable,
+ JobManagerCallback callback,
+ gpointer user_data);
+void job_manager_stop (JobManager *manager,
+ const gchar *unit,
+ GCancellable *cancellable,
+ JobManagerCallback callback,
+ gpointer user_data);
+
+G_END_DECLS
+
+#endif /* !__JOB_MANAGER_H__ */
diff --git a/node-startup-controller/la-handler-service.c b/node-startup-controller/la-handler-service.c
new file mode 100644
index 0000000..edcd219
--- /dev/null
+++ b/node-startup-controller/la-handler-service.c
@@ -0,0 +1,683 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/* -
+ * Copyright (c) 2012 GENIVI.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <dlt/dlt.h>
+
+#include <common/la-handler-dbus.h>
+#include <common/nsm-consumer-dbus.h>
+#include <common/nsm-enum-types.h>
+#include <common/shutdown-client.h>
+#include <common/shutdown-consumer-dbus.h>
+
+#include <node-startup-controller/job-manager.h>
+#include <node-startup-controller/la-handler-service.h>
+
+
+
+DLT_IMPORT_CONTEXT (la_handler_context);
+
+
+
+/* property identifiers */
+enum
+{
+ PROP_0,
+ PROP_CONNECTION,
+ PROP_JOB_MANAGER,
+};
+
+
+
+typedef struct _LAHandlerServiceData LAHandlerServiceData;
+
+
+
+static void la_handler_service_constructed (GObject *object);
+static void la_handler_service_finalize (GObject *object);
+static void la_handler_service_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void la_handler_service_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static gboolean la_handler_service_handle_register (LAHandler *interface,
+ GDBusMethodInvocation *invocation,
+ const gchar *unit,
+ NSMShutdownType mode,
+ guint timeout,
+ LAHandlerService *service);
+static void la_handler_service_handle_register_finish (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data);
+static gboolean la_handler_service_handle_consumer_lifecycle_request (ShutdownConsumer *consumer,
+ GDBusMethodInvocation *invocation,
+ guint request,
+ guint request_id,
+ ShutdownClient *client);
+static void la_handler_service_handle_consumer_lifecycle_request_finish (JobManager *manager,
+ const gchar *unit,
+ const gchar *result,
+ GError *error,
+ gpointer user_data);
+static LAHandlerServiceData *la_handler_service_data_new (LAHandlerService *service,
+ GDBusMethodInvocation *invocation,
+ guint request_id);
+static void la_handler_service_data_unref (LAHandlerServiceData *data);
+
+
+
+struct _LAHandlerServiceClass
+{
+ GObjectClass __parent__;
+};
+
+struct _LAHandlerService
+{
+ GObject __parent__;
+
+ GDBusConnection *connection;
+ LAHandler *interface;
+ JobManager *job_manager;
+
+ /* Associations of shutdown clients and their units */
+ GHashTable *units_to_clients;
+ GHashTable *clients_to_units;
+
+ const gchar *prefix;
+ guint index;
+ guint bus_name_id;
+
+ /* connection to the NSM consumer interface */
+ NSMConsumer *nsm_consumer;
+};
+
+struct _LAHandlerServiceData
+{
+ GDBusMethodInvocation *invocation;
+ LAHandlerService *service;
+ guint request_id;
+};
+
+
+
+G_DEFINE_TYPE (LAHandlerService, la_handler_service, G_TYPE_OBJECT);
+
+
+
+static void
+la_handler_service_class_init (LAHandlerServiceClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->constructed = la_handler_service_constructed;
+ gobject_class->finalize = la_handler_service_finalize;
+ gobject_class->get_property = la_handler_service_get_property;
+ gobject_class->set_property = la_handler_service_set_property;
+
+ g_object_class_install_property (gobject_class,
+ PROP_CONNECTION,
+ g_param_spec_object ("connection",
+ "connection",
+ "connection",
+ G_TYPE_DBUS_CONNECTION,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+ g_object_class_install_property (gobject_class,
+ PROP_JOB_MANAGER,
+ g_param_spec_object ("job-manager",
+ "Job Manager",
+ "The internal handler of Start()"
+ " and Stop() jobs",
+ TYPE_JOB_MANAGER,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
+
+
+
+static void
+la_handler_service_constructed (GObject *object)
+{
+ LAHandlerService *service = LA_HANDLER_SERVICE (object);
+ GError *error = NULL;
+ gchar *log_text;
+
+ /* connect to the node state manager */
+ service->nsm_consumer =
+ nsm_consumer_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM, G_DBUS_PROXY_FLAGS_NONE,
+ "com.contiautomotive.NodeStateManager",
+ "/com/contiautomotive/NodeStateManager/Consumer",
+ NULL, &error);
+ if (error != NULL)
+ {
+ log_text = g_strdup_printf ("Error occurred connecting to NSM Consumer: %s",
+ error->message);
+ DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ g_error_free (error);
+ }
+}
+
+
+
+static void
+la_handler_service_init (LAHandlerService *service)
+{
+ service->interface = la_handler_skeleton_new ();
+
+ /* the number that follows the prefix in the shutdown client's object path,
+ * making every shutdown client unique */
+ service->index = 1;
+
+ /* the string that precedes the index in the shutdown client's object path */
+ service->prefix = "/org/genivi/BootManager1/ShutdownConsumer";
+
+ /* initialize the association of shutdown client to units */
+ service->units_to_clients = g_hash_table_new_full (g_str_hash, g_str_equal,
+ (GDestroyNotify) g_free,
+ (GDestroyNotify) g_object_unref);
+ service->clients_to_units = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ (GDestroyNotify) g_object_unref,
+ (GDestroyNotify) g_free);
+
+ /* implement the Register() handler */
+ g_signal_connect (service->interface, "handle-register",
+ G_CALLBACK (la_handler_service_handle_register),
+ service);
+}
+
+
+
+static void
+la_handler_service_finalize (GObject *object)
+{
+ LAHandlerService *service = LA_HANDLER_SERVICE (object);
+
+ /* release the bus name */
+ g_bus_unown_name (service->bus_name_id);
+
+ /* release the NSM consumer service object, if there is one */
+ if (service->nsm_consumer != NULL)
+ g_object_unref (service->nsm_consumer);
+
+ /* release the interface skeleton */
+ g_signal_handlers_disconnect_matched (service->interface,
+ G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, service);
+ g_object_unref (service->interface);
+
+ /* release the job manager skeleton */
+ g_object_unref (service->job_manager);
+
+ /* release the D-Bus connection object */
+ if (service->connection != NULL)
+ g_object_unref (service->connection);
+
+ /* release the shutdown clients */
+ g_hash_table_unref (service->units_to_clients);
+ g_hash_table_unref (service->clients_to_units);
+
+ (*G_OBJECT_CLASS (la_handler_service_parent_class)->finalize) (object);
+}
+
+
+
+static void
+la_handler_service_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ LAHandlerService *service = LA_HANDLER_SERVICE (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONNECTION:
+ g_value_set_object (value, service->connection);
+ break;
+ case PROP_JOB_MANAGER:
+ g_value_set_object (value, service->job_manager);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+
+static void
+la_handler_service_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ LAHandlerService *service = LA_HANDLER_SERVICE (object);
+
+ switch (prop_id)
+ {
+ case PROP_CONNECTION:
+ service->connection = g_value_dup_object (value);
+ break;
+ case PROP_JOB_MANAGER:
+ service->job_manager = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+
+static gboolean
+la_handler_service_handle_register (LAHandler *interface,
+ GDBusMethodInvocation *invocation,
+ const gchar *unit,
+ NSMShutdownType shutdown_mode,
+ guint timeout,
+ LAHandlerService *service)
+{
+ ShutdownConsumer *consumer;
+ ShutdownClient *client;
+ GError *error = NULL;
+ const gchar *existing_bus_name;
+ const gchar *existing_object_path;
+ gchar *bus_name;
+ gchar *log_text;
+ gchar *object_path;
+
+ g_return_val_if_fail (IS_LA_HANDLER (interface), FALSE);
+ g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);
+ g_return_val_if_fail (unit != NULL && *unit != '\0', FALSE);
+ g_return_val_if_fail (LA_HANDLER_IS_SERVICE (service), FALSE);
+
+ /* find out if we have a shutdown client for this unit already */
+ client = g_hash_table_lookup (service->units_to_clients, unit);
+ if (client != NULL)
+ {
+ /* there already is a shutdown client for the unit, so simply
+ * re-register its client with the new shutdown mode and timeout */
+
+ /* extract information from the client */
+ consumer = shutdown_client_get_consumer (client);
+ existing_bus_name = shutdown_client_get_bus_name (client);
+ existing_object_path = shutdown_client_get_object_path (client);
+
+ /* temporarily store a reference to the legacy app handler service object
+ * in the invocation object */
+ g_object_set_data_full (G_OBJECT (invocation), "la-handler-service",
+ g_object_ref (service), (GDestroyNotify) g_object_unref);
+
+ /* re-register the shutdown consumer with the NSM Consumer */
+ nsm_consumer_call_register_shutdown_client (service->nsm_consumer,
+ existing_bus_name, existing_object_path,
+ shutdown_mode, timeout, NULL,
+ la_handler_service_handle_register_finish,
+ invocation);
+ }
+ else
+ {
+ /* create a new shutdown client and consumer for the unit */
+ bus_name = "org.genivi.BootManager1";
+ object_path = g_strdup_printf ("%s/%u", service->prefix, service->index);
+ client = shutdown_client_new (bus_name, object_path, shutdown_mode, timeout);
+ consumer = shutdown_consumer_skeleton_new ();
+ shutdown_client_set_consumer (client, consumer);
+
+ /* remember the legacy app handler service object in shutdown client */
+ g_object_set_data_full (G_OBJECT (client), "la-handler-service",
+ g_object_ref (service), (GDestroyNotify) g_object_unref);
+
+ /* implement the LifecycleRequest method of the shutdown consumer */
+ g_signal_connect (consumer, "handle-lifecycle-request",
+ G_CALLBACK (la_handler_service_handle_consumer_lifecycle_request),
+ client);
+
+ /* associate the shutdown client with the unit name */
+ g_hash_table_insert (service->units_to_clients, g_strdup (unit),
+ g_object_ref (client));
+ g_hash_table_insert (service->clients_to_units, g_object_ref (client),
+ g_strdup (unit));
+
+ /* export the shutdown consumer on the bus */
+ g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (consumer),
+ service->connection, object_path, &error);
+ if (error != NULL)
+ {
+ log_text = g_strdup_printf ("Failed to export shutdown consumer on the bus: %s",
+ error->message);
+ DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ g_error_free (error);
+ }
+
+ /* temporarily store a reference to the legacy app handler service object
+ * in the invocation object */
+ g_object_set_data_full (G_OBJECT (invocation), "la-handler-service",
+ g_object_ref (service), (GDestroyNotify) g_object_unref);
+
+ /* register the shutdown consumer with the NSM Consumer */
+ nsm_consumer_call_register_shutdown_client (service->nsm_consumer,
+ bus_name, object_path,
+ shutdown_mode, timeout, NULL,
+ la_handler_service_handle_register_finish,
+ invocation);
+
+ /* free strings and release the shutdown consumer */
+ g_free (object_path);
+ g_object_unref (consumer);
+
+ /* increment the counter for our shutdown consumer object paths */
+ service->index++;
+ }
+
+ return TRUE;
+}
+
+
+
+static void
+la_handler_service_handle_register_finish (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (user_data);
+ LAHandlerService *service;
+ NSMConsumer *nsm_consumer = NSM_CONSUMER (object);
+ GError *error = NULL;
+ gchar *log_text;
+ gint error_code;
+
+ g_return_if_fail (IS_NSM_CONSUMER (nsm_consumer));
+ g_return_if_fail (G_IS_ASYNC_RESULT (res));
+ g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation));
+
+ /* finish registering the shutdown client */
+ nsm_consumer_call_register_shutdown_client_finish (nsm_consumer, &error_code, res,
+ &error);
+ if (error != NULL)
+ {
+ log_text = g_strdup_printf ("Failed to register a shutdown consumer: %s",
+ error->message);
+ DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ g_error_free (error);
+ }
+
+ /* retrieve the LAHandlerService from the invocation object */
+ service = g_object_get_data (G_OBJECT (invocation), "la-handler-service");
+
+ /* notify the caller that we have handled the registration request */
+ la_handler_complete_register (service->interface, invocation);
+}
+
+
+
+static gboolean
+la_handler_service_handle_consumer_lifecycle_request (ShutdownConsumer *consumer,
+ GDBusMethodInvocation *invocation,
+ guint request,
+ guint request_id,
+ ShutdownClient *client)
+{
+ LAHandlerServiceData *data;
+ LAHandlerService *service;
+ gchar *unit_name;
+
+ g_return_val_if_fail (IS_SHUTDOWN_CONSUMER (consumer), FALSE);
+ g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);
+ g_return_val_if_fail (IS_SHUTDOWN_CLIENT (client), FALSE);
+
+ /* get the service from the shutdown client */
+ service = g_object_get_data (G_OBJECT (client), "la-handler-service");
+
+ /* look up the unit name associated with this shutdown client */
+ unit_name = g_hash_table_lookup (service->clients_to_units, client);
+
+ if (unit_name != NULL)
+ {
+ data = la_handler_service_data_new (service, NULL, request_id);
+
+ /* stop this unit now */
+ job_manager_stop (service->job_manager, unit_name, NULL,
+ la_handler_service_handle_consumer_lifecycle_request_finish,
+ data);
+
+ /* let the NSM know that we are working on this request */
+ shutdown_consumer_complete_lifecycle_request (consumer, invocation,
+ NSM_ERROR_STATUS_RESPONSE_PENDING);
+ }
+ else
+ {
+ /* NSM asked us to shutdown a shutdown consumer we did not register;
+ * make it aware by returning an error */
+ shutdown_consumer_complete_lifecycle_request (consumer, invocation,
+ NSM_ERROR_STATUS_ERROR);
+ }
+
+ return TRUE;
+}
+
+
+
+static void
+la_handler_service_handle_consumer_lifecycle_request_finish (JobManager *manager,
+ const gchar *unit,
+ const gchar *result,
+ GError *error,
+ gpointer user_data)
+{
+ LAHandlerServiceData *data = (LAHandlerServiceData *)user_data;
+ GError *err = NULL;
+ gchar *log_text;
+ gint error_status = NSM_ERROR_STATUS_OK;
+ gint status = NSM_ERROR_STATUS_OK;
+
+ g_return_if_fail (IS_JOB_MANAGER (manager));
+ g_return_if_fail (unit != NULL && *unit != '\0');
+ g_return_if_fail (result != NULL && *result != '\0');
+ g_return_if_fail (data != NULL);
+
+ /* log that we are completing a lifecycle request */
+ log_text = g_strdup_printf ("Completing lifecycle request: request id %u",
+ data->request_id);
+ DLT_LOG (la_handler_context, DLT_LOG_INFO, DLT_STRING (log_text));
+ g_free (log_text);
+
+ /* log an error if shutting down the consumer has failed */
+ if (error != NULL)
+ {
+ log_text = g_strdup_printf ("Failed to shutdown a shutdown consumer: %s",
+ error->message);
+ DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+
+ /* send an error back to the NSM */
+ status = NSM_ERROR_STATUS_ERROR;
+ }
+
+ /* log an error if systemd failed to stop the consumer */
+ if (g_strcmp0 (result, "failed") == 0)
+ {
+ DLT_LOG (la_handler_context, DLT_LOG_ERROR,
+ DLT_STRING ("Failed to shutdown a shutdown consumer"));
+
+ /* send an error back to the NSM */
+ status = NSM_ERROR_STATUS_ERROR;
+ }
+
+ /* let the NSM know that we have handled the lifecycle request */
+ if (!nsm_consumer_call_lifecycle_request_complete_sync (data->service->nsm_consumer,
+ data->request_id, status,
+ &error_status, NULL, &err))
+ {
+ log_text = g_strdup_printf ("Failed to notify Node State Manager about completed "
+ "lifecycle request: request id %u: %s",
+ data->request_id, err->message);
+ DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ g_error_free (err);
+ }
+ else if (error_status == NSM_ERROR_STATUS_OK)
+ {
+ log_text = g_strdup_printf ("Successfully notified Node State Manager about "
+ "completed lifecycle request: request id %u",
+ data->request_id);
+ DLT_LOG (la_handler_context, DLT_LOG_INFO, DLT_STRING (log_text));
+ g_free (log_text);
+ }
+ else
+ {
+ log_text = g_strdup_printf ("Failed to notify Node State Manager about completed "
+ "lifecycle request: request id %u, error status %u",
+ data->request_id, error_status);
+ DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ }
+
+ la_handler_service_data_unref (data);
+}
+
+
+
+static LAHandlerServiceData *
+la_handler_service_data_new (LAHandlerService *service,
+ GDBusMethodInvocation *invocation,
+ guint request_id)
+{
+ LAHandlerServiceData *data;
+
+ data = g_slice_new0 (LAHandlerServiceData);
+ if (service != NULL)
+ data->service = g_object_ref (service);
+ if (invocation != NULL)
+ data->invocation = g_object_ref (invocation);
+ data->request_id = request_id;
+
+ return data;
+}
+
+
+
+static void
+la_handler_service_data_unref (LAHandlerServiceData *data)
+{
+ if (data == NULL)
+ return;
+
+ if (data->invocation != NULL)
+ g_object_unref (data->invocation);
+ if (data->service != NULL)
+ g_object_unref (data->service);
+ g_slice_free (LAHandlerServiceData, data);
+}
+
+
+
+LAHandlerService *
+la_handler_service_new (GDBusConnection *connection,
+ JobManager *job_manager)
+{
+ g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
+ g_return_val_if_fail (IS_JOB_MANAGER (job_manager), NULL);
+
+ return g_object_new (LA_HANDLER_TYPE_SERVICE,
+ "connection", connection,
+ "job-manager", job_manager,
+ NULL);
+}
+
+
+gboolean
+la_handler_service_start (LAHandlerService *service,
+ GError **error)
+{
+ g_return_val_if_fail (LA_HANDLER_IS_SERVICE (service), FALSE);
+ g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
+
+ /* announce the org.genivi.BootManager1.LegacyAppHandler service on the bus */
+ return g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (service->interface),
+ service->connection,
+ "/org/genivi/BootManager1/LegacyAppHandler",
+ error);
+}
+
+
+
+NSMConsumer *
+la_handler_service_get_nsm_consumer (LAHandlerService *service)
+{
+ g_return_val_if_fail (LA_HANDLER_IS_SERVICE (service), NULL);
+
+ return service->nsm_consumer;
+}
+
+
+
+void
+la_handler_service_deregister_consumers (LAHandlerService *service)
+{
+ GHashTableIter iter;
+ ShutdownClient *client;
+ const gchar *bus_name;
+ const gchar *object_path;
+ const gchar *unit;
+ GError *error = NULL;
+ gchar *log_text;
+ gint error_code;
+ gint shutdown_mode;
+
+ g_return_if_fail (LA_HANDLER_IS_SERVICE (service));
+
+ g_hash_table_iter_init (&iter, service->clients_to_units);
+ while (g_hash_table_iter_next (&iter, (gpointer *)&client, (gpointer *)&unit))
+ {
+ /* extract data from the current client */
+ bus_name = shutdown_client_get_bus_name (client);
+ object_path = shutdown_client_get_object_path (client);
+ shutdown_mode = shutdown_client_get_shutdown_mode (client);
+
+ /* unregister the shutdown client */
+ nsm_consumer_call_un_register_shutdown_client_sync (service->nsm_consumer,
+ bus_name, object_path,
+ shutdown_mode, &error_code,
+ NULL, &error);
+
+ if (error != NULL)
+ {
+ log_text = g_strdup_printf ("Failed to unregister shutdown client %s "
+ "for unit %s: %s", object_path, unit,
+ error->message);
+ DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ g_error_free (error);
+ }
+ else if (error_code != NSM_ERROR_STATUS_OK)
+ {
+ log_text = g_strdup_printf ("Failed to unregister shutdown client %s "
+ "for unit %s: error code %d", object_path, unit,
+ error_code);
+ DLT_LOG (la_handler_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ }
+ }
+}
diff --git a/node-startup-controller/la-handler-service.h b/node-startup-controller/la-handler-service.h
new file mode 100644
index 0000000..831fcca
--- /dev/null
+++ b/node-startup-controller/la-handler-service.h
@@ -0,0 +1,49 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/* -
+ * Copyright (c) 2012 GENIVI.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef __LA_HANDLER_SERVICE_H__
+#define __LA_HANDLER_SERVICE_H__
+
+#include <gio/gio.h>
+
+#include <common/nsm-consumer-dbus.h>
+
+#include <node-startup-controller/job-manager.h>
+
+G_BEGIN_DECLS
+
+#define LA_HANDLER_TYPE_SERVICE (la_handler_service_get_type ())
+#define LA_HANDLER_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), LA_HANDLER_TYPE_SERVICE, LAHandlerService))
+#define LA_HANDLER_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), LA_HANDLER_TYPE_SERVICE, LAHandlerServiceClass))
+#define LA_HANDLER_IS_SERVICE(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), LA_HANDLER_TYPE_SERVICE))
+#define LA_HANDLER_IS_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), LA_HANDLER_TYPE_SERVICE))
+#define LA_HANDLER_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), LA_HANDLER_TYPE_SERVICE, LAHandlerServiceClass))
+
+typedef struct _LAHandlerServiceClass LAHandlerServiceClass;
+typedef struct _LAHandlerService LAHandlerService;
+
+GType la_handler_service_get_type (void) G_GNUC_CONST;
+
+LAHandlerService *la_handler_service_new (GDBusConnection *connection,
+ JobManager *job_manager) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+gboolean la_handler_service_start (LAHandlerService *service,
+ GError **error);
+void la_handler_service_register (LAHandlerService *service,
+ const gchar *unit,
+ const gchar *mode,
+ guint timeout,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+NSMConsumer *la_handler_service_get_nsm_consumer (LAHandlerService *service);
+void la_handler_service_deregister_consumers (LAHandlerService *service);
+
+G_END_DECLS
+
+#endif /* !__LA_HANDLER_SERVICE_H__ */
+
diff --git a/node-startup-controller/luc-starter.c b/node-startup-controller/luc-starter.c
new file mode 100644
index 0000000..071f174
--- /dev/null
+++ b/node-startup-controller/luc-starter.c
@@ -0,0 +1,663 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/* -
+ * Copyright (c) 2012 GENIVI.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <dlt/dlt.h>
+
+#include <common/nsm-lifecycle-control-dbus.h>
+
+#include <node-startup-controller/boot-manager-service.h>
+#include <node-startup-controller/job-manager.h>
+#include <node-startup-controller/luc-starter.h>
+
+
+
+DLT_IMPORT_CONTEXT (boot_manager_context);
+
+
+
+/* signal identifiers */
+enum
+{
+ SIGNAL_LUC_GROUPS_STARTED,
+ LAST_SIGNAL,
+};
+
+
+
+/* property identifiers */
+enum
+{
+ PROP_0,
+ PROP_JOB_MANAGER,
+ PROP_BOOT_MANAGER_SERVICE,
+};
+
+
+static void luc_starter_constructed (GObject *object);
+static void luc_starter_finalize (GObject *object);
+static void luc_starter_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void luc_starter_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static gint luc_starter_compare_luc_types (gconstpointer a,
+ gconstpointer b,
+ gpointer user_data);
+static void luc_starter_start_next_group (LUCStarter *starter);
+static void luc_starter_start_app (const gchar *app,
+ LUCStarter *starter);
+static void luc_starter_start_app_finish (JobManager *manager,
+ const gchar *unit,
+ const gchar *result,
+ GError *error,
+ gpointer user_data);
+static void luc_starter_cancel_start (const gchar *app,
+ GCancellable *cancellable,
+ gpointer user_data);
+static void luc_starter_check_luc_required_finish (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data);
+static void luc_starter_start_groups_for_real (LUCStarter *starter);
+
+
+
+struct _LUCStarterClass
+{
+ GObjectClass __parent__;
+};
+
+struct _LUCStarter
+{
+ GObject __parent__;
+
+ JobManager *job_manager;
+ BootManagerService *boot_manager_service;
+ NSMLifecycleControl *nsm_lifecycle_control;
+
+ GArray *prioritised_types;
+
+ GArray *start_order;
+ GHashTable *start_groups;
+
+ GHashTable *cancellables;
+};
+
+
+
+static guint luc_starter_signals[LAST_SIGNAL];
+
+
+
+G_DEFINE_TYPE (LUCStarter, luc_starter, G_TYPE_OBJECT);
+
+
+
+static void
+luc_starter_class_init (LUCStarterClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->constructed = luc_starter_constructed;
+ gobject_class->finalize = luc_starter_finalize;
+ gobject_class->get_property = luc_starter_get_property;
+ gobject_class->set_property = luc_starter_set_property;
+
+ g_object_class_install_property (gobject_class,
+ PROP_JOB_MANAGER,
+ g_param_spec_object ("job-manager",
+ "Job Manager",
+ "The internal handler of Start()"
+ " and Stop() jobs",
+ TYPE_JOB_MANAGER,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ g_object_class_install_property (gobject_class,
+ PROP_BOOT_MANAGER_SERVICE,
+ g_param_spec_object ("boot-manager-service",
+ "boot-manager-service",
+ "boot-manager-service",
+ BOOT_MANAGER_TYPE_SERVICE,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+
+ luc_starter_signals[SIGNAL_LUC_GROUPS_STARTED] =
+ g_signal_new ("luc-groups-started",
+ TYPE_LUC_STARTER,
+ G_SIGNAL_RUN_LAST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+}
+
+
+
+static void
+luc_starter_init (LUCStarter *starter)
+{
+ /* allocate data structures for the start order and groups */
+ starter->start_order = g_array_new (FALSE, TRUE, sizeof (gint));
+ starter->start_groups = g_hash_table_new_full (g_direct_hash, g_direct_equal,
+ NULL, (GDestroyNotify) g_ptr_array_free);
+
+ /* allocate a mapping of app names to correspoding cancellables */
+ starter->cancellables = g_hash_table_new_full (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify) g_object_unref);
+}
+
+
+
+static void
+luc_starter_constructed (GObject *object)
+{
+ LUCStarter *starter = LUC_STARTER (object);
+ GError *error = NULL;
+ gchar **types;
+ gchar *log_text;
+ guint n;
+ gint type;
+
+ /* connect to the node state manager */
+ starter->nsm_lifecycle_control =
+ nsm_lifecycle_control_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ "com.contiautomotive.NodeStateManager",
+ "/com/contiautomotive/NodeStateManager/LifecycleControl",
+ NULL, &error);
+ if (error != NULL)
+ {
+ log_text = g_strdup_printf ("Failed to connect to the NSM lifecycle control: %s",
+ error->message);
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ g_error_free (error);
+ }
+
+ /* parse the prioritised LUC types defined at build-time */
+ types = g_strsplit (PRIORITISED_LUC_TYPES, ",", -1);
+ starter->prioritised_types = g_array_new (FALSE, TRUE, sizeof (gint));
+ for (n = 0; types != NULL && types[n] != NULL; n++)
+ {
+ type = strtol (types[n], NULL, 10);
+ g_array_append_val (starter->prioritised_types, type);
+ }
+ g_strfreev (types);
+}
+
+
+
+static void
+luc_starter_finalize (GObject *object)
+{
+ LUCStarter *starter = LUC_STARTER (object);
+
+ /* release NSMLifecycleControl */
+ if (starter->nsm_lifecycle_control != NULL)
+ g_object_unref (starter->nsm_lifecycle_control);
+
+ /* release start order, and groups */
+ g_array_free (starter->start_order, TRUE);
+ g_hash_table_unref (starter->start_groups);
+
+ /* release the cancellables */
+ g_hash_table_unref (starter->cancellables);
+
+ /* free the prioritised types array */
+ g_array_free (starter->prioritised_types, TRUE);
+
+ /* release the job manager */
+ g_object_unref (starter->job_manager);
+
+ /* release the boot manager service */
+ g_object_unref (starter->boot_manager_service);
+
+ (*G_OBJECT_CLASS (luc_starter_parent_class)->finalize) (object);
+}
+
+
+
+static void
+luc_starter_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ LUCStarter *starter = LUC_STARTER (object);
+
+ switch (prop_id)
+ {
+ case PROP_JOB_MANAGER:
+ g_value_set_object (value, starter->job_manager);
+ break;
+ case PROP_BOOT_MANAGER_SERVICE:
+ g_value_set_object (value, starter->boot_manager_service);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+
+static void
+luc_starter_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ LUCStarter *starter = LUC_STARTER (object);
+
+ switch (prop_id)
+ {
+ case PROP_JOB_MANAGER:
+ starter->job_manager = g_value_dup_object (value);
+ break;
+ case PROP_BOOT_MANAGER_SERVICE:
+ starter->boot_manager_service = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+
+static gint
+luc_starter_compare_luc_types (gconstpointer a,
+ gconstpointer b,
+ gpointer user_data)
+{
+ LUCStarter *starter = LUC_STARTER (user_data);
+ guint n;
+ gint type;
+ gint type_a = *(gint *)a;
+ gint type_b = *(gint *)b;
+ gint pos_a = G_MAXINT;
+ gint pos_b = G_MAXINT;
+
+ /* try to find the first type in the prioritised types list */
+ for (n = 0; pos_a == G_MAXINT && n < starter->prioritised_types->len; n++)
+ {
+ type = g_array_index (starter->prioritised_types, gint, n);
+ if (type == type_a)
+ pos_a = n;
+ }
+
+ /* try to find the second type in the prioritised types list */
+ for (n = 0; pos_b == G_MAXINT && n < starter->prioritised_types->len; n++)
+ {
+ type = g_array_index (starter->prioritised_types, gint, n);
+ if (type == type_b)
+ pos_b = n;
+ }
+
+ /* this statement has the following effect when sorting:
+ * - a and b are prioritised -> prioritization order is preserved
+ * - a is prioritised, b isn't -> negative return value, a comes first
+ * - b is prioritised, a isn't -> positive return value, b comes first
+ * - neither a nor b prioritised -> return value is 0, arbitrary order
+ */
+ return pos_a - pos_b;
+}
+
+
+
+static void
+luc_starter_start_next_group (LUCStarter *starter)
+{
+ GPtrArray *apps;
+ gint group;
+
+ g_return_if_fail (IS_LUC_STARTER (starter));
+ g_return_if_fail (starter->start_order->len > 0);
+
+ /* fetch the next group */
+ group = g_array_index (starter->start_order, gint, 0);
+
+ g_debug ("start group %i", group);
+
+ /* look up the apps for the group */
+ apps = g_hash_table_lookup (starter->start_groups, GINT_TO_POINTER (group));
+ if (apps != NULL)
+ {
+ /* launch all the applications in the group asynchronously */
+ g_ptr_array_foreach (apps, (GFunc) luc_starter_start_app, starter);
+ }
+}
+
+
+
+static void
+luc_starter_start_app (const gchar *app,
+ LUCStarter *starter)
+{
+ GCancellable *cancellable;
+
+ g_return_if_fail (app != NULL && *app != '\0');
+ g_return_if_fail (IS_LUC_STARTER (starter));
+
+ g_debug ("start app '%s'", app);
+
+ /* create the new cancellable */
+ cancellable = g_cancellable_new ();
+
+ /* store an association between each app and its cancellable, so that it is possible to
+ * call g_cancellable_cancel() for each respective app. */
+ g_hash_table_insert (starter->cancellables, g_strdup (app), cancellable);
+
+ /* start the service, passing the cancellable */
+ job_manager_start (starter->job_manager, app, cancellable,
+ luc_starter_start_app_finish,
+ g_object_ref (starter));
+}
+
+
+
+static void
+luc_starter_start_app_finish (JobManager *manager,
+ const gchar *unit,
+ const gchar *result,
+ GError *error,
+ gpointer user_data)
+{
+ LUCStarter *starter = LUC_STARTER (user_data);
+ GPtrArray *apps;
+ gboolean app_found = FALSE;
+ gchar *message;
+ guint n;
+ gint group;
+
+ g_return_if_fail (IS_JOB_MANAGER (manager));
+ g_return_if_fail (unit != NULL && *unit != '\0');
+ g_return_if_fail (IS_LUC_STARTER (user_data));
+ g_return_if_fail (starter->start_order->len > 0);
+
+ g_debug ("start app '%s' finish", unit);
+
+ /* respond to errors */
+ if (error != NULL)
+ {
+ message = g_strdup_printf ("Failed to start the LUC application \"%s\": %s",
+ unit, error->message);
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (message));
+ g_free (message);
+ }
+
+ /* get the current start group */
+ group = g_array_index (starter->start_order, gint, 0);
+
+ /* look up the apps for this group */
+ apps = g_hash_table_lookup (starter->start_groups, GINT_TO_POINTER (group));
+ if (apps != NULL)
+ {
+ /* try to find the current app in the group */
+ for (n = 0; !app_found && n < apps->len; n++)
+ {
+ if (g_strcmp0 (unit, g_ptr_array_index (apps, n)) == 0)
+ app_found = TRUE;
+ }
+
+ /* remove the app from the group */
+ if (app_found)
+ g_ptr_array_remove_index (apps, n-1);
+
+ /* check if this was the last app in the group to be started */
+ if (apps->len == 0)
+ {
+ g_debug ("start group %i finish", group);
+
+ /* remove the group from the groups and the order */
+ g_hash_table_remove (starter->start_groups, GINT_TO_POINTER (group));
+ g_array_remove_index (starter->start_order, 0);
+
+ /* check if we have more groups to start */
+ if (starter->start_order->len > 0)
+ {
+ /* we do, so start the next group now */
+ luc_starter_start_next_group (starter);
+ }
+ else
+ {
+ /* no, we are finished; notify others */
+ g_signal_emit (starter, luc_starter_signals[SIGNAL_LUC_GROUPS_STARTED],
+ 0, NULL);
+ }
+ }
+ }
+
+ /* remove the association between an app and its cancellable, because the app has
+ * finished starting, so cannot be cancelled any more */
+ g_hash_table_remove (starter->cancellables, unit);
+
+ /* release the LUCStarter because the operation is finished */
+ g_object_unref (starter);
+}
+
+
+
+static void
+luc_starter_cancel_start (const gchar *app,
+ GCancellable *cancellable,
+ gpointer user_data)
+{
+ g_cancellable_cancel (cancellable);
+}
+
+
+
+static void
+luc_starter_check_luc_required_finish (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ NSMLifecycleControl *nsm_lifecycle_control = NSM_LIFECYCLE_CONTROL (object);
+ LUCStarter *starter = LUC_STARTER (user_data);
+ gboolean luc_required = TRUE;
+ GError *error = NULL;
+ gchar *log_text;
+
+ g_return_if_fail (IS_NSM_LIFECYCLE_CONTROL (nsm_lifecycle_control));
+ g_return_if_fail (G_IS_ASYNC_RESULT (res));
+ g_return_if_fail (IS_LUC_STARTER (starter));
+
+ /* finish the checking for reloading the LUC */
+ if (!nsm_lifecycle_control_call_check_luc_required_finish (nsm_lifecycle_control,
+ &luc_required, res, &error))
+ {
+ log_text = g_strdup_printf ("Failed checking whether the LUC is required: %s",
+ error->message);
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ g_clear_error (&error);
+
+ DLT_LOG (boot_manager_context, DLT_LOG_INFO,
+ DLT_STRING ("Assuming that we should start the LUC"));
+
+ /* start all the LUC groups now */
+ luc_starter_start_groups_for_real (starter);
+ }
+ else
+ {
+ /* check whether we need to start the LUC or not */
+ if (luc_required)
+ {
+ DLT_LOG (boot_manager_context, DLT_LOG_INFO,
+ DLT_STRING ("LUC is required, starting it now"));
+
+ /* start all the LUC groups now */
+ luc_starter_start_groups_for_real (starter);
+ }
+ else
+ {
+ /* LUC is not required, log this information */
+ DLT_LOG (boot_manager_context, DLT_LOG_INFO,
+ DLT_STRING ("LUC is not required"));
+
+ /* notify others that we have started the LUC groups; we haven't
+ * in this case but the call of luc_starter_start_groups() may
+ * still want to be notified that the call has been processed */
+ g_signal_emit (starter, luc_starter_signals[SIGNAL_LUC_GROUPS_STARTED],
+ 0, NULL);
+ }
+ }
+}
+
+
+
+static void
+luc_starter_start_groups_for_real (LUCStarter *starter)
+{
+ GVariantIter iter;
+ GPtrArray *group_apps;
+ GVariant *context;
+ GError *error = NULL;
+ GList *groups;
+ GList *lp;
+ gchar **apps;
+ gchar *log_text;
+ guint n;
+ gint group;
+ gint type;
+
+ g_return_if_fail (IS_LUC_STARTER (starter));
+
+ /* to load the LUC is required */
+ g_debug ("prioritised types:");
+ for (n = 0; n < starter->prioritised_types->len; n++)
+ g_debug (" %i", g_array_index (starter->prioritised_types, gint, n));
+
+ /* clear the start order */
+ if (starter->start_order->len > 0)
+ g_array_remove_range (starter->start_order, 0, starter->start_order->len);
+
+ /* clear the start groups */
+ g_hash_table_remove_all (starter->start_groups);
+
+ /* clear the mapping between apps and their cancellables */
+ g_hash_table_remove_all (starter->cancellables);
+
+ /* get the current last user context */
+ context = boot_manager_service_read_luc (starter->boot_manager_service, &error);
+ if (error != NULL)
+ {
+ if (error->code == G_IO_ERROR_NOT_FOUND)
+ {
+ DLT_LOG (boot_manager_context, DLT_LOG_INFO,
+ DLT_STRING ("Boot manager could not find the last user context"));
+ }
+ else
+ {
+ log_text = g_strdup_printf ("Error reading last user context: %s",
+ error->message);
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ }
+ g_error_free (error);
+ return;
+ }
+
+ /* create groups for all types in the LUC */
+ g_variant_iter_init (&iter, context);
+ while (g_variant_iter_loop (&iter, "{i^as}", &type, &apps))
+ {
+ group_apps = g_ptr_array_new_with_free_func (g_free);
+
+ for (n = 0; apps != NULL && apps[n] != NULL; n++)
+ g_ptr_array_add (group_apps, g_strdup (apps[n]));
+
+ g_hash_table_insert (starter->start_groups, GINT_TO_POINTER (type), group_apps);
+ }
+
+ /* release the last user context */
+ g_variant_unref (context);
+
+ /* generate the start order by sorting the LUC types according to
+ * the prioritised types */
+ groups = g_hash_table_get_keys (starter->start_groups);
+ for (lp = groups; lp != NULL; lp = lp->next)
+ {
+ group = GPOINTER_TO_INT (lp->data);
+ g_array_append_val (starter->start_order, group);
+ }
+ g_array_sort_with_data (starter->start_order, luc_starter_compare_luc_types, starter);
+
+ g_debug ("start groups (ordered):");
+ for (n = 0; n < starter->start_order->len; n++)
+ g_debug (" %i", g_array_index (starter->start_order, gint, n));
+
+ if (starter->start_order->len > 0)
+ luc_starter_start_next_group (starter);
+}
+
+
+
+LUCStarter *
+luc_starter_new (JobManager *job_manager,
+ BootManagerService *boot_manager_service)
+{
+ g_return_val_if_fail (IS_JOB_MANAGER (job_manager), NULL);
+ g_return_val_if_fail (BOOT_MANAGER_IS_SERVICE (boot_manager_service), NULL);
+
+ return g_object_new (TYPE_LUC_STARTER,
+ "job-manager", job_manager,
+ "boot-manager-service", boot_manager_service,
+ NULL);
+}
+
+
+
+void
+luc_starter_start_groups (LUCStarter *starter)
+{
+ g_return_if_fail (IS_LUC_STARTER (starter));
+
+ /* check whether the NSMLifecycleProxy is available or not */
+ if (starter->nsm_lifecycle_control != NULL)
+ {
+ /* check with NSM whether to load the LUC */
+ nsm_lifecycle_control_call_check_luc_required (starter->nsm_lifecycle_control, NULL,
+ luc_starter_check_luc_required_finish,
+ starter);
+ }
+ else
+ {
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR,
+ DLT_STRING ("NSM unavailable, starting the LUC unconditionally"));
+
+ /* start all the LUC groups now */
+ luc_starter_start_groups_for_real (starter);
+ }
+}
+
+
+
+void
+luc_starter_cancel (LUCStarter *starter)
+{
+ g_hash_table_foreach (starter->cancellables, (GHFunc) luc_starter_cancel_start, NULL);
+}
diff --git a/node-startup-controller/luc-starter.h b/node-startup-controller/luc-starter.h
new file mode 100644
index 0000000..bcb145e
--- /dev/null
+++ b/node-startup-controller/luc-starter.h
@@ -0,0 +1,38 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/* -
+ * Copyright (c) 2012 GENIVI.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef __LUC_STARTER_H__
+#define __LUC_STARTER_H__
+
+#include <node-startup-controller/boot-manager-service.h>
+#include <node-startup-controller/job-manager.h>
+
+G_BEGIN_DECLS
+
+#define TYPE_LUC_STARTER (luc_starter_get_type ())
+#define LUC_STARTER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_LUC_STARTER, LUCStarter))
+#define LUC_STARTER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_LUC_STARTER, LUCStarterClass))
+#define IS_LUC_STARTER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_LUC_STARTER))
+#define IS_LUC_STARTER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_LUC_STARTER))
+#define LUC_STARTER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_LUC_STARTER, LUCStarterClass))
+
+typedef struct _LUCStarterClass LUCStarterClass;
+typedef struct _LUCStarter LUCStarter;
+
+GType luc_starter_get_type (void) G_GNUC_CONST;
+
+LUCStarter *luc_starter_new (JobManager *job_manager,
+ BootManagerService *boot_manager_service) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+void luc_starter_start_groups (LUCStarter *starter);
+void luc_starter_cancel (LUCStarter *starter);
+
+G_END_DECLS
+
+#endif /* !__LUC_STARTER_H__ */
+
diff --git a/node-startup-controller/main.c b/node-startup-controller/main.c
new file mode 100644
index 0000000..348597b
--- /dev/null
+++ b/node-startup-controller/main.c
@@ -0,0 +1,195 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/* -
+ * Copyright (c) 2012 GENIVI.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#include <glib.h>
+#include <gio/gio.h>
+
+#include <dlt/dlt.h>
+
+#include <node-startup-controller/boot-manager-application.h>
+#include <node-startup-controller/boot-manager-dbus.h>
+#include <node-startup-controller/boot-manager-service.h>
+#include <node-startup-controller/la-handler-service.h>
+#include <node-startup-controller/systemd-manager-dbus.h>
+#include <node-startup-controller/target-startup-monitor.h>
+
+
+
+DLT_DECLARE_CONTEXT (boot_manager_context);
+DLT_DECLARE_CONTEXT (la_handler_context);
+
+
+
+static void
+unregister_dlt (void)
+{
+ DLT_UNREGISTER_CONTEXT (boot_manager_context);
+ DLT_UNREGISTER_CONTEXT (la_handler_context);
+ DLT_UNREGISTER_APP ();
+}
+
+
+
+int
+main (int argc,
+ char **argv)
+{
+ BootManagerApplication *application;
+ TargetStartupMonitor *target_startup_monitor;
+ BootManagerService *boot_manager_service;
+ LAHandlerService *la_handler_service;
+ GDBusConnection *connection;
+ SystemdManager *systemd_manager;
+ JobManager *job_manager;
+ GMainLoop *main_loop;
+ GError *error = NULL;
+ gchar *msg;
+
+ /* register the application and context in DLT */
+ DLT_REGISTER_APP ("BMGR", "GENIVI Boot Manager");
+ DLT_REGISTER_CONTEXT (boot_manager_context, "MGR",
+ "Context of the boot manager itself");
+ DLT_REGISTER_CONTEXT (la_handler_context, "LAH",
+ "Context of the legacy application handler that hooks legacy "
+ "applications up with the shutdown concept of the Node State "
+ "Manager");
+
+ /* have DLT unregistered at exit */
+ atexit (unregister_dlt);
+
+ /* initialize the GType type system */
+ g_type_init ();
+
+ /* attempt to connect to D-Bus */
+ connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
+ if (connection == NULL)
+ {
+ msg = g_strdup_printf ("Failed to connect to the system bus: %s", error->message);
+ DLT_LOG (boot_manager_context, DLT_LOG_FATAL, DLT_STRING (msg));
+ g_free (msg);
+
+ /* clean up */
+ g_error_free (error);
+
+ return EXIT_FAILURE;
+ }
+
+ /* attempt to connect to the systemd manager */
+ systemd_manager =
+ systemd_manager_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ "org.freedesktop.systemd1",
+ "/org/freedesktop/systemd1",
+ NULL, &error);
+ if (systemd_manager == NULL)
+ {
+ msg = g_strdup_printf ("Failed to connect to the systemd manager: %s",
+ error->message);
+ DLT_LOG (boot_manager_context, DLT_LOG_FATAL, DLT_STRING (msg));
+ g_free (msg);
+
+ /* clean up */
+ g_error_free (error);
+ g_object_unref (connection);
+
+ return EXIT_FAILURE;
+ }
+
+ /* subscribe to the systemd manager */
+ if (!systemd_manager_call_subscribe_sync (systemd_manager, NULL, &error))
+ {
+ msg = g_strdup_printf ("Failed to subscribe to the systemd manager: %s",
+ error->message);
+ DLT_LOG (boot_manager_context, DLT_LOG_FATAL, DLT_STRING (msg));
+ g_free (msg);
+
+ /* clean up */
+ g_error_free (error);
+ g_object_unref (connection);
+
+ return EXIT_FAILURE;
+ }
+
+ /* instantiate the boot manager service implementation */
+ boot_manager_service = boot_manager_service_new (connection);
+
+ /* attempt to start the boot manager service */
+ if (!boot_manager_service_start_up (boot_manager_service, &error))
+ {
+ msg = g_strdup_printf ("Failed to start the boot manager service: %s",
+ error->message);
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (msg));
+ g_free (msg);
+
+ /* clean up */
+ g_error_free (error);
+ g_object_unref (boot_manager_service);
+ g_object_unref (systemd_manager);
+ g_object_unref (connection);
+
+ return EXIT_FAILURE;
+ }
+
+ /* instantiate the job manager */
+ job_manager = job_manager_new (connection, systemd_manager);
+
+ /* instantiate the legacy app handler */
+ la_handler_service = la_handler_service_new (connection, job_manager);
+
+ /* start the legacy app handler */
+ if (!la_handler_service_start (la_handler_service, &error))
+ {
+ msg = g_strdup_printf ("Failed to start the legacy app handler service: %s",
+ error->message);
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (msg));
+ g_free (msg);
+
+ /* clean up */
+ g_clear_error (&error);
+ g_object_unref (la_handler_service);
+ g_object_unref (job_manager);
+ g_object_unref (boot_manager_service);
+ g_object_unref (systemd_manager);
+ g_object_unref (connection);
+
+ return EXIT_FAILURE;
+ }
+
+ /* create the main loop */
+ main_loop = g_main_loop_new (NULL, FALSE);
+
+ /* create the target startup monitor */
+ target_startup_monitor = target_startup_monitor_new (systemd_manager);
+
+ /* create and run the main application */
+ application = boot_manager_application_new (main_loop, connection, job_manager,
+ la_handler_service, boot_manager_service);
+
+ /* run the main loop */
+ g_main_loop_run (main_loop);
+ g_main_loop_unref (main_loop);
+
+ /* release allocated objects */
+ g_object_unref (application);
+ g_object_unref (target_startup_monitor);
+ g_object_unref (systemd_manager);
+ g_object_unref (job_manager);
+ g_object_unref (boot_manager_service);
+ g_object_unref (connection);
+
+ return EXIT_SUCCESS;
+}
diff --git a/node-startup-controller/org.genivi.BootManager1.service.in b/node-startup-controller/org.genivi.BootManager1.service.in
new file mode 100644
index 0000000..04d1ee7
--- /dev/null
+++ b/node-startup-controller/org.genivi.BootManager1.service.in
@@ -0,0 +1,4 @@
+[D-BUS Service]
+Name=org.genivi.BootManager1
+SystemdService = boot-manager.service
+Exec=@libdir@/boot-manager-@BOOT_MANAGER_VERSION_API@/boot-manager
diff --git a/node-startup-controller/systemd-manager-dbus.xml b/node-startup-controller/systemd-manager-dbus.xml
new file mode 100644
index 0000000..29337e1
--- /dev/null
+++ b/node-startup-controller/systemd-manager-dbus.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/org/freedesktop/systemd1">
+ <interface name="org.freedesktop.systemd1.Manager">
+ <method name="GetUnit">
+ <arg name="name" type="s" direction="in"/>
+ <arg name="unit" type="o" direction="out"/>
+ </method>
+
+ <method name="StartUnit">
+ <arg name="name" type="s" direction="in"/>
+ <arg name="mode" type="s" direction="in"/>
+ <arg name="job" type="o" direction="out"/>
+ </method>
+
+ <method name="StopUnit">
+ <arg name="name" type="s" direction="in"/>
+ <arg name="mode" type="s" direction="in"/>
+ <arg name="job" type="o" direction="out"/>
+ </method>
+
+ <method name="ListJobs">
+ <arg name="jobs" type="a(usssoo)" direction="out"/>
+ </method>
+
+ <method name="Subscribe"/>
+
+ <method name="Unsubscribe"/>
+
+ <signal name="JobRemoved">
+ <arg name="id" type="u"/>
+ <arg name="job" type="o"/>
+ <arg name="unit" type="s"/>
+ <arg name="result" type="s"/>
+ </signal>
+ </interface>
+</node>
diff --git a/node-startup-controller/systemd-unit-dbus.xml b/node-startup-controller/systemd-unit-dbus.xml
new file mode 100644
index 0000000..1461a3b
--- /dev/null
+++ b/node-startup-controller/systemd-unit-dbus.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<node name="/org/freedesktop/systemd1">
+ <interface name="org.freedesktop.systemd1.Unit">
+ <property name="ActiveState" type="s" access="read"/>
+ <property name="Id" type="s" access="read"/>
+ </interface>
+</node>
diff --git a/node-startup-controller/target-startup-monitor.c b/node-startup-controller/target-startup-monitor.c
new file mode 100644
index 0000000..722ab56
--- /dev/null
+++ b/node-startup-controller/target-startup-monitor.c
@@ -0,0 +1,457 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/* -
+ * Copyright (c) 2012 GENIVI.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <glib-object.h>
+#include <gio/gio.h>
+
+#include <dlt/dlt.h>
+
+#include <common/nsm-enum-types.h>
+#include <common/nsm-lifecycle-control-dbus.h>
+
+#include <node-startup-controller/boot-manager-dbus.h>
+#include <node-startup-controller/target-startup-monitor.h>
+#include <node-startup-controller/systemd-unit-dbus.h>
+
+
+
+DLT_IMPORT_CONTEXT (boot_manager_context);
+
+
+
+/* property identifier */
+enum
+{
+ PROP_0,
+ PROP_SYSTEMD_MANAGER,
+};
+
+
+
+typedef struct _GetUnitData GetUnitData;
+
+
+
+static void target_startup_monitor_finalize (GObject *object);
+static void target_startup_monitor_constructed (GObject *object);
+static void target_startup_monitor_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec);
+static void target_startup_monitor_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec);
+static void target_startup_monitor_job_removed (SystemdManager *manager,
+ guint id,
+ const gchar *job_name,
+ const gchar *unit,
+ const gchar *result,
+ TargetStartupMonitor *monitor);
+static void target_startup_monitor_get_unit_finish (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data);
+static void target_startup_monitor_unit_proxy_new_finish (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data);
+static void target_startup_monitor_set_node_state (TargetStartupMonitor *monitor,
+ NSMNodeState state);
+static void target_startup_monitor_set_node_state_finish (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data);
+
+
+
+struct _TargetStartupMonitorClass
+{
+ GObjectClass __parent__;
+};
+
+struct _TargetStartupMonitor
+{
+ GObject __parent__;
+
+ SystemdManager *systemd_manager;
+
+ NSMLifecycleControl *nsm_lifecycle_control;
+
+ /* list of systemd units for the targets we are interested in */
+ GList *units;
+
+ /* map of systemd target names to corresponding node states */
+ GHashTable *targets_to_states;
+};
+
+struct _GetUnitData
+{
+ TargetStartupMonitor *monitor;
+ gchar *unit_name;
+ gchar *object_path;
+};
+
+
+
+G_DEFINE_TYPE (TargetStartupMonitor, target_startup_monitor, G_TYPE_OBJECT);
+
+
+
+static void
+target_startup_monitor_class_init (TargetStartupMonitorClass *klass)
+{
+ GObjectClass *gobject_class;
+
+ gobject_class = G_OBJECT_CLASS (klass);
+ gobject_class->finalize = target_startup_monitor_finalize;
+ gobject_class->constructed = target_startup_monitor_constructed;
+ gobject_class->get_property = target_startup_monitor_get_property;
+ gobject_class->set_property = target_startup_monitor_set_property;
+
+ g_object_class_install_property (gobject_class,
+ PROP_SYSTEMD_MANAGER,
+ g_param_spec_object ("systemd-manager",
+ "systemd-manager",
+ "systemd-manager",
+ TYPE_SYSTEMD_MANAGER,
+ G_PARAM_READWRITE |
+ G_PARAM_CONSTRUCT_ONLY |
+ G_PARAM_STATIC_STRINGS));
+}
+
+
+
+static void
+target_startup_monitor_init (TargetStartupMonitor *monitor)
+{
+ GError *error = NULL;
+ gchar *log_text;
+
+ /* create proxy to talk to the Node State Manager's lifecycle control */
+ monitor->nsm_lifecycle_control =
+ nsm_lifecycle_control_proxy_new_for_bus_sync (G_BUS_TYPE_SYSTEM,
+ G_DBUS_PROXY_FLAGS_NONE,
+ "com.contiautomotive.NodeStateManager",
+ "/com/contiautomotive/NodeStateManager/LifecycleControl",
+ NULL, &error);
+ if (error != NULL)
+ {
+ log_text = g_strdup_printf ("Failed to connect to the NSM lifecycle control: %s",
+ error->message);
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ g_error_free (error);
+ }
+
+ /* set the initial state to base running, which means that
+ * the mandatory.target has been started (this is done before the
+ * boot manager itself is brought up) */
+ target_startup_monitor_set_node_state (monitor, NSM_NODE_STATE_BASE_RUNNING);
+
+ /* create the table of targets and their node states */
+ monitor->targets_to_states = g_hash_table_new (g_str_hash, g_str_equal);
+ g_hash_table_insert (monitor->targets_to_states, "focussed.target",
+ GUINT_TO_POINTER (NSM_NODE_STATE_LUC_RUNNING));
+ g_hash_table_insert (monitor->targets_to_states, "unfocussed.target",
+ GUINT_TO_POINTER (NSM_NODE_STATE_FULLY_RUNNING));
+ g_hash_table_insert (monitor->targets_to_states, "lazy.target",
+ GUINT_TO_POINTER (NSM_NODE_STATE_FULLY_OPERATIONAL));
+}
+
+
+
+static void
+target_startup_monitor_constructed (GObject *object)
+{
+ TargetStartupMonitor *monitor = TARGET_STARTUP_MONITOR (object);
+
+ g_signal_connect (monitor->systemd_manager, "job-removed",
+ G_CALLBACK (target_startup_monitor_job_removed), monitor);
+}
+
+
+
+static void
+target_startup_monitor_finalize (GObject *object)
+{
+ TargetStartupMonitor *monitor = TARGET_STARTUP_MONITOR (object);
+ GList *lp;
+
+ /* disconnect from all the unit proxies and release them */
+ for (lp = monitor->units; lp != NULL; lp = lp->next)
+ {
+ g_signal_handlers_disconnect_matched (lp->data, G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, monitor);
+ g_object_unref (lp->data);
+ }
+ g_list_free (lp->data);
+
+ /* release the mapping of systemd targets to node states */
+ g_hash_table_destroy (monitor->targets_to_states);
+
+ /* release the systemd manager */
+ g_signal_handlers_disconnect_matched (monitor->systemd_manager,
+ G_SIGNAL_MATCH_DATA,
+ 0, 0, NULL, NULL, monitor);
+ g_object_unref (monitor->systemd_manager);
+
+ if (monitor->nsm_lifecycle_control != NULL)
+ g_object_unref (monitor->nsm_lifecycle_control);
+
+ (*G_OBJECT_CLASS (target_startup_monitor_parent_class)->finalize) (object);
+}
+
+
+
+static void
+target_startup_monitor_get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ TargetStartupMonitor *monitor = TARGET_STARTUP_MONITOR (object);
+
+ switch (prop_id)
+ {
+ case PROP_SYSTEMD_MANAGER:
+ g_value_set_object (value, monitor->systemd_manager);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+
+static void
+target_startup_monitor_set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ TargetStartupMonitor *monitor = TARGET_STARTUP_MONITOR (object);
+
+ switch (prop_id)
+ {
+ case PROP_SYSTEMD_MANAGER:
+ monitor->systemd_manager = g_value_dup_object (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+
+
+static void
+target_startup_monitor_job_removed (SystemdManager *manager,
+ guint id,
+ const gchar *job_name,
+ const gchar *unit,
+ const gchar *result,
+ TargetStartupMonitor *monitor)
+{
+ GetUnitData *data;
+
+ g_return_if_fail (IS_SYSTEMD_MANAGER (manager));
+ g_return_if_fail (job_name != NULL && *job_name != '\0');
+ g_return_if_fail (unit != NULL && *unit != '\0');
+ g_return_if_fail (result != NULL && *result != '\0');
+ g_return_if_fail (IS_TARGET_STARTUP_MONITOR (monitor));
+
+ /* create a temporary struct to bundle information about the unit */
+ data = g_slice_new0 (GetUnitData);
+ data->monitor = g_object_ref (monitor);
+ data->unit_name = g_strdup (unit);
+
+ /* ask systemd to return the object path for this unit */
+ systemd_manager_call_get_unit (monitor->systemd_manager, unit, NULL,
+ target_startup_monitor_get_unit_finish, data);
+
+
+}
+
+
+
+static void
+target_startup_monitor_get_unit_finish (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GetUnitData *data = user_data;
+ GError *error = NULL;
+ gchar *message;
+ gchar *object_path;
+
+ g_return_if_fail (IS_SYSTEMD_MANAGER (object));
+ g_return_if_fail (G_IS_ASYNC_RESULT (res));
+ g_return_if_fail (data != NULL);
+
+ /* finish obtaining the object path for the unit from systemd */
+ if (!systemd_manager_call_get_unit_finish (SYSTEMD_MANAGER (object), &object_path,
+ res, &error))
+ {
+ /* there was an error, log it */
+ message = g_strdup_printf ("Failed to get unit \"%s\" from systemd: %s",
+ data->unit_name, error->message);
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (message));
+ g_free (message);
+ g_error_free (error);
+
+ /* release the get unit data */
+ g_object_unref (data->monitor);
+ g_free (data->unit_name);
+ g_slice_free (GetUnitData, data);
+ }
+ else
+ {
+ message = g_strdup_printf ("Creating D-Bus proxy for unit \"%s\"", object_path);
+ DLT_LOG (boot_manager_context, DLT_LOG_INFO, DLT_STRING (message));
+ g_free (message);
+
+ /* remember the object path */
+ data->object_path = object_path;
+
+ /* create a proxy for this unit D-Bus object */
+ systemd_unit_proxy_new (g_dbus_proxy_get_connection (G_DBUS_PROXY (data->monitor->systemd_manager)),
+ G_DBUS_PROXY_FLAGS_NONE,
+ "org.freedesktop.systemd1",
+ object_path,
+ NULL,
+ target_startup_monitor_unit_proxy_new_finish,
+ data);
+ }
+}
+
+
+
+static void
+target_startup_monitor_unit_proxy_new_finish (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ GetUnitData *data = user_data;
+ SystemdUnit *unit;
+ const gchar *state;
+ gpointer node_state;
+ GError *error = NULL;
+ gchar *message;
+
+ g_return_if_fail (G_IS_ASYNC_RESULT (res));
+ g_return_if_fail (data != NULL);
+
+ /* finish creating the proxy for this systemd unit */
+ unit = systemd_unit_proxy_new_finish (res, &error);
+ if (error != NULL)
+ {
+ /* there was an error, log it */
+ message = g_strdup_printf ("Failed to create a D-Bus proxy for unit \"%s\": %s",
+ data->object_path, error->message);
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (message));
+ g_error_free (error);
+ }
+ else
+ {
+ /* query the unit for its current active state */
+ state = systemd_unit_get_active_state (unit);
+
+ /* log the the active state has changed */
+ message = g_strdup_printf ("Active state of unit \"%s\" changed to %s",
+ data->unit_name, state);
+ DLT_LOG (boot_manager_context, DLT_LOG_INFO, DLT_STRING (message));
+ g_free (message);
+
+ /* check if the new state is active */
+ if (g_strcmp0 (state, "active") == 0)
+ {
+ /* look up the node state corresponding to this unit, if there is one */
+ if (g_hash_table_lookup_extended (data->monitor->targets_to_states,
+ data->unit_name, NULL, &node_state))
+ {
+ /* we do have a state for this unit, so apply it now */
+ target_startup_monitor_set_node_state (data->monitor,
+ GPOINTER_TO_UINT (node_state));
+ }
+ }
+
+ /* release the unit proxy */
+ g_object_unref (unit);
+ }
+
+ /* free the get unit data */
+ g_object_unref (data->monitor);
+ g_free (data->unit_name);
+ g_free (data->object_path);
+ g_slice_free (GetUnitData, data);
+}
+
+
+
+static void
+target_startup_monitor_set_node_state (TargetStartupMonitor *monitor,
+ NSMNodeState state)
+{
+ g_return_if_fail (IS_TARGET_STARTUP_MONITOR (monitor));
+
+ /* set node state in the Node State Manager */
+ nsm_lifecycle_control_call_set_node_state (monitor->nsm_lifecycle_control,
+ (gint) state, NULL,
+ target_startup_monitor_set_node_state_finish,
+ NULL);
+}
+
+
+
+static void
+target_startup_monitor_set_node_state_finish (GObject *object,
+ GAsyncResult *res,
+ gpointer user_data)
+{
+ NSMLifecycleControl *nsm_lifecycle_control = NSM_LIFECYCLE_CONTROL (object);
+ GError *error = NULL;
+ gchar *log_text;
+ NSMErrorStatus error_code = NSM_ERROR_STATUS_OK;
+
+ g_return_if_fail (IS_NSM_LIFECYCLE_CONTROL (nsm_lifecycle_control));
+ g_return_if_fail (G_IS_ASYNC_RESULT (res));
+
+ /* finish setting the node state in the NSM */
+ if (!nsm_lifecycle_control_call_set_node_state_finish (nsm_lifecycle_control,
+ (gint *) &error_code, res,
+ &error))
+ {
+ log_text = g_strdup_printf ("Failed to set the node state: %s", error->message);
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ g_error_free (error);
+ }
+ else if (error_code != NSM_ERROR_STATUS_OK)
+ {
+ log_text = g_strdup_printf ("Failed to set the node state: error code %d",
+ error_code);
+ DLT_LOG (boot_manager_context, DLT_LOG_ERROR, DLT_STRING (log_text));
+ g_free (log_text);
+ }
+}
+
+
+
+TargetStartupMonitor *
+target_startup_monitor_new (SystemdManager *systemd_manager)
+{
+ g_return_val_if_fail (IS_SYSTEMD_MANAGER (systemd_manager), NULL);
+
+ return g_object_new (TYPE_TARGET_STARTUP_MONITOR,
+ "systemd-manager", systemd_manager,
+ NULL);
+}
diff --git a/node-startup-controller/target-startup-monitor.h b/node-startup-controller/target-startup-monitor.h
new file mode 100644
index 0000000..b16eb21
--- /dev/null
+++ b/node-startup-controller/target-startup-monitor.h
@@ -0,0 +1,32 @@
+/* vi:set et ai sw=2 sts=2 ts=2: */
+/* -
+ * Copyright (c) 2012 GENIVI.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#ifndef __TARGET_STARTUP_MONITOR_H__
+#define __TARGET_STARTUP_MONITOR_H__
+
+#include <node-startup-controller/systemd-manager-dbus.h>
+
+G_BEGIN_DECLS
+
+#define TYPE_TARGET_STARTUP_MONITOR (target_startup_monitor_get_type ())
+#define TARGET_STARTUP_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), TYPE_TARGET_STARTUP_MONITOR, TargetStartupMonitor))
+#define TARGET_STARTUP_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), TYPE_TARGET_STARTUP_MONITOR, TargetStartupMonitorClass))
+#define IS_TARGET_STARTUP_MONITOR(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), TYPE_TARGET_STARTUP_MONITOR))
+#define IS_TARGET_STARTUP_MONITOR_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), TYPE_TARGET_STARTUP_MONITOR))
+#define TARGET_STARTUP_MONITOR_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), TYPE_TARGET_STARTUP_MONITOR, TargetStartupMonitorClass))
+
+typedef struct _TargetStartupMonitor TargetStartupMonitor;
+typedef struct _TargetStartupMonitorClass TargetStartupMonitorClass;
+
+GType target_startup_monitor_get_type (void) G_GNUC_CONST;
+TargetStartupMonitor *target_startup_monitor_new (SystemdManager *systemd_manager) G_GNUC_MALLOC G_GNUC_WARN_UNUSED_RESULT;
+
+G_END_DECLS
+
+#endif /* !__TARGET_STARTUP_MONITOR_H__ */