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 <node-startup-controller/job-manager.h>
18 #include <node-startup-controller/systemd-manager-dbus.h>
23 * SECTION: job-manager
25 * @short_description: Manages systemd jobs.
26 * @stability: Internal
28 * The Job Manager simplifies starting and stopping systemd units by handling all the
29 * JobRemoved signals internally, so units can be started and stopped using
30 * job_manager_start() and job_manager_stop().
35 typedef struct _JobManagerJob JobManagerJob;
39 /* property identifiers */
49 static void job_manager_constructed (GObject *object);
50 static void job_manager_finalize (GObject *object);
51 static void job_manager_get_property (GObject *object,
55 static void job_manager_set_property (GObject *object,
59 static void job_manager_start_unit_reply (GObject *object,
62 static void job_manager_stop_unit_reply (GObject *object,
65 static void job_manager_job_removed (SystemdManager *systemd_manager,
67 const gchar *job_name,
70 JobManager *job_manager);
71 static JobManagerJob *job_manager_job_new (JobManager *manager,
73 GCancellable *cancellable,
74 JobManagerCallback callback,
76 static void job_manager_job_unref (JobManagerJob *job);
77 static void job_manager_remember_job (JobManager *manager,
78 const gchar *job_name,
80 static void job_manager_forget_job (JobManager *manager,
81 const gchar *job_name);
85 struct _JobManagerClass
87 GObjectClass __parent__;
94 GDBusConnection *connection;
95 SystemdManager *systemd_manager;
100 struct _JobManagerJob
104 GCancellable *cancellable;
105 JobManagerCallback callback;
111 G_DEFINE_TYPE (JobManager, job_manager, G_TYPE_OBJECT);
116 job_manager_class_init (JobManagerClass *klass)
118 GObjectClass *gobject_class;
120 gobject_class = G_OBJECT_CLASS (klass);
121 gobject_class->finalize = job_manager_finalize;
122 gobject_class->constructed = job_manager_constructed;
123 gobject_class->get_property = job_manager_get_property;
124 gobject_class->set_property = job_manager_set_property;
126 g_object_class_install_property (gobject_class,
128 g_param_spec_object ("connection",
131 G_TYPE_DBUS_CONNECTION,
133 G_PARAM_CONSTRUCT_ONLY |
134 G_PARAM_STATIC_STRINGS));
136 g_object_class_install_property (gobject_class,
137 PROP_SYSTEMD_MANAGER,
138 g_param_spec_object ("systemd-manager",
141 TYPE_SYSTEMD_MANAGER,
143 G_PARAM_CONSTRUCT_ONLY |
144 G_PARAM_STATIC_STRINGS));
150 job_manager_init (JobManager *manager)
152 /* create a mapping of systemd job names to job objects; we will use this
153 * to remember jobs that we started */
154 manager->jobs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
155 (GDestroyNotify) job_manager_job_unref);
162 job_manager_finalize (GObject *object)
164 JobManager *manager = JOB_MANAGER (object);
166 /* release all the jobs we have remembered */
167 g_hash_table_unref (manager->jobs);
169 /* release the D-Bus connection */
170 g_object_unref (manager->connection);
172 /* release the systemd manager */
173 g_signal_handlers_disconnect_matched (manager->systemd_manager,
175 0, 0, NULL, NULL, manager);
176 g_object_unref (manager->systemd_manager);
178 /* chain up to finalize parent class */
179 (*G_OBJECT_CLASS (job_manager_parent_class)->finalize) (object);
185 job_manager_constructed (GObject *object)
187 JobManager *manager = JOB_MANAGER (object);
189 /* connect to systemd's "JobRemoved" signal so that we are notified
190 * whenever a job is finished */
191 g_signal_connect (manager->systemd_manager, "job-removed",
192 G_CALLBACK (job_manager_job_removed), manager);
198 job_manager_get_property (GObject *object,
203 JobManager *manager = JOB_MANAGER (object);
207 case PROP_CONNECTION:
208 g_value_set_object (value, manager->connection);
210 case PROP_SYSTEMD_MANAGER:
211 g_value_set_object (value, manager->systemd_manager);
214 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
222 job_manager_set_property (GObject *object,
227 JobManager *manager = JOB_MANAGER (object);
231 case PROP_CONNECTION:
232 manager->connection = g_value_dup_object (value);
234 case PROP_SYSTEMD_MANAGER:
235 manager->systemd_manager = g_value_dup_object (value);
238 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
246 job_manager_start_unit_reply (GObject *object,
247 GAsyncResult *result,
250 JobManagerJob *job = user_data;
251 GError *error = NULL;
252 gchar *job_name = NULL;
254 g_return_if_fail (IS_SYSTEMD_MANAGER (object));
255 g_return_if_fail (G_IS_ASYNC_RESULT (result));
256 g_return_if_fail (user_data != NULL);
258 /* finish the start unit call */
259 if (!systemd_manager_call_start_unit_finish (job->manager->systemd_manager,
260 &job_name, result, &error))
262 /* there was an error. notify the caller */
263 job->callback (job->manager, job->unit, "failed", error, job->user_data);
264 g_error_free (error);
267 /* finish the job immediately */
268 job_manager_job_unref (job);
272 /* remember the job so that we can finish it in the "job-removed" signal handler.
273 * the service takes ownership of the job so we don't need to unref it here */
274 job_manager_remember_job (job->manager, job_name, job);
281 job_manager_stop_unit_reply (GObject *object,
282 GAsyncResult *result,
285 JobManagerJob *job = user_data;
286 GError *error = NULL;
287 gchar *job_name = NULL;
289 g_return_if_fail (IS_SYSTEMD_MANAGER (object));
290 g_return_if_fail (G_IS_ASYNC_RESULT (result));
291 g_return_if_fail (user_data != NULL);
293 /* finish the stop unit call */
294 if (!systemd_manager_call_stop_unit_finish (job->manager->systemd_manager,
295 &job_name, result, &error))
297 /* there was an error. notify the caller */
298 job->callback (job->manager, job->unit, "failed", error, job->user_data);
299 g_error_free (error);
302 /* finish the job immediately */
303 job_manager_job_unref (job);
307 /* remember the job so that we can finish it in the "job-removed" signal handler.
308 * the service takes ownership of the job so we don't need to unref it here */
309 job_manager_remember_job (job->manager, job_name, job);
316 job_manager_job_removed (SystemdManager *systemd_manager,
318 const gchar *job_name,
321 JobManager *job_manager)
325 g_return_if_fail (IS_SYSTEMD_MANAGER (systemd_manager));
326 g_return_if_fail (job_name != NULL && *job_name != '\0');
327 g_return_if_fail (result != NULL && *result != '\0');
328 g_return_if_fail (unit != NULL && *unit != '\0');
329 g_return_if_fail (IS_JOB_MANAGER (job_manager));
331 /* look up the remembered job for this job name */
332 job = g_hash_table_lookup (job_manager->jobs, job_name);
334 /* if no job is found, ignore this job-removed signal */
338 /* finish the job by notifying the caller */
339 job->callback (job_manager, job->unit, result, NULL, job->user_data);
341 /* forget about this job */
342 job_manager_forget_job (job_manager, job_name);
347 static JobManagerJob *
348 job_manager_job_new (JobManager *manager,
350 GCancellable *cancellable,
351 JobManagerCallback callback,
356 g_return_val_if_fail (IS_JOB_MANAGER (manager), NULL);
357 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
359 /* allocate a new job struct */
360 job = g_slice_new0 (JobManagerJob);
361 job->manager = g_object_ref (manager);
362 job->unit = g_strdup(unit);
363 if (cancellable != NULL)
364 job->cancellable = g_object_ref (cancellable);
365 job->callback = callback;
366 job->user_data = user_data;
373 job_manager_job_unref (JobManagerJob *job)
378 /* release all memory and references held by job */
379 if (job->cancellable != NULL)
380 g_object_unref (job->cancellable);
382 g_object_unref (job->manager);
383 g_slice_free (JobManagerJob, job);
389 job_manager_remember_job (JobManager *manager,
390 const char *job_name,
393 JobManagerJob *existing_job;
395 g_return_if_fail (IS_JOB_MANAGER (manager));
396 g_return_if_fail (job_name != NULL && *job_name != '\0');
397 g_return_if_fail (job != NULL);
399 /* if the job is already being remembered, there is a programming error that should be
401 existing_job = g_hash_table_lookup (manager->jobs, job_name);
402 if (existing_job != NULL)
404 g_critical ("Trying to remember the same job twice.");
407 /* associate the job name with the job */
408 g_hash_table_insert (manager->jobs, g_strdup (job_name), job);
414 job_manager_forget_job (JobManager *manager,
415 const gchar *job_name)
417 g_return_if_fail (IS_JOB_MANAGER (manager));
418 g_return_if_fail (job_name != NULL && *job_name != '\0');
420 g_hash_table_remove (manager->jobs, job_name);
426 * @connection: A connection to the system bus.
427 * @systemd_manager: An interface to the systemd manager created with
428 * systemd_manager_proxy_new_for_bus_sync()
430 * Creates a new JobManager object.
432 * Returns: A new instance of the #JobManager.
435 job_manager_new (GDBusConnection *connection,
436 SystemdManager *systemd_manager)
438 g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
439 g_return_val_if_fail (IS_SYSTEMD_MANAGER (systemd_manager), NULL);
441 return g_object_new (TYPE_JOB_MANAGER,
442 "connection", connection,
443 "systemd-manager", systemd_manager,
451 * @unit: The name of the systemd unit to start.
452 * @callback: a #JobManagerCallback that is called after the job is started.
453 * @user_data: userdata that is available in the #JobManagerCallback.
455 * Asynchronously starts @unit, and calls @callback with @user_data when it is finished.
458 job_manager_start (JobManager *manager,
460 GCancellable *cancellable,
461 JobManagerCallback callback,
466 g_return_if_fail (IS_JOB_MANAGER (manager));
467 g_return_if_fail (unit != NULL);
468 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
469 g_return_if_fail (callback != NULL);
471 /* create a new job object */
472 job = job_manager_job_new (manager, unit, cancellable, callback, user_data);
474 /* ask systemd to start the unit asynchronously */
475 systemd_manager_call_start_unit (manager->systemd_manager, unit, "fail", cancellable,
476 job_manager_start_unit_reply, job);
483 * @unit: The name of the systemd unit to stop.
484 * @callback: a #JobManagerCallback that is called after the job is stopped.
485 * @user_data: userdata that is available in the #JobManagerCallback.
487 * Asynchronously stops @unit, and calls @callback with @user_data when it is finished.
490 job_manager_stop (JobManager *manager,
492 GCancellable *cancellable,
493 JobManagerCallback callback,
498 g_return_if_fail (IS_JOB_MANAGER (manager));
499 g_return_if_fail (unit != NULL);
500 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
501 g_return_if_fail (callback != NULL);
503 /* create a new job object */
504 job = job_manager_job_new (manager, unit, cancellable, callback, user_data);
506 /* ask systemd to stop the unit asynchronously */
507 systemd_manager_call_stop_unit (manager->systemd_manager, unit, "fail", cancellable,
508 job_manager_stop_unit_reply, job);