1 /* vi:set et ai sw=2 sts=2 ts=2: */
3 * Copyright (c) 2012 GENIVI.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
14 #include <glib-object.h>
17 #include <boot-manager/boot-manager-dbus.h>
18 #include <boot-manager/boot-manager-service.h>
22 typedef struct _BootManagerServiceJob BootManagerServiceJob;
26 /* property identifiers */
36 static void boot_manager_service_finalize (GObject *object);
37 static void boot_manager_service_constructed (GObject *object);
38 static void boot_manager_service_get_property (GObject *object,
42 static void boot_manager_service_set_property (GObject *object,
46 static gboolean boot_manager_service_handle_start (BootManager *interface,
47 GDBusMethodInvocation *invocation,
49 BootManagerService *service);
50 static void boot_manager_service_handle_start_finish (BootManagerService *service,
55 static void boot_manager_service_start_unit_reply (GObject *object,
58 static gboolean boot_manager_service_handle_stop (BootManager *interface,
59 GDBusMethodInvocation *invocation,
61 BootManagerService *service);
62 static void boot_manager_service_handle_stop_finish (BootManagerService *service,
67 static void boot_manager_service_stop_unit_reply (GObject *object,
70 static gboolean boot_manager_service_handle_kill (BootManager *interface,
71 GDBusMethodInvocation *invocation,
73 BootManagerService *service);
74 static void boot_manager_service_handle_kill_finish (BootManagerService *service,
79 static void boot_manager_service_kill_unit_reply (GObject *object,
82 static gboolean boot_manager_service_handle_restart (BootManager *interface,
83 GDBusMethodInvocation *invocation,
85 BootManagerService *service);
86 static void boot_manager_service_handle_restart_finish (BootManagerService *service,
91 static void boot_manager_service_restart_unit_reply (GObject *object,
94 static void boot_manager_service_job_removed (SystemdManager *manager,
96 const gchar *job_name,
98 BootManagerService *service);
99 static BootManagerServiceJob *boot_manager_service_job_new (BootManagerService *service,
101 GCancellable *cancellable,
102 BootManagerServiceCallback callback,
104 static void boot_manager_service_job_unref (BootManagerServiceJob *job);
105 static void boot_manager_service_remember_job (BootManagerService *service,
106 const gchar *job_name,
107 BootManagerServiceJob *job);
108 static void boot_manager_service_forget_job (BootManagerService *service,
109 const gchar *job_name);
113 struct _BootManagerServiceClass
115 GObjectClass __parent__;
118 struct _BootManagerService
122 GDBusConnection *connection;
123 BootManager *interface;
124 SystemdManager *systemd_manager;
129 struct _BootManagerServiceJob
131 BootManagerService *service;
133 GCancellable *cancellable;
134 BootManagerServiceCallback callback;
141 G_DEFINE_TYPE (BootManagerService, boot_manager_service, G_TYPE_OBJECT);
146 boot_manager_service_class_init (BootManagerServiceClass *klass)
148 GObjectClass *gobject_class;
150 gobject_class = G_OBJECT_CLASS (klass);
151 gobject_class->finalize = boot_manager_service_finalize;
152 gobject_class->constructed = boot_manager_service_constructed;
153 gobject_class->get_property = boot_manager_service_get_property;
154 gobject_class->set_property = boot_manager_service_set_property;
156 g_object_class_install_property (gobject_class,
158 g_param_spec_object ("connection",
161 G_TYPE_DBUS_CONNECTION,
163 G_PARAM_CONSTRUCT_ONLY |
164 G_PARAM_STATIC_STRINGS));
166 g_object_class_install_property (gobject_class,
167 PROP_SYSTEMD_MANAGER,
168 g_param_spec_object ("systemd-manager",
171 TYPE_SYSTEMD_MANAGER,
173 G_PARAM_CONSTRUCT_ONLY |
174 G_PARAM_STATIC_STRINGS));
180 boot_manager_service_init (BootManagerService *service)
182 /* create a mapping of systemd job names to job objects; we will use this
183 * to remember jobs that we started */
184 service->jobs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
185 (GDestroyNotify) boot_manager_service_job_unref);
187 service->interface = boot_manager_skeleton_new ();
189 /* implement the Start() method handler */
190 g_signal_connect (service->interface, "handle-start",
191 G_CALLBACK (boot_manager_service_handle_start), service);
193 /* implement the Stop() method handler */
194 g_signal_connect (service->interface, "handle-stop",
195 G_CALLBACK (boot_manager_service_handle_stop), service);
197 /* implement the Kill() method handler */
198 g_signal_connect (service->interface, "handle-kill",
199 G_CALLBACK (boot_manager_service_handle_kill), service);
200 /* implement the Restart() method handler */
201 g_signal_connect (service->interface, "handle-restart",
202 G_CALLBACK (boot_manager_service_handle_restart), service);
208 boot_manager_service_finalize (GObject *object)
210 BootManagerService *service = BOOT_MANAGER_SERVICE (object);
212 /* release all the jobs we have remembered */
213 g_hash_table_unref (service->jobs);
215 /* release the D-Bus connection object */
216 g_object_unref (service->connection);
218 /* release the systemd manager */
219 g_signal_handlers_disconnect_matched (service->systemd_manager,
221 0, 0, NULL, NULL, service);
222 g_object_unref (service->systemd_manager);
224 /* release the interface skeleton */
225 g_signal_handlers_disconnect_matched (service->interface,
227 0, 0, NULL, NULL, service);
228 g_object_unref (service->interface);
230 (*G_OBJECT_CLASS (boot_manager_service_parent_class)->finalize) (object);
236 boot_manager_service_constructed (GObject *object)
238 BootManagerService *service = BOOT_MANAGER_SERVICE (object);
240 /* connect to systemd's "JobRemoved" signal so that we are notified
241 * whenever a job is finished */
242 g_signal_connect (service->systemd_manager, "job-removed",
243 G_CALLBACK (boot_manager_service_job_removed), service);
249 boot_manager_service_get_property (GObject *object,
254 BootManagerService *service = BOOT_MANAGER_SERVICE (object);
258 case PROP_CONNECTION:
259 g_value_set_object (value, service->connection);
261 case PROP_SYSTEMD_MANAGER:
262 g_value_set_object (value, service->systemd_manager);
265 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
273 boot_manager_service_set_property (GObject *object,
278 BootManagerService *service = BOOT_MANAGER_SERVICE (object);
282 case PROP_CONNECTION:
283 service->connection = g_value_dup_object (value);
285 case PROP_SYSTEMD_MANAGER:
286 service->systemd_manager = g_value_dup_object (value);
289 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
297 boot_manager_service_handle_start (BootManager *interface,
298 GDBusMethodInvocation *invocation,
300 BootManagerService *service)
302 g_return_val_if_fail (IS_BOOT_MANAGER (interface), FALSE);
303 g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);
304 g_return_val_if_fail (unit != NULL, FALSE);
305 g_return_val_if_fail (BOOT_MANAGER_IS_SERVICE (service), FALSE);
307 /* ask systemd to start the unit for us, send a D-Bus reply in the finish callback */
308 boot_manager_service_start (service, unit, NULL,
309 boot_manager_service_handle_start_finish, invocation);
317 boot_manager_service_handle_start_finish (BootManagerService *service,
323 GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (user_data);
325 g_return_if_fail (BOOT_MANAGER_IS_SERVICE (service));
326 g_return_if_fail (unit != NULL);
327 g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation));
329 /* log any potential errors */
331 g_warning ("there was an error: %s", error->message);
333 /* report the result back to the boot manager client */
334 boot_manager_complete_start (service->interface, invocation, result);
340 boot_manager_service_start_unit_reply (GObject *object,
341 GAsyncResult *result,
344 BootManagerServiceJob *job = user_data;
345 GError *error = NULL;
346 gchar *job_name = NULL;
348 g_return_if_fail (IS_SYSTEMD_MANAGER (object));
349 g_return_if_fail (G_IS_ASYNC_RESULT (result));
350 g_return_if_fail (user_data != NULL);
352 /* finish the start unit call */
353 if (!systemd_manager_call_start_unit_finish (job->service->systemd_manager,
354 &job_name, result, &error))
356 /* there was an error; let the caller know */
357 job->callback (job->service, job->unit, "failed", error, job->user_data);
358 g_error_free (error);
361 /* finish the job immediately */
362 boot_manager_service_job_unref (job);
366 /* remember the job so that we can finish it in the "job-removed" signal
367 * handler. the service takes ownership of the job so we don't need to
369 boot_manager_service_remember_job (job->service, job_name, job);
376 boot_manager_service_handle_stop (BootManager *interface,
377 GDBusMethodInvocation *invocation,
379 BootManagerService *service)
381 g_return_val_if_fail (IS_BOOT_MANAGER (interface), FALSE);
382 g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);
383 g_return_val_if_fail (unit != NULL, FALSE);
384 g_return_val_if_fail (BOOT_MANAGER_IS_SERVICE (service), FALSE);
386 /* ask systemd to stop the unit for us, send a D-Bus reply in the finish callback */
387 boot_manager_service_stop (service, unit, NULL,
388 boot_manager_service_handle_stop_finish, invocation);
396 boot_manager_service_handle_stop_finish (BootManagerService *service,
402 GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (user_data);
404 g_return_if_fail (BOOT_MANAGER_IS_SERVICE (service));
405 g_return_if_fail (unit != NULL);
406 g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation));
408 /* log any potential errors */
410 g_warning ("there was an error: %s", error->message);
412 /* report the result back to the boot manager client */
413 boot_manager_complete_stop (service->interface, invocation, result);
419 boot_manager_service_stop_unit_reply (GObject *object,
420 GAsyncResult *result,
423 BootManagerServiceJob *job = user_data;
424 GError *error = NULL;
425 gchar *job_name = NULL;
427 g_return_if_fail (IS_SYSTEMD_MANAGER (object));
428 g_return_if_fail (G_IS_ASYNC_RESULT (result));
429 g_return_if_fail (user_data != NULL);
431 /* finish the stop unit call */
432 if (!systemd_manager_call_stop_unit_finish (job->service->systemd_manager,
433 &job_name, result, &error))
435 /* there was an error; let the caller know */
436 job->callback (job->service, job->unit, "failed", error, job->user_data);
437 g_error_free (error);
440 /* finish the job immediately */
441 boot_manager_service_job_unref (job);
445 /* remember the job so that we can finish it in the "job-removed" signal
446 * handler. the service takes ownership of the job so we don't need to
448 boot_manager_service_remember_job (job->service, job_name, job);
455 boot_manager_service_handle_kill (BootManager *interface,
456 GDBusMethodInvocation *invocation,
458 BootManagerService *service)
460 g_return_val_if_fail (IS_BOOT_MANAGER (interface), FALSE);
461 g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);
462 g_return_val_if_fail (unit != NULL, FALSE);
463 g_return_val_if_fail (BOOT_MANAGER_IS_SERVICE (service), FALSE);
465 /* ask systemd to kill the unit, send a D-Bus reply in the finish callback */
466 boot_manager_service_kill (service, unit, NULL, boot_manager_service_handle_kill_finish,
475 boot_manager_service_handle_kill_finish (BootManagerService *service,
481 GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (user_data);
483 g_return_if_fail (BOOT_MANAGER_IS_SERVICE (service));
484 g_return_if_fail (unit!= NULL);
485 g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation));
487 /* log any potential errors */
489 g_warning ("there was an error: %s", error->message);
491 /* report the result back to the boot manager client */
492 boot_manager_complete_kill (service->interface, invocation, result);
498 boot_manager_service_kill_unit_reply (GObject *object,
499 GAsyncResult *result,
502 BootManagerServiceJob *job = user_data;
503 GError *error = NULL;
505 g_return_if_fail (IS_SYSTEMD_MANAGER (object));
506 g_return_if_fail (G_IS_ASYNC_RESULT (result));
507 g_return_if_fail (user_data != NULL);
509 /* finish the kill unit call */
510 systemd_manager_call_kill_unit_finish (job->service->systemd_manager,
513 /* got a reply from systemd; let the caller know we're done */
514 job->callback (job->service, job->unit, (error == NULL) ? "done" : "failed", error,
517 g_error_free (error);
520 boot_manager_service_job_unref (job);
526 boot_manager_service_handle_restart (BootManager *interface,
527 GDBusMethodInvocation *invocation,
529 BootManagerService *service)
531 g_return_val_if_fail (IS_BOOT_MANAGER (interface), FALSE);
532 g_return_val_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation), FALSE);
533 g_return_val_if_fail (unit != NULL, FALSE);
534 g_return_val_if_fail (BOOT_MANAGER_IS_SERVICE (service), FALSE);
536 /* ask systemd to restart the unit for us, send a D-Bus reply in the finish callback */
537 boot_manager_service_restart (service, unit, NULL,
538 boot_manager_service_handle_restart_finish, invocation);
546 boot_manager_service_handle_restart_finish (BootManagerService *service,
552 GDBusMethodInvocation *invocation = G_DBUS_METHOD_INVOCATION (user_data);
554 g_return_if_fail (BOOT_MANAGER_IS_SERVICE (service));
555 g_return_if_fail (unit != NULL);
556 g_return_if_fail (G_IS_DBUS_METHOD_INVOCATION (invocation));
558 /* log any potential errors */
560 g_warning ("there was an error: %s", error->message);
562 /* report the result back to the boot manager client */
563 boot_manager_complete_restart (service->interface, invocation, result);
569 boot_manager_service_restart_unit_reply (GObject *object,
570 GAsyncResult *result,
573 BootManagerServiceJob *job = user_data;
574 GError *error = NULL;
575 gchar *job_name = NULL;
577 g_return_if_fail (IS_SYSTEMD_MANAGER (object));
578 g_return_if_fail (G_IS_ASYNC_RESULT (result));
579 g_return_if_fail (user_data != NULL);
581 /* finish the restart unit call */
582 if (!systemd_manager_call_restart_unit_finish (job->service->systemd_manager,
583 &job_name, result, &error))
585 /* there was an error; let the caller know */
586 job->callback (job->service, job->unit, "failed", error, job->user_data);
587 g_error_free (error);
590 /* finish the job immediately */
591 boot_manager_service_job_unref (job);
595 /* remember the job so that we can finish it in the "job-removed" signal
596 * handler. the service takes ownership of the job so we don't need to
598 boot_manager_service_remember_job (job->service, job_name, job);
605 boot_manager_service_job_removed (SystemdManager *manager,
607 const gchar *job_name,
609 BootManagerService *service)
611 BootManagerServiceJob *job;
613 g_return_if_fail (IS_SYSTEMD_MANAGER (manager));
614 g_return_if_fail (job_name != NULL && *job_name != '\0');
615 g_return_if_fail (result != NULL && *result != '\0');
616 g_return_if_fail (BOOT_MANAGER_IS_SERVICE (service));
618 /* lookup the rememebred job for this job name */
619 job = g_hash_table_lookup (service->jobs, job_name);
621 /* if there is no such job, just ignore this job-removed signal */
625 /* finish the job by notifying the caller */
626 job->callback (service, job->unit, result, NULL, job->user_data);
628 /* forget about this job; this will unref the job */
629 boot_manager_service_forget_job (service, job_name);
634 static BootManagerServiceJob *
635 boot_manager_service_job_new (BootManagerService *service,
637 GCancellable *cancellable,
638 BootManagerServiceCallback callback,
641 BootManagerServiceJob *job;
643 g_return_val_if_fail (BOOT_MANAGER_IS_SERVICE (service), NULL);
644 g_return_val_if_fail (unit != NULL, NULL);
645 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
647 /* allocate a new job struct */
648 job = g_slice_new0 (BootManagerServiceJob);
649 job->service = g_object_ref (service);
650 job->unit = g_strdup (unit);
651 if (cancellable != NULL)
652 job->cancellable = g_object_ref (cancellable);
653 job->callback = callback;
654 job->user_data = user_data;
662 boot_manager_service_job_unref (BootManagerServiceJob *job)
667 /* release all memory and references held by the job */
668 if (job->cancellable != NULL)
669 g_object_unref (job->cancellable);
671 g_object_unref (job->service);
672 g_slice_free (BootManagerServiceJob, job);
678 boot_manager_service_remember_job (BootManagerService *service,
679 const gchar *job_name,
680 BootManagerServiceJob *job)
682 BootManagerServiceJob *existing_job;
684 g_return_if_fail (BOOT_MANAGER_IS_SERVICE (service));
685 g_return_if_fail (job_name != NULL && *job_name != '\0');
686 g_return_if_fail (job != NULL);
688 /* if the job is already being remembered, then there is a programming
689 * mistake and we should make people aware of it */
690 existing_job = g_hash_table_lookup (service->jobs, job_name);
691 if (existing_job != NULL)
693 g_critical ("trying to remember the same job twice");
697 /* associate the job name with the job */
698 g_hash_table_insert (service->jobs, g_strdup (job_name), job);
704 boot_manager_service_forget_job (BootManagerService *service,
705 const gchar *job_name)
707 g_return_if_fail (BOOT_MANAGER_IS_SERVICE (service));
708 g_return_if_fail (job_name != NULL && *job_name != '\0');
710 g_hash_table_remove (service->jobs, job_name);
716 boot_manager_service_new (GDBusConnection *connection,
717 SystemdManager *systemd_manager)
719 g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
720 g_return_val_if_fail (IS_SYSTEMD_MANAGER (systemd_manager), NULL);
722 return g_object_new (BOOT_MANAGER_TYPE_SERVICE,
723 "connection", connection,
724 "systemd-manager", systemd_manager,
731 boot_manager_service_start_up (BootManagerService *service,
734 g_return_val_if_fail (BOOT_MANAGER_IS_SERVICE (service), FALSE);
735 g_return_val_if_fail (error == NULL || *error == NULL, FALSE);
737 /* announce the org.genivi.BootManager1 service on the bus */
738 return g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (service->interface),
740 "/org/genivi/BootManager1",
747 boot_manager_service_start (BootManagerService *service,
749 GCancellable *cancellable,
750 BootManagerServiceCallback callback,
753 BootManagerServiceJob *job;
755 g_return_if_fail (BOOT_MANAGER_IS_SERVICE (service));
756 g_return_if_fail (unit != NULL);
757 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
758 g_return_if_fail (callback != NULL);
760 /* create a new job object */
761 job = boot_manager_service_job_new (service, unit, cancellable, callback, user_data);
763 /* ask systemd to start the unit asynchronously */
764 systemd_manager_call_start_unit (service->systemd_manager, unit, "fail", cancellable,
765 boot_manager_service_start_unit_reply, job);
771 boot_manager_service_stop (BootManagerService *service,
773 GCancellable *cancellable,
774 BootManagerServiceCallback callback,
777 BootManagerServiceJob *job;
779 g_return_if_fail (BOOT_MANAGER_IS_SERVICE (service));
780 g_return_if_fail (unit != NULL);
781 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
782 g_return_if_fail (callback != NULL);
784 /* create a new job object */
785 job = boot_manager_service_job_new (service, unit, cancellable, callback, user_data);
787 /* ask systemd to stop the unit asynchronously */
788 systemd_manager_call_stop_unit (service->systemd_manager, unit, "fail", cancellable,
789 boot_manager_service_stop_unit_reply, job);
795 boot_manager_service_kill (BootManagerService *service,
797 GCancellable *cancellable,
798 BootManagerServiceCallback callback,
801 BootManagerServiceJob *job;
803 g_return_if_fail (BOOT_MANAGER_IS_SERVICE (service));
804 g_return_if_fail (unit != NULL);
805 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
806 g_return_if_fail (callback != NULL);
808 /* create a new job object */
809 job = boot_manager_service_job_new (service, unit, cancellable, callback, user_data);
811 /* ask systemd to stop the unit asynchronously */
812 systemd_manager_call_kill_unit (service->systemd_manager, unit, "all", "control-group",
813 SIGKILL, cancellable,
814 boot_manager_service_kill_unit_reply, job);
820 boot_manager_service_restart (BootManagerService *service,
822 GCancellable *cancellable,
823 BootManagerServiceCallback callback,
826 BootManagerServiceJob *job;
828 g_return_if_fail (BOOT_MANAGER_IS_SERVICE (service));
829 g_return_if_fail (unit != NULL);
830 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
831 g_return_if_fail (callback != NULL);
833 /* create a new job object */
834 job = boot_manager_service_job_new (service, unit, cancellable, callback, user_data);
836 /* ask systemd to restart the unit asynchronously */
837 systemd_manager_call_restart_unit (service->systemd_manager, unit, "fail", cancellable,
838 boot_manager_service_restart_unit_reply, job);