Update to upstream 1.0.1
[profile/ivi/gsignond.git] / src / daemon / plugins / gsignond-plugin-remote.c
1 /* vi: set et sw=4 ts=4 cino=t0,(0: */
2 /* -*- Mode: C; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 /*
4  * This file is part of gsignond
5  *
6  * Copyright (C) 2013-2014 Intel Corporation.
7  *
8  * Contact: Imran Zaman <imran.zaman@intel.com>
9  *
10  * This library is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public
12  * License as published by the Free Software Foundation; either
13  * version 2.1 of the License, or (at your option) any later version.
14  *
15  * This library is distributed in the hope that it will be useful, but
16  * WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with this library; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23  * 02110-1301 USA
24  */
25
26 #include "config.h"
27
28 #include "gsignond/gsignond-log.h"
29 #include "gsignond/gsignond-error.h"
30 #include "gsignond/gsignond-plugin-interface.h"
31 #include "common/gsignond-pipe-stream.h"
32 #include "daemon/dbus/gsignond-dbus.h"
33 #include "gsignond-plugin-remote-private.h"
34 #include "gsignond-plugin-remote.h"
35
36 enum
37 {
38     PROP_0,
39     PROP_TYPE,
40     PROP_MECHANISMS,
41     N_PROPERTIES
42 };
43
44 static void
45 gsignond_plugin_remote_interface_init (
46         GSignondPluginInterface *iface);
47
48 G_DEFINE_TYPE_WITH_CODE (GSignondPluginRemote, gsignond_plugin_remote,
49         G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GSIGNOND_TYPE_PLUGIN,
50                 gsignond_plugin_remote_interface_init));
51
52 #define GSIGNOND_PLUGIN_REMOTE_GET_PRIV(obj) \
53         G_TYPE_INSTANCE_GET_PRIVATE ((obj), GSIGNOND_TYPE_PLUGIN_REMOTE, \
54         GSignondPluginRemotePrivate)
55
56 static void
57 _on_child_down_cb (
58         GPid  pid,
59         gint  status,
60         gpointer data)
61 {
62     g_spawn_close_pid (pid);
63
64     GSignondPluginRemote *plugin = GSIGNOND_PLUGIN_REMOTE (data);
65
66     DBG ("Plugind(%p) with pid (%d) closed with status %d", plugin, pid,
67             status);
68
69     plugin->priv->is_plugind_up = FALSE;
70
71 }
72
73 static void
74 gsignond_plugin_remote_set_property (
75         GObject *object,
76         guint property_id,
77         const GValue *value,
78         GParamSpec *pspec)
79 {
80     switch (property_id) {
81         default:
82             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
83     }
84 }
85
86 static void
87 gsignond_plugin_remote_get_property (
88         GObject *object,
89         guint property_id,
90         GValue *value,
91         GParamSpec *pspec)
92 {
93     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (object);
94
95     switch (property_id) {
96         case PROP_TYPE: {
97             g_value_set_string (value,
98                                 gsignond_dbus_remote_plugin_v1_get_method(self->priv->dbus_plugin_proxy));
99             break;
100         }
101         case PROP_MECHANISMS: {
102             g_value_set_boxed (value,
103                                gsignond_dbus_remote_plugin_v1_get_mechanisms(self->priv->dbus_plugin_proxy));
104             break;
105         }
106         default:
107             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
108     }
109
110 }
111
112 static gboolean _check_child_exited(GSignondPluginRemote *self)
113 {
114     if (kill (self->priv->cpid, 0) == 0) {
115         WARN ("Plugind has to be killed with SIGKILL");
116         kill (self->priv->cpid, SIGKILL);
117     }
118     return FALSE;
119 }
120
121 static void
122 gsignond_plugin_remote_dispose (GObject *object)
123 {
124     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (object);
125
126     if (self->priv->cpid > 0 && self->priv->is_plugind_up) {
127         DBG ("Send SIGTERM to Plugind");
128         kill (self->priv->cpid, SIGTERM);
129         guint check_id = g_timeout_add(1000, (GSourceFunc)_check_child_exited, self);
130         while (self->priv->is_plugind_up)
131             g_main_context_iteration(NULL, TRUE);
132         g_source_remove(check_id);
133         DBG ("Plugind DESTROYED");
134     }
135     self->priv->cpid = 0;
136
137     if (self->priv->child_watch_id > 0) {
138         g_source_remove (self->priv->child_watch_id);
139         self->priv->child_watch_id = 0;
140     }
141
142     if (self->priv->connection) {
143         g_object_unref (self->priv->connection);
144         self->priv->connection = NULL;
145     }
146
147     if (self->priv->dbus_plugin_proxy) {
148         g_signal_handler_disconnect (self->priv->dbus_plugin_proxy,
149                 self->priv->signal_response);
150         g_signal_handler_disconnect (self->priv->dbus_plugin_proxy,
151                 self->priv->signal_response_final);
152         g_signal_handler_disconnect (self->priv->dbus_plugin_proxy,
153                 self->priv->signal_store);
154         g_signal_handler_disconnect (self->priv->dbus_plugin_proxy,
155                 self->priv->signal_error);
156         g_signal_handler_disconnect (self->priv->dbus_plugin_proxy,
157                 self->priv->signal_user_action_required);
158         g_signal_handler_disconnect (self->priv->dbus_plugin_proxy,
159                 self->priv->signal_refreshed);
160         g_signal_handler_disconnect (self->priv->dbus_plugin_proxy,
161                 self->priv->signal_status_changed);
162         g_object_unref (self->priv->dbus_plugin_proxy);
163         self->priv->dbus_plugin_proxy = NULL;
164     }
165
166     G_OBJECT_CLASS (gsignond_plugin_remote_parent_class)->dispose (object);
167 }
168
169 static void
170 gsignond_plugin_remote_finalize (GObject *object)
171 {
172     //GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (object);
173
174     G_OBJECT_CLASS (gsignond_plugin_remote_parent_class)->finalize (object);
175 }
176
177 static void
178 gsignond_plugin_remote_class_init (GSignondPluginRemoteClass *klass)
179 {
180     GObjectClass* object_class = G_OBJECT_CLASS (klass);
181
182     g_type_class_add_private (object_class,
183             sizeof (GSignondPluginRemotePrivate));
184
185     object_class->get_property = gsignond_plugin_remote_get_property;
186     object_class->set_property = gsignond_plugin_remote_set_property;
187     object_class->dispose = gsignond_plugin_remote_dispose;
188     object_class->finalize = gsignond_plugin_remote_finalize;
189
190     g_object_class_override_property (object_class, PROP_TYPE, "type");
191     g_object_class_override_property (object_class, PROP_MECHANISMS,
192             "mechanisms");
193
194 }
195
196 static void
197 gsignond_plugin_remote_init (GSignondPluginRemote *self)
198 {
199     self->priv = GSIGNOND_PLUGIN_REMOTE_GET_PRIV(self);
200
201     self->priv->connection = NULL;
202     self->priv->dbus_plugin_proxy = NULL;
203     self->priv->cpid = 0;
204
205     self->priv->child_watch_id = 0;
206
207     self->priv->is_plugind_up = FALSE;
208 }
209
210 static void
211 _cancel_async_cb (
212         GObject *object,
213         GAsyncResult *res,
214         gpointer user_data)
215 {
216     GError *error = NULL;
217     GSignondDbusRemotePluginV1 *proxy = GSIGNOND_DBUS_REMOTE_PLUGIN_V1 (object);
218     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (user_data);
219
220     gsignond_dbus_remote_plugin_v1_call_cancel_finish (proxy, res, &error);
221     if (error) {
222         gsignond_plugin_error (GSIGNOND_PLUGIN(self), error);
223         g_error_free (error);
224     }
225 }
226
227 static void
228 gsignond_plugin_remote_cancel (
229         GSignondPlugin *plugin)
230 {
231     g_return_if_fail (plugin && GSIGNOND_IS_PLUGIN_REMOTE (plugin));
232     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (plugin);
233
234     gsignond_dbus_remote_plugin_v1_call_cancel (
235             self->priv->dbus_plugin_proxy, NULL, _cancel_async_cb, self);
236 }
237
238 static void
239 _request_initial_async_cb (
240         GObject *object,
241         GAsyncResult *res,
242         gpointer user_data)
243 {
244     GError *error = NULL;
245     GSignondDbusRemotePluginV1 *proxy = GSIGNOND_DBUS_REMOTE_PLUGIN_V1 (object);
246     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (user_data);
247     gsignond_dbus_remote_plugin_v1_call_request_initial_finish (proxy,
248             res, &error);
249     if (error) {
250         gsignond_plugin_error (GSIGNOND_PLUGIN(self), error);
251         g_error_free (error);
252     }
253 }
254
255 static void
256 gsignond_plugin_remote_request_initial (
257     GSignondPlugin *plugin,
258     GSignondSessionData *session_data,
259     GSignondDictionary *identity_method_cache,
260     const gchar *mechanism)
261 {
262     g_return_if_fail (session_data && plugin &&
263             GSIGNOND_IS_PLUGIN_REMOTE (plugin));
264     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (plugin);
265
266     GVariant *data = gsignond_dictionary_to_variant (session_data);
267     GVariant *cache;
268     if (identity_method_cache)
269         cache = gsignond_dictionary_to_variant (identity_method_cache);
270     else {
271         GSignondDictionary* empty_cache = gsignond_dictionary_new();
272         cache = gsignond_dictionary_to_variant (empty_cache);
273         gsignond_dictionary_unref(empty_cache);
274     }
275     gsignond_dbus_remote_plugin_v1_call_request_initial (
276             self->priv->dbus_plugin_proxy, data, cache, mechanism, NULL,
277             _request_initial_async_cb, self);
278 }
279
280 static void
281 _request_async_cb (
282         GObject *object,
283         GAsyncResult *res,
284         gpointer user_data)
285 {
286     GError *error = NULL;
287     GSignondDbusRemotePluginV1 *proxy = GSIGNOND_DBUS_REMOTE_PLUGIN_V1 (object);
288     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (user_data);
289
290     gsignond_dbus_remote_plugin_v1_call_request_finish (proxy, res, &error);
291     if (error) {
292         gsignond_plugin_error (GSIGNOND_PLUGIN(self), error);
293         g_error_free (error);
294     }
295 }
296
297 static void
298 gsignond_plugin_remote_request (
299     GSignondPlugin *plugin,
300     GSignondSessionData *session_data)
301 {
302     g_return_if_fail (session_data && plugin &&
303             GSIGNOND_IS_PLUGIN_REMOTE (plugin));
304     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (plugin);
305
306     GVariant *data = gsignond_dictionary_to_variant (session_data);
307     gsignond_dbus_remote_plugin_v1_call_request (
308             self->priv->dbus_plugin_proxy, data, NULL, _request_async_cb, self);
309 }
310
311 static void
312 _user_action_finished_async_cb (
313         GObject *object,
314         GAsyncResult *res,
315         gpointer user_data)
316 {
317     GError *error = NULL;
318     GSignondDbusRemotePluginV1 *proxy = GSIGNOND_DBUS_REMOTE_PLUGIN_V1 (object);
319     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (user_data);
320
321     gsignond_dbus_remote_plugin_v1_call_user_action_finished_finish (proxy,
322             res, &error);
323     if (error) {
324         gsignond_plugin_error (GSIGNOND_PLUGIN(self), error);
325         g_error_free (error);
326     }
327 }
328
329 static void
330 gsignond_plugin_remote_user_action_finished (
331     GSignondPlugin *plugin,
332     GSignondSignonuiData *signonui_data)
333 {
334     g_return_if_fail (signonui_data && plugin &&
335             GSIGNOND_IS_PLUGIN_REMOTE (plugin));
336     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (plugin);
337
338     GVariant *data = gsignond_dictionary_to_variant (signonui_data);
339     gsignond_dbus_remote_plugin_v1_call_user_action_finished (
340             self->priv->dbus_plugin_proxy, data, NULL,
341             _user_action_finished_async_cb, self);
342 }
343
344 static void
345 _refresh_async_cb (
346         GObject *object,
347         GAsyncResult *res,
348         gpointer user_data)
349 {
350     GError *error = NULL;
351     GSignondDbusRemotePluginV1 *proxy = GSIGNOND_DBUS_REMOTE_PLUGIN_V1 (object);
352     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (user_data);
353
354     gsignond_dbus_remote_plugin_v1_call_refresh_finish (proxy, res, &error);
355     if (error) {
356         gsignond_plugin_error (GSIGNOND_PLUGIN(self), error);
357         g_error_free (error);
358     }
359 }
360
361 static void
362 gsignond_plugin_remote_refresh (
363     GSignondPlugin *plugin,
364     GSignondSignonuiData *signonui_data)
365 {
366     g_return_if_fail (signonui_data && plugin &&
367             GSIGNOND_IS_PLUGIN_REMOTE (plugin));
368     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (plugin);
369
370     GVariant *data = gsignond_dictionary_to_variant (signonui_data);
371     gsignond_dbus_remote_plugin_v1_call_refresh (
372             self->priv->dbus_plugin_proxy, data, NULL, _refresh_async_cb, self);
373 }
374
375 static void
376 gsignond_plugin_remote_interface_init (GSignondPluginInterface *iface)
377 {
378     iface->cancel = gsignond_plugin_remote_cancel;
379     iface->request_initial = gsignond_plugin_remote_request_initial;
380     iface->request = gsignond_plugin_remote_request;
381     iface->user_action_finished = gsignond_plugin_remote_user_action_finished;
382     iface->refresh = gsignond_plugin_remote_refresh;
383 }
384
385 static void
386 _response_cb (
387         GSignondPluginRemote *self,
388         GVariant *session_data,
389         gpointer user_data)
390 {
391     g_return_if_fail (self && GSIGNOND_IS_PLUGIN_REMOTE (self));
392
393     GSignondSessionData *data = (GSignondSessionData *)
394             gsignond_dictionary_new_from_variant (session_data);
395     gsignond_plugin_response (GSIGNOND_PLUGIN(self), data);
396     gsignond_dictionary_unref (data);
397 }
398
399 static void
400 _response_final_cb (
401         GSignondPluginRemote *self,
402         GVariant *session_data,
403         gpointer user_data)
404 {
405     g_return_if_fail (self && GSIGNOND_IS_PLUGIN_REMOTE (self));
406
407     GSignondSessionData *data = (GSignondSessionData *)
408             gsignond_dictionary_new_from_variant (session_data);
409     gsignond_plugin_response_final (GSIGNOND_PLUGIN(self), data);
410     gsignond_dictionary_unref (data);
411 }
412
413 static void
414 _store_cb (
415         GSignondPluginRemote *self,
416         GVariant *session_data,
417         gpointer user_data)
418 {
419     g_return_if_fail (self && GSIGNOND_IS_PLUGIN_REMOTE (self));
420
421     GSignondSessionData *data = (GSignondSessionData *)
422             gsignond_dictionary_new_from_variant (session_data);
423     gsignond_plugin_store (GSIGNOND_PLUGIN(self), data);
424     gsignond_dictionary_unref (data);
425 }
426
427 static void
428 _error_cb (
429         GSignondPluginRemote *self,
430         GVariant *error,
431         gpointer user_data)
432 {
433     g_return_if_fail (self && GSIGNOND_IS_PLUGIN_REMOTE (self));
434     GError *gerror = gsignond_error_new_from_variant (error);
435     gsignond_plugin_error (GSIGNOND_PLUGIN(self), gerror);
436     g_error_free (gerror);
437 }
438
439 static void
440 _user_action_required_cb (
441         GSignondPluginRemote *self,
442         GVariant *ui_data,
443         gpointer user_data)
444 {
445     g_return_if_fail (self && GSIGNOND_IS_PLUGIN_REMOTE (self));
446
447     GSignondSignonuiData *data = (GSignondSignonuiData *)
448             gsignond_dictionary_new_from_variant (ui_data);
449     gsignond_plugin_user_action_required (GSIGNOND_PLUGIN(self), data);
450     gsignond_dictionary_unref (data);
451 }
452
453 static void
454 _refreshed_cb(
455         GSignondPluginRemote *self,
456         GVariant *ui_data,
457         gpointer user_data)
458 {
459     g_return_if_fail (self && GSIGNOND_IS_PLUGIN_REMOTE (self));
460
461     GSignondSignonuiData *data = (GSignondSignonuiData *)
462             gsignond_dictionary_new_from_variant (ui_data);
463     gsignond_plugin_refreshed (GSIGNOND_PLUGIN(self), data);
464     gsignond_dictionary_unref (data);
465 }
466
467 static void
468 _status_changed_cb (
469         GSignondPluginRemote *self,
470         gint status,
471         gchar *message,
472         gpointer user_data)
473 {
474     g_return_if_fail (self && GSIGNOND_IS_PLUGIN_REMOTE (self));
475
476     gsignond_plugin_status_changed (GSIGNOND_PLUGIN(self),
477             (GSignondPluginState)status, message);
478 }
479
480 GSignondPluginRemote *
481 gsignond_plugin_remote_new (
482         const gchar *loader_path,
483         const gchar *plugin_type)
484 {
485     GError *error = NULL;
486     GPid cpid = 0;
487     gchar **argv;
488     gint cin_fd, cout_fd;
489     GSignondPluginRemote *plugin = NULL;
490     GSignondPipeStream *stream = NULL;
491     gboolean ret = FALSE;
492
493     /* This guarantees that writes to a pipe will never cause
494      * a process terminanation via SIGPIPE, and instead a proper
495      * error will be returned */
496     signal(SIGPIPE, SIG_IGN);
497
498     /* Spawn child process */
499     argv = g_new0 (gchar *, 2 + 1);
500     argv[0] = g_strdup(loader_path);
501     argv[1] = g_strdup_printf("--load-plugin=%s",plugin_type);
502     ret = g_spawn_async_with_pipes (NULL, argv, NULL,
503             G_SPAWN_DO_NOT_REAP_CHILD, NULL,
504             NULL, &cpid, &cin_fd, &cout_fd, NULL, &error);
505     g_strfreev (argv);
506     if (ret == FALSE || (kill(cpid, 0) != 0)) {
507         DBG ("failed to start plugind: error %s(%d)", 
508             error ? error->message : "(null)", ret);
509         if (error) g_error_free (error);
510         return NULL;
511     }
512
513     /* Create dbus plugin object */
514     plugin = GSIGNOND_PLUGIN_REMOTE (g_object_new (GSIGNOND_TYPE_PLUGIN_REMOTE,
515             NULL));
516
517     plugin->priv->child_watch_id = g_child_watch_add (cpid,
518             (GChildWatchFunc)_on_child_down_cb, plugin);
519     plugin->priv->cpid = cpid;
520     plugin->priv->is_plugind_up = TRUE;
521
522     /* Create dbus connection */
523     stream = gsignond_pipe_stream_new (cout_fd, cin_fd, TRUE);
524     plugin->priv->connection = g_dbus_connection_new_sync (G_IO_STREAM (stream),
525             NULL, G_DBUS_CONNECTION_FLAGS_NONE, NULL, NULL, NULL);
526     g_object_unref (stream);
527
528     /* Create dbus proxy */
529     plugin->priv->dbus_plugin_proxy =
530             gsignond_dbus_remote_plugin_v1_proxy_new_sync (
531                     plugin->priv->connection,
532                     G_DBUS_PROXY_FLAGS_NONE,
533                     NULL,
534                     GSIGNOND_PLUGIN_OBJECTPATH,
535                     NULL,
536                     &error);
537     if (error) {
538         DBG ("Failed to register object: %s", error->message);
539         g_error_free (error);
540         g_object_unref (plugin);
541         return NULL;
542     }
543     DBG("'%s' object exported(%p)", GSIGNOND_PLUGIN_OBJECTPATH, plugin);
544
545     plugin->priv->signal_response = g_signal_connect_swapped (
546             plugin->priv->dbus_plugin_proxy, "response",
547             G_CALLBACK (_response_cb), plugin);
548     plugin->priv->signal_response_final = g_signal_connect_swapped (
549             plugin->priv->dbus_plugin_proxy, "response-final",
550             G_CALLBACK(_response_final_cb), plugin);
551     plugin->priv->signal_store = g_signal_connect_swapped (
552             plugin->priv->dbus_plugin_proxy, "store",
553             G_CALLBACK(_store_cb), plugin);
554     plugin->priv->signal_error = g_signal_connect_swapped (
555             plugin->priv->dbus_plugin_proxy, "error",
556             G_CALLBACK(_error_cb), plugin);
557     plugin->priv->signal_user_action_required = g_signal_connect_swapped (
558             plugin->priv->dbus_plugin_proxy, "user-action-required",
559             G_CALLBACK(_user_action_required_cb), plugin);
560     plugin->priv->signal_refreshed = g_signal_connect_swapped (
561             plugin->priv->dbus_plugin_proxy, "refreshed",
562             G_CALLBACK(_refreshed_cb), plugin);
563     plugin->priv->signal_status_changed = g_signal_connect_swapped (
564             plugin->priv->dbus_plugin_proxy, "status-changed",
565             G_CALLBACK(_status_changed_cb), plugin);
566
567     return plugin;
568 }
569