ed5b357e402a7b7f9d1f8feb905121204326293b
[profile/ivi/node-startup-controller.git] / boot-manager / luc-starter.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 <boot-manager/boot-manager-service.h>
18 #include <boot-manager/luc-starter.h>
19 #include <luc-handler/luc-handler-dbus.h>
20
21
22
23 /* property identifiers */
24 enum
25 {
26   PROP_0,
27   PROP_BOOT_MANAGER,
28   PROP_LUC_HANDLER,
29 };
30
31
32
33 static void luc_starter_constructed       (GObject            *object);
34 static void luc_starter_finalize          (GObject            *object);
35 static void luc_starter_get_property      (GObject            *object,
36                                            guint               prop_id,
37                                            GValue             *value,
38                                            GParamSpec         *pspec);
39 static void luc_starter_set_property      (GObject            *object,
40                                            guint               prop_id,
41                                            const GValue       *value,
42                                            GParamSpec         *pspec);
43 static gint luc_starter_compare_luc_types (gconstpointer       a,
44                                            gconstpointer       b,
45                                            gpointer            user_data);
46 static void luc_starter_start_next_group  (LUCStarter         *starter);
47 static void luc_starter_start_app         (const gchar        *app,
48                                            LUCStarter         *starter);
49 static void luc_starter_start_app_finish  (BootManagerService *service,
50                                            const gchar        *unit,
51                                            const gchar        *result,
52                                            GError             *error,
53                                            gpointer            user_data);
54
55
56
57 struct _LUCStarterClass
58 {
59   GObjectClass __parent__;
60 };
61
62 struct _LUCStarter
63 {
64   GObject             __parent__;
65
66   BootManagerService *boot_manager;
67   LUCHandler         *luc_handler;
68
69   gchar             **prioritised_types;
70
71   GList              *start_order;
72   GHashTable         *start_groups;
73 };
74
75
76
77 G_DEFINE_TYPE (LUCStarter, luc_starter, G_TYPE_OBJECT);
78
79
80
81 static void
82 luc_starter_class_init (LUCStarterClass *klass)
83 {
84   GObjectClass *gobject_class;
85
86   gobject_class = G_OBJECT_CLASS (klass);
87   gobject_class->constructed = luc_starter_constructed;
88   gobject_class->finalize = luc_starter_finalize;
89   gobject_class->get_property = luc_starter_get_property;
90   gobject_class->set_property = luc_starter_set_property;
91
92   g_object_class_install_property (gobject_class,
93                                    PROP_BOOT_MANAGER,
94                                    g_param_spec_object ("boot-manager",
95                                                         "boot-manager",
96                                                         "boot-manager",
97                                                         BOOT_MANAGER_TYPE_SERVICE,
98                                                         G_PARAM_READWRITE |
99                                                         G_PARAM_CONSTRUCT_ONLY |
100                                                         G_PARAM_STATIC_STRINGS));
101
102   g_object_class_install_property (gobject_class,
103                                    PROP_LUC_HANDLER,
104                                    g_param_spec_object ("luc-handler",
105                                                         "luc-handler",
106                                                         "luc-handler",
107                                                         TYPE_LUC_HANDLER,
108                                                         G_PARAM_READWRITE |
109                                                         G_PARAM_CONSTRUCT_ONLY |
110                                                         G_PARAM_STATIC_STRINGS));
111 }
112
113
114
115 static void
116 luc_starter_init (LUCStarter *starter)
117 {
118 }
119
120
121
122 static void
123 luc_starter_constructed (GObject *object)
124 {
125   LUCStarter *starter = LUC_STARTER (object);
126
127   /* parse the prioritised LUC types defined at build-time */
128   starter->prioritised_types = g_strsplit (PRIORITISED_LUC_TYPES, ",", -1);
129 }
130
131
132
133 static void
134 luc_starter_finalize (GObject *object)
135 {
136   LUCStarter *starter = LUC_STARTER (object);
137
138   /* free the prioritised types array */
139   g_strfreev (starter->prioritised_types);
140
141   /* release the boot manager */
142   g_object_unref (starter->boot_manager);
143
144   /* release the LUC handler */
145   g_object_unref (starter->luc_handler);
146
147   (*G_OBJECT_CLASS (luc_starter_parent_class)->finalize) (object);
148 }
149
150
151
152 static void
153 luc_starter_get_property (GObject    *object,
154                           guint       prop_id,
155                           GValue     *value,
156                           GParamSpec *pspec)
157 {
158   LUCStarter *starter = LUC_STARTER (object);
159
160   switch (prop_id)
161     {
162     case PROP_BOOT_MANAGER:
163       g_value_set_object (value, starter->boot_manager);
164       break;
165     case PROP_LUC_HANDLER:
166       g_value_set_object (value, starter->luc_handler);
167       break;
168     default:
169       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
170       break;
171     }
172 }
173
174
175
176 static void
177 luc_starter_set_property (GObject      *object,
178                           guint         prop_id,
179                           const GValue *value,
180                           GParamSpec   *pspec)
181 {
182   LUCStarter *starter = LUC_STARTER (object);
183
184   switch (prop_id)
185     {
186     case PROP_BOOT_MANAGER:
187       starter->boot_manager = g_value_dup_object (value);
188       break;
189     case PROP_LUC_HANDLER:
190       starter->luc_handler = g_value_dup_object (value);
191       break;
192     default:
193       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
194       break;
195     }
196 }
197
198
199
200 static gint
201 luc_starter_compare_luc_types (gconstpointer a,
202                                gconstpointer b,
203                                gpointer      user_data)
204 {
205   LUCStarter *starter = LUC_STARTER (user_data);
206   gchar      *type_a;
207   gchar      *type_b;
208   gint        n;
209   gint        pos_a = G_MAXINT;
210   gint        pos_b = G_MAXINT;
211
212   /* try to find the first type in the prioritised types list */
213   for (n = 0; pos_a < 0 && starter->prioritised_types[n] != NULL; n++)
214     if (g_strcmp0 (starter->prioritised_types[n], type_a) == 0)
215       pos_a = n;
216
217   /* try to find the second type in the prioritised types list */
218   for (n = 0; pos_b < 0 && starter->prioritised_types[n] != NULL; n++)
219     if (g_strcmp0 (starter->prioritised_types[n], type_b) == 0)
220       pos_b = n;
221
222   /* this statement has the following effect when sorting:
223    * - a and b are prioritised     -> prioritization order is preserved
224    * - a is prioritised, b isn't   -> negative return value, a comes first
225    * - b is prioritised, a isn't   -> positive return value, b comes first
226    * - neither a nor b prioritised -> return value is 0, arbitrary order
227    */
228   return pos_a - pos_b;
229 }
230
231
232
233 static void
234 luc_starter_start_next_group (LUCStarter *starter)
235 {
236   const gchar *group_name;
237   GPtrArray   *group;
238
239   g_return_if_fail (IS_LUC_STARTER (starter));
240   g_return_if_fail (starter->start_order != NULL);
241
242   group_name = starter->start_order->data;
243
244   g_debug ("start group '%s'", group_name);
245
246   /* look up the group with this name */
247   group = g_hash_table_lookup (starter->start_groups, group_name);
248   if (group != NULL)
249     {
250       /* launch all the applications in the group asynchronously */
251       g_ptr_array_foreach (group, (GFunc) luc_starter_start_app, starter);
252     }
253 }
254
255
256
257 static void
258 luc_starter_start_app (const gchar *app,
259                        LUCStarter  *starter)
260 {
261   g_return_if_fail (app != NULL && *app != '\0');
262   g_return_if_fail (IS_LUC_STARTER (starter));
263
264   g_debug ("start app '%s'", app);
265
266   boot_manager_service_start (starter->boot_manager, app, NULL,
267                               luc_starter_start_app_finish,
268                               starter);
269 }
270
271
272
273 static void
274 luc_starter_start_app_finish (BootManagerService *service,
275                               const gchar        *unit,
276                               const gchar        *result,
277                               GError             *error,
278                               gpointer            user_data)
279 {
280   const gchar *group_name;
281   LUCStarter  *starter = LUC_STARTER (user_data);
282   GPtrArray   *group;
283   gboolean     app_found = FALSE;
284   guint        n;
285
286   g_return_if_fail (BOOT_MANAGER_IS_SERVICE (service));
287   g_return_if_fail (unit != NULL && *unit != '\0');
288   g_return_if_fail (IS_LUC_STARTER (user_data));
289   g_return_if_fail (starter->start_order != NULL);
290
291   g_debug ("start app '%s' finish", unit);
292
293   /* get the current start group */
294   group_name = starter->start_order->data;
295
296   /* look up the apps for this group */
297   group = g_hash_table_lookup (starter->start_groups, group_name);
298   if (group != NULL)
299     {
300       /* try to find the current app in the group */
301       for (n = 0; !app_found && n < group->len; n++)
302         {
303           if (g_strcmp0 (unit, g_ptr_array_index (group, n)) == 0)
304             app_found = TRUE;
305         }
306
307       /* remove the app from the group */
308       if (app_found)
309         g_ptr_array_remove_index (group, n-1);
310
311       /* check if this was the last app in the group to be started */
312       if (group->len == 0)
313         {
314           g_debug ("start group %s finish", group_name);
315
316           /* remove the group from the groups and the order */
317           g_hash_table_remove (starter->start_groups, group_name);
318           starter->start_order = g_list_delete_link (starter->start_order,
319                                                      starter->start_order);
320
321           /* start the next group if there are any left */
322           if (starter->start_order != NULL)
323             luc_starter_start_next_group (starter);
324         }
325     }
326 }
327
328
329
330 LUCStarter *
331 luc_starter_new (BootManagerService *boot_manager,
332                  LUCHandler         *luc_handler)
333 {
334   g_return_val_if_fail (BOOT_MANAGER_IS_SERVICE (boot_manager), NULL);
335   g_return_val_if_fail (IS_LUC_HANDLER (luc_handler), NULL);
336
337   return g_object_new (TYPE_LUC_STARTER,
338                        "boot-manager", boot_manager,
339                        "luc-handler", luc_handler,
340                        NULL);
341 }
342
343
344
345 void
346 luc_starter_start_groups (LUCStarter *starter)
347 {
348   GVariantIter iter;
349   GPtrArray   *group;
350   GVariant    *context;
351   GList       *lp;
352   gchar      **apps;
353   gchar       *type;
354   guint        n;
355
356   g_debug ("prioritised types:");
357   for (n = 0; starter->prioritised_types[n] != NULL; n++)
358     g_debug ("  %s",  starter->prioritised_types[n]);
359
360   /* clear the start order */
361   if (starter->start_order != NULL)
362     {
363       g_list_free (starter->start_order);
364       starter->start_order = NULL;
365     }
366
367   /* clear the start groups */
368   if (starter->start_groups != NULL)
369     {
370       g_hash_table_remove_all (starter->start_groups);
371     }
372   else
373     {
374       starter->start_groups = 
375         g_hash_table_new_full (g_str_hash, g_str_equal,
376                                g_free, (GDestroyNotify) g_ptr_array_free);
377     }
378
379   /* get the current last user context */
380   context = luc_handler_get_last_user_context (starter->luc_handler);
381   type = g_variant_print (context, TRUE);
382   g_debug ("context: %s", type);
383
384   /* create groups for all types in the LUC */
385   g_variant_iter_init (&iter, context);
386   while (g_variant_iter_loop (&iter, "{s^as}", &type, &apps))
387     {
388       group = g_ptr_array_new_with_free_func (g_free);
389
390       for (n = 0; apps != NULL && apps[n] != NULL; n++)
391         {
392           g_debug ("  group %s app %s", type, apps[n]);
393           g_ptr_array_add (group, g_strdup (apps[n]));
394         }
395
396       g_hash_table_insert (starter->start_groups, g_strdup (type), group);
397     }
398
399   /* generate the start order by sorting the LUC types according to
400    * the prioritised types */
401   starter->start_order = g_hash_table_get_keys (starter->start_groups);
402   starter->start_order = g_list_sort_with_data (starter->start_order,
403                                                 luc_starter_compare_luc_types,
404                                                 starter);
405
406   g_debug ("start groups (ordered):");
407   for (lp = starter->start_order; lp != NULL; lp = lp->next)
408     g_debug ("  %s", (gchar *)lp->data);
409
410   if (starter->start_order != NULL)
411     luc_starter_start_next_group (starter);
412 }