First step of renaming the Boot Manager to Node Startup Controller
[profile/ivi/node-startup-controller.git] / node-startup-controller / job-manager.c
1 /* vi:set et ai sw=2 sts=2 ts=2: */
2 /* -
3  * Copyright (c) 2012 GENIVI.
4  *
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/.
8  */
9
10 #ifdef HAVE_CONFIG_H
11 #include <config.h>
12 #endif
13
14 #include <glib-object.h>
15 #include <gio/gio.h>
16
17 #include <node-startup-controller/job-manager.h>
18 #include <node-startup-controller/systemd-manager-dbus.h>
19
20
21
22 typedef struct _JobManagerJob JobManagerJob;
23
24
25
26 /* property identifiers */
27 enum
28 {
29   PROP_0,
30   PROP_CONNECTION,
31   PROP_SYSTEMD_MANAGER,
32 };
33
34
35
36 static void           job_manager_constructed      (GObject           *object);
37 static void           job_manager_finalize         (GObject           *object);
38 static void           job_manager_get_property     (GObject           *object,
39                                                     guint              prop_id,
40                                                     GValue            *value,
41                                                     GParamSpec        *pspec);
42 static void           job_manager_set_property     (GObject           *object,
43                                                     guint              prop_id,
44                                                     const GValue      *value,
45                                                     GParamSpec        *pspec);
46 static void           job_manager_start_unit_reply (GObject           *object,
47                                                     GAsyncResult      *result,
48                                                     gpointer           user_data);
49 static void           job_manager_stop_unit_reply  (GObject           *object,
50                                                     GAsyncResult      *result,
51                                                     gpointer           user_data);
52 static void           job_manager_job_removed      (SystemdManager    *systemd_manager,
53                                                     guint              id,
54                                                     const gchar       *job_name,
55                                                     const gchar       *unit,
56                                                     const gchar       *result,
57                                                     JobManager        *job_manager);
58 static JobManagerJob *job_manager_job_new          (JobManager        *manager,
59                                                     const gchar       *unit,
60                                                     GCancellable      *cancellable,
61                                                     JobManagerCallback callback,
62                                                     gpointer           user_data);
63 static void           job_manager_job_unref        (JobManagerJob     *job);
64 static void           job_manager_remember_job     (JobManager        *manager,
65                                                     const gchar       *job_name,
66                                                     JobManagerJob     *job);
67 static void           job_manager_forget_job       (JobManager        *manager,
68                                                     const gchar       *job_name);
69
70
71
72 struct _JobManagerClass
73 {
74   GObjectClass __parent__;
75 };
76
77 struct _JobManager
78 {
79   GObject __parent__;
80
81   GDBusConnection *connection;
82   SystemdManager  *systemd_manager;
83
84   GHashTable      *jobs;
85 };
86
87 struct _JobManagerJob
88 {
89   JobManager        *manager;
90   gchar             *unit;
91   GCancellable      *cancellable;
92   JobManagerCallback callback;
93   gpointer           user_data;
94 };
95
96
97
98 G_DEFINE_TYPE (JobManager, job_manager, G_TYPE_OBJECT);
99
100
101
102 static void
103 job_manager_class_init (JobManagerClass *klass)
104 {
105   GObjectClass *gobject_class;
106
107   gobject_class = G_OBJECT_CLASS (klass);
108   gobject_class->finalize = job_manager_finalize;
109   gobject_class->constructed = job_manager_constructed;
110   gobject_class->get_property = job_manager_get_property;
111   gobject_class->set_property = job_manager_set_property;
112
113   g_object_class_install_property (gobject_class,
114                                    PROP_CONNECTION,
115                                    g_param_spec_object ("connection",
116                                                         "connection",
117                                                         "connection",
118                                                         G_TYPE_DBUS_CONNECTION,
119                                                         G_PARAM_READWRITE |
120                                                         G_PARAM_CONSTRUCT_ONLY |
121                                                         G_PARAM_STATIC_STRINGS));
122
123   g_object_class_install_property (gobject_class,
124                                    PROP_SYSTEMD_MANAGER,
125                                    g_param_spec_object ("systemd-manager",
126                                                         "systemd-manager",
127                                                         "systemd-manager",
128                                                         TYPE_SYSTEMD_MANAGER,
129                                                         G_PARAM_READWRITE |
130                                                         G_PARAM_CONSTRUCT_ONLY |
131                                                         G_PARAM_STATIC_STRINGS));
132 }
133
134
135
136 static void
137 job_manager_init (JobManager *manager)
138 {
139   /* create a mapping of systemd job names to job objects; we will use this
140    * to remember jobs that we started */
141   manager->jobs = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
142                                         (GDestroyNotify) job_manager_job_unref);
143
144 }
145
146
147
148 static void
149 job_manager_finalize (GObject *object)
150 {
151   JobManager *manager = JOB_MANAGER (object);
152
153   /* release all the jobs we have remembered */
154   g_hash_table_unref (manager->jobs);
155
156   /* release the D-Bus connection */
157   g_object_unref (manager->connection);
158
159   /* release the systemd manager */
160   g_signal_handlers_disconnect_matched (manager->systemd_manager,
161                                         G_SIGNAL_MATCH_DATA,
162                                         0, 0, NULL, NULL, manager);
163   g_object_unref (manager->systemd_manager);
164
165   /* chain up to finalize parent class */
166   (*G_OBJECT_CLASS (job_manager_parent_class)->finalize) (object);
167 }
168
169
170
171 static void
172 job_manager_constructed (GObject *object)
173 {
174   JobManager *manager = JOB_MANAGER (object);
175
176   /* connect to systemd's "JobRemoved" signal so that we are notified
177    * whenever a job is finished */
178   g_signal_connect (manager->systemd_manager, "job-removed",
179                     G_CALLBACK (job_manager_job_removed), manager);
180 }
181
182
183
184 static void
185 job_manager_get_property (GObject    *object,
186                           guint       prop_id,
187                           GValue     *value,
188                           GParamSpec *pspec)
189 {
190   JobManager *manager = JOB_MANAGER (object);
191
192   switch (prop_id)
193     {
194     case PROP_CONNECTION:
195       g_value_set_object (value, manager->connection);
196       break;
197     case PROP_SYSTEMD_MANAGER:
198       g_value_set_object (value, manager->systemd_manager);
199       break;
200     default:
201       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
202       break;
203     }
204 }
205
206
207
208 static void
209 job_manager_set_property (GObject      *object,
210                           guint         prop_id,
211                           const GValue *value,
212                           GParamSpec   *pspec)
213 {
214   JobManager *manager = JOB_MANAGER (object);
215
216   switch (prop_id)
217     {
218     case PROP_CONNECTION:
219       manager->connection = g_value_dup_object (value);
220       break;
221     case PROP_SYSTEMD_MANAGER:
222       manager->systemd_manager = g_value_dup_object (value);
223       break;
224     default:
225       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
226       break;
227     }
228 }
229
230
231
232 static void
233 job_manager_start_unit_reply (GObject       *object,
234                               GAsyncResult *result,
235                               gpointer      user_data)
236 {
237   JobManagerJob *job = user_data;
238   GError        *error = NULL;
239   gchar         *job_name = NULL;
240
241   g_return_if_fail (IS_SYSTEMD_MANAGER (object));
242   g_return_if_fail (G_IS_ASYNC_RESULT (result));
243   g_return_if_fail (user_data != NULL);
244
245   /* finish the start unit call */
246   if (!systemd_manager_call_start_unit_finish (job->manager->systemd_manager,
247                                                &job_name, result, &error))
248     {
249       /* there was an error. notify the caller */
250       job->callback (job->manager, job->unit, "failed", error, job->user_data);
251       g_error_free (error);
252       g_free (job_name);
253
254       /* finish the job immediately */
255       job_manager_job_unref (job);
256     }
257   else
258     {
259       /* remember the job so that we can finish it in the "job-removed" signal handler.
260        * the service takes ownership of the job so we don't need to unref it here */
261       job_manager_remember_job (job->manager, job_name, job);
262     }
263 }
264
265
266
267 static void
268 job_manager_stop_unit_reply (GObject       *object,
269                              GAsyncResult *result,
270                              gpointer      user_data)
271 {
272   JobManagerJob *job = user_data;
273   GError        *error = NULL;
274   gchar         *job_name = NULL;
275
276   g_return_if_fail (IS_SYSTEMD_MANAGER (object));
277   g_return_if_fail (G_IS_ASYNC_RESULT (result));
278   g_return_if_fail (user_data != NULL);
279
280   /* finish the stop unit call */
281   if (!systemd_manager_call_stop_unit_finish (job->manager->systemd_manager,
282                                               &job_name, result, &error))
283     {
284       /* there was an error. notify the caller */
285       job->callback (job->manager, job->unit, "failed", error, job->user_data);
286       g_error_free (error);
287       g_free (job_name);
288
289       /* finish the job immediately */
290       job_manager_job_unref (job);
291     }
292   else
293     {
294       /* remember the job so that we can finish it in the "job-removed" signal handler.
295        * the service takes ownership of the job so we don't need to unref it here */
296       job_manager_remember_job (job->manager, job_name, job);
297     }
298 }
299
300
301
302 static void
303 job_manager_job_removed (SystemdManager *systemd_manager,
304                          guint           id,
305                          const gchar    *job_name,
306                          const gchar    *unit,
307                          const gchar    *result,
308                          JobManager     *job_manager)
309 {
310   JobManagerJob *job;
311
312   g_return_if_fail (IS_SYSTEMD_MANAGER (systemd_manager));
313   g_return_if_fail (job_name != NULL && *job_name != '\0');
314   g_return_if_fail (result != NULL && *result != '\0');
315   g_return_if_fail (unit != NULL && *unit != '\0');
316   g_return_if_fail (IS_JOB_MANAGER (job_manager));
317
318   /* look up the remembered job for this job name */
319   job = g_hash_table_lookup (job_manager->jobs, job_name);
320
321   /* if no job is found, ignore this job-removed signal */
322   if (job == NULL)
323     return;
324
325   /* finish the job by notifying the caller */
326   job->callback (job_manager, job->unit, result, NULL, job->user_data);
327
328   /* forget about this job */
329   job_manager_forget_job (job_manager, job_name);
330 }
331
332
333
334 static JobManagerJob *
335 job_manager_job_new (JobManager        *manager,
336                      const gchar       *unit,
337                      GCancellable      *cancellable,
338                      JobManagerCallback callback,
339                      gpointer           user_data)
340 {
341   JobManagerJob *job;
342
343   g_return_val_if_fail (IS_JOB_MANAGER (manager), NULL);
344   g_return_val_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable), NULL);
345
346   /* allocate a new job struct */
347   job = g_slice_new0 (JobManagerJob);
348   job->manager = g_object_ref (manager);
349   job->unit = g_strdup(unit);
350   if (cancellable != NULL)
351     job->cancellable = g_object_ref (cancellable);
352   job->callback = callback;
353   job->user_data = user_data;
354
355   return job;
356 }
357
358
359 static void
360 job_manager_job_unref (JobManagerJob *job)
361 {
362   if (job == NULL)
363     return;
364
365   /* release all memory and references held by job */
366   if (job->cancellable != NULL)
367     g_object_unref (job->cancellable);
368   g_free (job->unit);
369   g_object_unref (job->manager);
370   g_slice_free (JobManagerJob, job);
371 }
372
373
374
375 static void
376 job_manager_remember_job (JobManager    *manager,
377                           const char    *job_name,
378                           JobManagerJob *job)
379 {
380   JobManagerJob *existing_job;
381
382   g_return_if_fail (IS_JOB_MANAGER (manager));
383   g_return_if_fail (job_name != NULL && *job_name != '\0');
384   g_return_if_fail (job != NULL);
385
386   /* if the job is already being remembered, there is a programming error that should be
387    * notified */
388   existing_job = g_hash_table_lookup (manager->jobs, job_name);
389   if (existing_job != NULL)
390     {
391       g_critical ("Trying to remember the same job twice.");
392       return;
393     }
394   /* associate the job name with the job */
395   g_hash_table_insert (manager->jobs, g_strdup (job_name), job);
396 }
397
398
399
400 static void
401 job_manager_forget_job (JobManager  *manager,
402                         const gchar *job_name)
403 {
404   g_return_if_fail (IS_JOB_MANAGER (manager));
405   g_return_if_fail (job_name != NULL && *job_name != '\0');
406
407   g_hash_table_remove (manager->jobs, job_name);
408 }
409
410
411
412 JobManager *
413 job_manager_new (GDBusConnection *connection,
414                  SystemdManager  *systemd_manager)
415 {
416   g_return_val_if_fail (G_IS_DBUS_CONNECTION (connection), NULL);
417   g_return_val_if_fail (IS_SYSTEMD_MANAGER (systemd_manager), NULL);
418
419   return g_object_new (TYPE_JOB_MANAGER,
420                        "connection", connection,
421                        "systemd-manager", systemd_manager,
422                        NULL);
423 }
424
425
426
427 void
428 job_manager_start (JobManager        *manager,
429                    const gchar       *unit,
430                    GCancellable      *cancellable,
431                    JobManagerCallback callback,
432                    gpointer           user_data)
433 {
434   JobManagerJob *job;
435
436   g_return_if_fail (IS_JOB_MANAGER (manager));
437   g_return_if_fail (unit != NULL);
438   g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
439   g_return_if_fail (callback != NULL);
440
441   /* create a new job object */
442   job = job_manager_job_new (manager, unit, cancellable, callback, user_data);
443
444   /* ask systemd to start the unit asynchronously */
445   systemd_manager_call_start_unit (manager->systemd_manager, unit, "fail", cancellable,
446                                    job_manager_start_unit_reply, job);
447 }
448
449
450
451 void
452 job_manager_stop (JobManager        *manager,
453                   const gchar       *unit,
454                   GCancellable      *cancellable,
455                   JobManagerCallback callback,
456                   gpointer           user_data)
457 {
458   JobManagerJob *job;
459
460   g_return_if_fail (IS_JOB_MANAGER (manager));
461   g_return_if_fail (unit != NULL);
462   g_return_if_fail (cancellable == NULL || G_IS_CANCELLABLE (cancellable));
463   g_return_if_fail (callback != NULL);
464
465   /* create a new job object */
466   job = job_manager_job_new (manager, unit, cancellable, callback, user_data);
467
468   /* ask systemd to stop the unit asynchronously */
469   systemd_manager_call_stop_unit (manager->systemd_manager, unit, "fail", cancellable,
470                                   job_manager_stop_unit_reply, job);
471 }