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 #JobManager simplifies starting and stopping systemd units by handling all the
29 * D-Bus communication with the systemd manager internally. Units can be started and
30 * stopped using job_manager_start() and job_manager_stop() and will call the @callback
31 * passed to the function when the unit is started or stopped.
33 * The #JobManager contains an internal table which associates systemd job names with the
34 * necessary information about that job, e.g. the unit name.
36 * When the #JobManager is told to start or stop a unit, it creates a job in systemd to
37 * start or stop that unit, stores the name of that job and waits until that job has
38 * finished before calling the @callback.
40 * The #JobManager finds out that a job has finished by listening to "JobRemoved" signals
41 * from systemd and looking for that job by its job name.
46 typedef struct _JobManagerJob JobManagerJob;
50 /* property identifiers */
60 static void job_manager_constructed (GObject *object);
61 static void job_manager_finalize (GObject *object);
62 static void job_manager_get_property (GObject *object,
66 static void job_manager_set_property (GObject *object,
70 static void job_manager_start_unit_reply (GObject *object,
73 static void job_manager_stop_unit_reply (GObject *object,
76 static void job_manager_job_removed (SystemdManager *systemd_manager,
78 const gchar *job_name,
81 JobManager *job_manager);
82 static JobManagerJob *job_manager_job_new (JobManager *manager,
84 GCancellable *cancellable,
85 JobManagerCallback callback,
87 static void job_manager_job_unref (JobManagerJob *job);
88 static void job_manager_remember_job (JobManager *manager,
89 const gchar *job_name,
91 static void job_manager_forget_job (JobManager *manager,
92 const gchar *job_name);
96 struct _JobManagerClass
98 GObjectClass __parent__;
105 GDBusConnection *connection;
106 SystemdManager *systemd_manager;
111 struct _JobManagerJob
115 GCancellable *cancellable;
116 JobManagerCallback callback;
122 G_DEFINE_TYPE (JobManager, job_manager, G_TYPE_OBJECT);
127 job_manager_class_init (JobManagerClass *klass)
129 GObjectClass *gobject_class;
131 gobject_class = G_OBJECT_CLASS (klass);
132 gobject_class->finalize = job_manager_finalize;
133 gobject_class->constructed = job_manager_constructed;
134 gobject_class->get_property = job_manager_get_property;
135 gobject_class->set_property = job_manager_set_property;
137 g_object_class_install_property (gobject_class,
139 g_param_spec_object ("connection",
142 G_TYPE_DBUS_CONNECTION,
144 G_PARAM_CONSTRUCT_ONLY |
145 G_PARAM_STATIC_STRINGS));
147 g_object_class_install_property (gobject_class,
148 PROP_SYSTEMD_MANAGER,
149 g_param_spec_object ("systemd-manager",
152 TYPE_SYSTEMD_MANAGER,
154 G_PARAM_CONSTRUCT_ONLY |
155 G_PARAM_STATIC_STRINGS));
161 job_manager_init (JobManager *manager)
163 /* create a mapping of systemd job names to job objects; we will use this
164 * to remember jobs that we started */
165 manager->jobs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
166 (GDestroyNotify) job_manager_job_unref);
173 job_manager_finalize (GObject *object)
175 JobManager *manager = JOB_MANAGER (object);
177 /* release all the jobs we have remembered */
178 g_hash_table_unref (manager->jobs);
180 /* release the D-Bus connection */
181 g_object_unref (manager->connection);
183 /* release the systemd manager */
184 g_signal_handlers_disconnect_matched (manager->systemd_manager,
186 0, 0, NULL, NULL, manager);
187 g_object_unref (manager->systemd_manager);
189 /* chain up to finalize parent class */
190 (*G_OBJECT_CLASS (job_manager_parent_class)->finalize) (object);
196 job_manager_constructed (GObject *object)
198 JobManager *manager = JOB_MANAGER (object);
200 /* connect to systemd's "JobRemoved" signal so that we are notified
201 * whenever a job is finished */
202 g_signal_connect (manager->systemd_manager, "job-removed",
203 G_CALLBACK (job_manager_job_removed), manager);
209 job_manager_get_property (GObject *object,
214 JobManager *manager = JOB_MANAGER (object);
218 case PROP_CONNECTION:
219 g_value_set_object (value, manager->connection);
221 case PROP_SYSTEMD_MANAGER:
222 g_value_set_object (value, manager->systemd_manager);
225 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
233 job_manager_set_property (GObject *object,
238 JobManager *manager = JOB_MANAGER (object);
242 case PROP_CONNECTION:
243 manager->connection = g_value_dup_object (value);
245 case PROP_SYSTEMD_MANAGER:
246 manager->systemd_manager = g_value_dup_object (value);
249 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
257 job_manager_start_unit_reply (GObject *object,
258 GAsyncResult *result,
261 JobManagerJob *job = user_data;
262 GError *error = NULL;
263 gchar *job_name = NULL;
265 g_return_if_fail (IS_SYSTEMD_MANAGER (object));
266 g_return_if_fail (G_IS_ASYNC_RESULT (result));
267 g_return_if_fail (user_data != NULL);
269 /* finish the start unit call */
270 if (!systemd_manager_call_start_unit_finish (job->manager->systemd_manager,
271 &job_name, result, &error))
273 /* there was an error. notify the caller */
274 job->callback (job->manager, job->unit, "failed", error, job->user_data);
275 g_error_free (error);
278 /* finish the job immediately */
279 job_manager_job_unref (job);
283 /* remember the job so that we can finish it in the "job-removed" signal handler.
284 * the service takes ownership of the job so we don't need to unref it here */
285 job_manager_remember_job (job->manager, job_name, job);
292 job_manager_stop_unit_reply (GObject *object,
293 GAsyncResult *result,
296 JobManagerJob *job = user_data;
297 GError *error = NULL;
298 gchar *job_name = NULL;
300 g_return_if_fail (IS_SYSTEMD_MANAGER (object));
301 g_return_if_fail (G_IS_ASYNC_RESULT (result));
302 g_return_if_fail (user_data != NULL);
304 /* finish the stop unit call */
305 if (!systemd_manager_call_stop_unit_finish (job->manager->systemd_manager,
306 &job_name, result, &error))
308 /* there was an error. notify the caller */
309 job->callback (job->manager, job->unit, "failed", error, job->user_data);
310 g_error_free (error);
313 /* finish the job immediately */
314 job_manager_job_unref (job);
318 /* remember the job so that we can finish it in the "job-removed" signal handler.
319 * the service takes ownership of the job so we don't need to unref it here */
320 job_manager_remember_job (job->manager, job_name, job);
327 job_manager_job_removed (SystemdManager *systemd_manager,
329 const gchar *job_name,
332 JobManager *job_manager)
336 g_return_if_fail (IS_SYSTEMD_MANAGER (systemd_manager));
337 g_return_if_fail (job_name != NULL && *job_name != '\0');
338 g_return_if_fail (result != NULL && *result != '\0');
339 g_return_if_fail (unit != NULL && *unit != '\0');
340 g_return_if_fail (IS_JOB_MANAGER (job_manager));
342 /* look up the remembered job for this job name */
343 job = g_hash_table_lookup (job_manager->jobs, job_name);
345 /* if no job is found, ignore this job-removed signal */
349 /* finish the job by notifying the caller */
350 job->callback (job_manager, job->unit, result, NULL, job->user_data);
352 /* forget about this job */
353 job_manager_forget_job (job_manager, job_name);
358 static JobManagerJob *
359 job_manager_job_new (JobManager *manager,
361 GCancellable *cancellable,
362 JobManagerCallback callback,
367 g_return_val_if_fail (IS_JOB_MANAGER (manager), NULL);
368 g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
370 /* allocate a new job struct */
371 job = g_slice_new0 (JobManagerJob);
372 job->manager = g_object_ref (manager);
373 job->unit = g_strdup(unit);
374 if (cancellable != NULL)
375 job->cancellable = g_object_ref (cancellable);
376 job->callback = callback;
377 job->user_data = user_data;
384 job_manager_job_unref (JobManagerJob *job)
389 /* release all memory and references held by job */
390 if (job->cancellable != NULL)
391 g_object_unref (job->cancellable);
393 g_object_unref (job->manager);
394 g_slice_free (JobManagerJob, job);
400 job_manager_remember_job (JobManager *manager,
401 const char *job_name,
404 JobManagerJob *existing_job;
406 g_return_if_fail (IS_JOB_MANAGER (manager));
407 g_return_if_fail (job_name != NULL && *job_name != '\0');
408 g_return_if_fail (job != NULL);
410 /* if the job is already being remembered, there is a programming error that should be
412 existing_job = g_hash_table_lookup (manager->jobs, job_name);
413 if (existing_job != NULL)
415 g_critical ("Trying to remember the same job twice.");
418 /* associate the job name with the job */
419 g_hash_table_insert (manager->jobs, g_strdup (job_name), job);
425 job_manager_forget_job (JobManager *manager,
426 const gchar *job_name)
428 g_return_if_fail (IS_JOB_MANAGER (manager));
429 g_return_if_fail (job_name != NULL && *job_name != '\0');
431 g_hash_table_remove (manager->jobs, job_name);
437 * @connection: A connection to the system bus.
438 * @systemd_manager: An interface to the systemd manager created with
439 * systemd_manager_proxy_new_for_bus_sync()
441 * Creates a new JobManager object.
443 * Returns: A new instance of the #JobManager.
446 job_manager_new (GDBusConnection *connection,
447 SystemdManager *systemd_manager)
449 g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
450 g_return_val_if_fail (IS_SYSTEMD_MANAGER (systemd_manager), NULL);
452 return g_object_new (TYPE_JOB_MANAGER,
453 "connection", connection,
454 "systemd-manager", systemd_manager,
462 * @unit: The name of the systemd unit to start.
463 * @callback: a #JobManagerCallback that is called after the job is started.
464 * @user_data: userdata that is available in the #JobManagerCallback.
466 * Asynchronously starts @unit, and calls @callback with @user_data when it is finished.
469 job_manager_start (JobManager *manager,
471 GCancellable *cancellable,
472 JobManagerCallback callback,
477 g_return_if_fail (IS_JOB_MANAGER (manager));
478 g_return_if_fail (unit != NULL);
479 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
480 g_return_if_fail (callback != NULL);
482 /* create a new job object */
483 job = job_manager_job_new (manager, unit, cancellable, callback, user_data);
485 /* ask systemd to start the unit asynchronously */
486 systemd_manager_call_start_unit (manager->systemd_manager, unit, "fail", cancellable,
487 job_manager_start_unit_reply, job);
494 * @unit: The name of the systemd unit to stop.
495 * @callback: a #JobManagerCallback that is called after the job is stopped.
496 * @user_data: userdata that is available in the #JobManagerCallback.
498 * Asynchronously stops @unit, and calls @callback with @user_data when it is finished.
501 job_manager_stop (JobManager *manager,
503 GCancellable *cancellable,
504 JobManagerCallback callback,
509 g_return_if_fail (IS_JOB_MANAGER (manager));
510 g_return_if_fail (unit != NULL);
511 g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
512 g_return_if_fail (callback != NULL);
514 /* create a new job object */
515 job = job_manager_job_new (manager, unit, cancellable, callback, user_data);
517 /* ask systemd to stop the unit asynchronously */
518 systemd_manager_call_stop_unit (manager->systemd_manager, unit, "fail", cancellable,
519 job_manager_stop_unit_reply, job);