7d6291a7c5eafaf264dd78a83428b9e956da24cf
[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 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 "gsignond/gsignond-log.h"
27 #include "gsignond/gsignond-error.h"
28 #include "gsignond/gsignond-plugin-interface.h"
29 #include "common/gsignond-pipe-stream.h"
30 #include "daemon/dbus/gsignond-dbus.h"
31 #include "gsignond-plugin-remote-private.h"
32 #include "gsignond-plugin-remote.h"
33
34 enum
35 {
36     PROP_0,
37     PROP_TYPE,
38     PROP_MECHANISMS,
39     N_PROPERTIES
40 };
41
42 static void
43 gsignond_plugin_remote_interface_init (
44         GSignondPluginInterface *iface);
45
46 G_DEFINE_TYPE_WITH_CODE (GSignondPluginRemote, gsignond_plugin_remote,
47         G_TYPE_OBJECT, G_IMPLEMENT_INTERFACE (GSIGNOND_TYPE_PLUGIN,
48                 gsignond_plugin_remote_interface_init));
49
50 #define GSIGNOND_PLUGIN_REMOTE_GET_PRIV(obj) \
51         G_TYPE_INSTANCE_GET_PRIVATE ((obj), GSIGNOND_TYPE_PLUGIN_REMOTE, \
52         GSignondPluginRemotePrivate)
53
54 #define GSIGNOND_PLUGIND_NAME "gsignond-plugind"
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     if (!g_source_is_destroyed (g_main_current_source ())) {
70         if (plugin->priv->main_loop && g_main_loop_is_running (
71                 plugin->priv->main_loop)) {
72             g_main_loop_quit (plugin->priv->main_loop);
73         }
74         plugin->priv->is_plugind_up = FALSE;
75     }
76
77     if (plugin->priv->unref_in_down_cb) {
78         plugin->priv->unref_in_down_cb = FALSE;
79         g_object_unref (plugin);
80     }
81 }
82
83 static gboolean
84 _on_child_status_cb (
85         GIOChannel *channel,
86         GIOCondition condition,
87         gpointer data)
88 {
89     GSignondPluginRemote *plugin = GSIGNOND_PLUGIN_REMOTE (data);
90     DBG ("Plugind(%p) with pid (%d) status cb", plugin, plugin->priv->cpid);
91
92     if (plugin->priv->main_loop && g_main_loop_is_running (
93             plugin->priv->main_loop)) {
94         g_main_loop_quit (plugin->priv->main_loop);
95     }
96
97     if (g_io_channel_get_flags (channel) & G_IO_FLAG_IS_READABLE) {
98         gchar string[1];
99         GError *error = NULL;
100         gsize bytes_read = 0;
101         GIOStatus status = g_io_channel_read_chars (channel, string, 1,
102                 &bytes_read, &error);
103         if (status == G_IO_STATUS_NORMAL && error == NULL) {
104             if (*string == '1') {
105                 DBG ("Plugind is UP and READY");
106                 plugin->priv->is_plugind_up = TRUE;
107             } else if (*string == '0') {
108                 DBG ("Plugind is DOWN");
109                 plugin->priv->is_plugind_up = FALSE;
110             }
111         }
112         if (error) {
113             g_error_free (error);
114         }
115     }
116
117     return FALSE;
118 }
119
120 static gboolean
121 _on_loop_timeout_cb (gpointer data)
122 {
123     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (data);
124
125     if (g_main_loop_is_running (self->priv->main_loop)) {
126         g_main_loop_quit (self->priv->main_loop);
127     }
128
129     return FALSE;
130 }
131
132 static guint
133 _create_main_loop_with_timeout (
134         GSignondPluginRemote *self,
135         GMainContext *context,
136         guint timeout)
137 {
138     guint timer_id = 0;
139     GSource *timer = g_timeout_source_new (timeout);
140     g_source_set_callback (timer, (GSourceFunc) _on_loop_timeout_cb, self,
141             NULL);
142     //g_source_attach increments the ref count of the source
143     timer_id = g_source_attach (timer, context);
144     g_source_unref (timer);
145
146     self->priv->main_loop = g_main_loop_new (context, TRUE);
147     //loop has ref'd the context
148     if (context) {
149         g_main_context_unref (context);
150     }
151     return timer_id;
152 }
153
154 static void
155 _run_main_loop (
156         GSignondPluginRemote *self)
157 {
158     if (self->priv->main_loop) {
159         g_main_loop_run (self->priv->main_loop);
160         /* attached context gets freed as well, which internally destroys all
161          * the attached sources */
162         g_main_loop_unref (self->priv->main_loop);
163         self->priv->main_loop = NULL;
164     }
165 }
166
167 static void
168 _run_main_loop_with_timeout (
169         GSignondPluginRemote *self,
170         guint timeout)
171 {
172     guint timer_id = _create_main_loop_with_timeout (self, NULL, timeout);
173     _run_main_loop (self);
174     g_source_remove (timer_id);
175 }
176
177 static void
178 _run_main_loop_with_ready_watch (
179         GSignondPluginRemote *self,
180         gint fd,
181         guint timeout)
182 {
183     GIOChannel *ready_watch = NULL;
184     GSource *up_source = NULL;
185
186     GMainContext *context = g_main_context_new ();
187     _create_main_loop_with_timeout (self, context, timeout);
188
189     ready_watch = g_io_channel_unix_new (fd);
190     up_source = g_io_create_watch (ready_watch, G_IO_IN | G_IO_HUP);
191     g_source_set_callback (up_source, (GSourceFunc)_on_child_status_cb, self,
192             NULL);
193     g_source_attach (up_source, context);
194     g_source_unref (up_source);
195
196     _run_main_loop (self);
197
198     g_io_channel_unref (ready_watch);
199 }
200
201 static void
202 gsignond_plugin_remote_set_property (
203         GObject *object,
204         guint property_id,
205         const GValue *value,
206         GParamSpec *pspec)
207 {
208     switch (property_id) {
209         default:
210             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
211     }
212 }
213
214 static void
215 gsignond_plugin_remote_get_property (
216         GObject *object,
217         guint property_id,
218         GValue *value,
219         GParamSpec *pspec)
220 {
221     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (object);
222
223     switch (property_id) {
224         case PROP_TYPE: {
225             if (!self->priv->plugin_type) {
226                 GError *error = NULL;
227                 gsignond_dbus_remote_plugin_call_get_info_sync (
228                     self->priv->dbus_plugin_proxy, &self->priv->plugin_type,
229                     &self->priv->plugin_mechanisms, NULL, &error);
230                 if (error) {
231                     DBG ("Plugin type retrieval error :: %s", error->message);
232                     g_error_free (error);
233                     if (self->priv->plugin_type) {
234                         g_free (self->priv->plugin_type);
235                         self->priv->plugin_type = NULL;
236                     }
237                 }
238             }
239             g_value_set_string (value, self->priv->plugin_type);
240             break;
241         }
242         case PROP_MECHANISMS: {
243             if (!self->priv->plugin_mechanisms) {
244                 GError *error = NULL;
245                 gsignond_dbus_remote_plugin_call_get_info_sync (
246                     self->priv->dbus_plugin_proxy, &self->priv->plugin_type,
247                     &self->priv->plugin_mechanisms, NULL, &error);
248                 if (error) {
249                     DBG ("Plugin mechanisms retrieval error :: %s",
250                             error->message);
251                     g_error_free (error);
252                     if (self->priv->plugin_mechanisms) {
253                         g_strfreev (self->priv->plugin_mechanisms);
254                         self->priv->plugin_mechanisms = NULL;
255                     }
256                 }
257             }
258             g_value_set_boxed (value, self->priv->plugin_mechanisms);
259             break;
260         }
261         default:
262             G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
263     }
264
265 }
266
267 static void
268 gsignond_plugin_remote_dispose (GObject *object)
269 {
270     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (object);
271
272     self->priv->unref_in_down_cb = FALSE;
273
274     if (self->priv->main_loop) {
275         if (g_main_loop_is_running (self->priv->main_loop)) {
276             g_main_loop_quit (self->priv->main_loop);
277         }
278         g_main_loop_unref (self->priv->main_loop);
279         self->priv->main_loop = NULL;
280     }
281
282     if (self->priv->cpid > 0 && self->priv->is_plugind_up) {
283         DBG ("Send SIGTERM to Plugind");
284         kill (self->priv->cpid, SIGTERM);
285         _run_main_loop_with_timeout (self, 1000); //1 sec
286
287         if (kill (self->priv->cpid, 0) == 0) {
288             WARN ("Plugind have to be killed with SIGKILL");
289             kill (self->priv->cpid, SIGKILL);
290             _run_main_loop_with_timeout (self, 1000); //1 sec
291         }
292
293         if (self->priv->is_plugind_up) {
294             WARN ("Plugind did not exit even after SIGKILL");
295         } else {
296             DBG ("Plugind DESTROYED");
297         }
298     }
299     self->priv->cpid = 0;
300
301     if (self->priv->child_watch_id > 0) {
302         g_source_remove (self->priv->child_watch_id);
303         self->priv->child_watch_id = 0;
304     }
305
306     if (self->priv->connection) {
307         g_object_unref (self->priv->connection);
308         self->priv->connection = NULL;
309     }
310
311     if (self->priv->dbus_plugin_proxy) {
312         g_signal_handler_disconnect (self->priv->dbus_plugin_proxy,
313                 self->priv->signal_response);
314         g_signal_handler_disconnect (self->priv->dbus_plugin_proxy,
315                 self->priv->signal_response_final);
316         g_signal_handler_disconnect (self->priv->dbus_plugin_proxy,
317                 self->priv->signal_store);
318         g_signal_handler_disconnect (self->priv->dbus_plugin_proxy,
319                 self->priv->signal_error);
320         g_signal_handler_disconnect (self->priv->dbus_plugin_proxy,
321                 self->priv->signal_user_action_required);
322         g_signal_handler_disconnect (self->priv->dbus_plugin_proxy,
323                 self->priv->signal_refreshed);
324         g_signal_handler_disconnect (self->priv->dbus_plugin_proxy,
325                 self->priv->signal_status_changed);
326         g_object_unref (self->priv->dbus_plugin_proxy);
327         self->priv->dbus_plugin_proxy = NULL;
328     }
329
330     if (self->priv->err_watch_ch) {
331         g_io_channel_shutdown (self->priv->err_watch_ch, FALSE, NULL);
332         g_io_channel_unref (self->priv->err_watch_ch);
333         self->priv->err_watch_ch = NULL;
334         if (self->priv->err_watch_id) {
335             g_source_remove (self->priv->err_watch_id);
336         }
337     }
338
339     G_OBJECT_CLASS (gsignond_plugin_remote_parent_class)->dispose (object);
340 }
341
342 static void
343 gsignond_plugin_remote_finalize (GObject *object)
344 {
345     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (object);
346
347     if (self->priv->plugin_type) {
348         g_free (self->priv->plugin_type);
349         self->priv->plugin_type = NULL;
350     }
351
352     if (self->priv->plugin_mechanisms) {
353         g_strfreev (self->priv->plugin_mechanisms);
354         self->priv->plugin_mechanisms = NULL;
355     }
356
357     G_OBJECT_CLASS (gsignond_plugin_remote_parent_class)->finalize (object);
358 }
359
360 static void
361 gsignond_plugin_remote_class_init (GSignondPluginRemoteClass *klass)
362 {
363     GObjectClass* object_class = G_OBJECT_CLASS (klass);
364
365     g_type_class_add_private (object_class,
366             sizeof (GSignondPluginRemotePrivate));
367
368     object_class->get_property = gsignond_plugin_remote_get_property;
369     object_class->set_property = gsignond_plugin_remote_set_property;
370     object_class->dispose = gsignond_plugin_remote_dispose;
371     object_class->finalize = gsignond_plugin_remote_finalize;
372
373     g_object_class_override_property (object_class, PROP_TYPE, "type");
374     g_object_class_override_property (object_class, PROP_MECHANISMS,
375             "mechanisms");
376
377 }
378
379 static void
380 gsignond_plugin_remote_init (GSignondPluginRemote *self)
381 {
382     self->priv = GSIGNOND_PLUGIN_REMOTE_GET_PRIV(self);
383
384     self->priv->connection = NULL;
385     self->priv->dbus_plugin_proxy = NULL;
386     self->priv->plugin_type = NULL;
387     self->priv->plugin_mechanisms = NULL;
388     self->priv->cpid = 0;
389
390     self->priv->err_watch_ch = NULL;
391     self->priv->child_watch_id = 0;
392
393     self->priv->main_loop = NULL;
394     self->priv->is_plugind_up = FALSE;
395     self->priv->unref_in_down_cb = FALSE;
396 }
397
398 static void
399 _cancel_async_cb (
400         GObject *object,
401         GAsyncResult *res,
402         gpointer user_data)
403 {
404     GError *error = NULL;
405     GSignondDbusRemotePlugin *proxy = GSIGNOND_DBUS_REMOTE_PLUGIN (object);
406     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (user_data);
407
408     gsignond_dbus_remote_plugin_call_cancel_finish (proxy, res, &error);
409     if (error) {
410         gsignond_plugin_error (GSIGNOND_PLUGIN(self), error);
411         g_error_free (error);
412     }
413 }
414
415 static void
416 gsignond_plugin_remote_cancel (
417         GSignondPlugin *plugin)
418 {
419     g_return_if_fail (plugin && GSIGNOND_IS_PLUGIN_REMOTE (plugin));
420     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (plugin);
421
422     gsignond_dbus_remote_plugin_call_cancel (
423             self->priv->dbus_plugin_proxy, NULL, _cancel_async_cb, self);
424 }
425
426 static void
427 _request_initial_async_cb (
428         GObject *object,
429         GAsyncResult *res,
430         gpointer user_data)
431 {
432     GError *error = NULL;
433     GSignondDbusRemotePlugin *proxy = GSIGNOND_DBUS_REMOTE_PLUGIN (object);
434     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (user_data);
435     gsignond_dbus_remote_plugin_call_request_initial_finish (proxy,
436             res, &error);
437     if (error) {
438         gsignond_plugin_error (GSIGNOND_PLUGIN(self), error);
439         g_error_free (error);
440     }
441 }
442
443 static void
444 gsignond_plugin_remote_request_initial (
445     GSignondPlugin *plugin,
446     GSignondSessionData *session_data,
447     GSignondDictionary *identity_method_cache,
448     const gchar *mechanism)
449 {
450     g_return_if_fail (session_data && plugin &&
451             GSIGNOND_IS_PLUGIN_REMOTE (plugin));
452     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (plugin);
453
454     GVariant *data = gsignond_dictionary_to_variant (session_data);
455     GVariant *cache;
456     if (identity_method_cache)
457         cache = gsignond_dictionary_to_variant (identity_method_cache);
458     else {
459         GSignondDictionary* empty_cache = gsignond_dictionary_new();
460         cache = gsignond_dictionary_to_variant (empty_cache);
461         gsignond_dictionary_unref(empty_cache);
462     }
463     gsignond_dbus_remote_plugin_call_request_initial (
464             self->priv->dbus_plugin_proxy, data, cache, mechanism, NULL,
465             _request_initial_async_cb, self);
466 }
467
468 static void
469 _request_async_cb (
470         GObject *object,
471         GAsyncResult *res,
472         gpointer user_data)
473 {
474     GError *error = NULL;
475     GSignondDbusRemotePlugin *proxy = GSIGNOND_DBUS_REMOTE_PLUGIN (object);
476     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (user_data);
477
478     gsignond_dbus_remote_plugin_call_request_finish (proxy, res, &error);
479     if (error) {
480         gsignond_plugin_error (GSIGNOND_PLUGIN(self), error);
481         g_error_free (error);
482     }
483 }
484
485 static void
486 gsignond_plugin_remote_request (
487     GSignondPlugin *plugin,
488     GSignondSessionData *session_data)
489 {
490     g_return_if_fail (session_data && plugin &&
491             GSIGNOND_IS_PLUGIN_REMOTE (plugin));
492     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (plugin);
493
494     GVariant *data = gsignond_dictionary_to_variant (session_data);
495     gsignond_dbus_remote_plugin_call_request (
496             self->priv->dbus_plugin_proxy, data, NULL, _request_async_cb, self);
497 }
498
499 static void
500 _user_action_finished_async_cb (
501         GObject *object,
502         GAsyncResult *res,
503         gpointer user_data)
504 {
505     GError *error = NULL;
506     GSignondDbusRemotePlugin *proxy = GSIGNOND_DBUS_REMOTE_PLUGIN (object);
507     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (user_data);
508
509     gsignond_dbus_remote_plugin_call_user_action_finished_finish (proxy,
510             res, &error);
511     if (error) {
512         gsignond_plugin_error (GSIGNOND_PLUGIN(self), error);
513         g_error_free (error);
514     }
515 }
516
517 static void
518 gsignond_plugin_remote_user_action_finished (
519     GSignondPlugin *plugin,
520     GSignondSignonuiData *signonui_data)
521 {
522     g_return_if_fail (signonui_data && plugin &&
523             GSIGNOND_IS_PLUGIN_REMOTE (plugin));
524     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (plugin);
525
526     GVariant *data = gsignond_dictionary_to_variant (signonui_data);
527     gsignond_dbus_remote_plugin_call_user_action_finished (
528             self->priv->dbus_plugin_proxy, data, NULL,
529             _user_action_finished_async_cb, self);
530 }
531
532 static void
533 _refresh_async_cb (
534         GObject *object,
535         GAsyncResult *res,
536         gpointer user_data)
537 {
538     GError *error = NULL;
539     GSignondDbusRemotePlugin *proxy = GSIGNOND_DBUS_REMOTE_PLUGIN (object);
540     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (user_data);
541
542     gsignond_dbus_remote_plugin_call_refresh_finish (proxy, res, &error);
543     if (error) {
544         gsignond_plugin_error (GSIGNOND_PLUGIN(self), error);
545         g_error_free (error);
546     }
547 }
548
549 static void
550 gsignond_plugin_remote_refresh (
551     GSignondPlugin *plugin,
552     GSignondSignonuiData *signonui_data)
553 {
554     g_return_if_fail (signonui_data && plugin &&
555             GSIGNOND_IS_PLUGIN_REMOTE (plugin));
556     GSignondPluginRemote *self = GSIGNOND_PLUGIN_REMOTE (plugin);
557
558     GVariant *data = gsignond_dictionary_to_variant (signonui_data);
559     gsignond_dbus_remote_plugin_call_refresh (
560             self->priv->dbus_plugin_proxy, data, NULL, _refresh_async_cb, self);
561 }
562
563 static void
564 gsignond_plugin_remote_interface_init (GSignondPluginInterface *iface)
565 {
566     iface->cancel = gsignond_plugin_remote_cancel;
567     iface->request_initial = gsignond_plugin_remote_request_initial;
568     iface->request = gsignond_plugin_remote_request;
569     iface->user_action_finished = gsignond_plugin_remote_user_action_finished;
570     iface->refresh = gsignond_plugin_remote_refresh;
571 }
572
573 static void
574 _response_cb (
575         GSignondPluginRemote *self,
576         GVariant *session_data,
577         gpointer user_data)
578 {
579     g_return_if_fail (self && GSIGNOND_IS_PLUGIN_REMOTE (self));
580
581     GSignondSessionData *data = (GSignondSessionData *)
582             gsignond_dictionary_new_from_variant (session_data);
583     gsignond_plugin_response (GSIGNOND_PLUGIN(self), data);
584     gsignond_dictionary_unref (data);
585 }
586
587 static void
588 _response_final_cb (
589         GSignondPluginRemote *self,
590         GVariant *session_data,
591         gpointer user_data)
592 {
593     g_return_if_fail (self && GSIGNOND_IS_PLUGIN_REMOTE (self));
594
595     GSignondSessionData *data = (GSignondSessionData *)
596             gsignond_dictionary_new_from_variant (session_data);
597     gsignond_plugin_response_final (GSIGNOND_PLUGIN(self), data);
598     gsignond_dictionary_unref (data);
599 }
600
601 static void
602 _store_cb (
603         GSignondPluginRemote *self,
604         GVariant *session_data,
605         gpointer user_data)
606 {
607     g_return_if_fail (self && GSIGNOND_IS_PLUGIN_REMOTE (self));
608
609     GSignondSessionData *data = (GSignondSessionData *)
610             gsignond_dictionary_new_from_variant (session_data);
611     gsignond_plugin_store (GSIGNOND_PLUGIN(self), data);
612     gsignond_dictionary_unref (data);
613 }
614
615 static void
616 _error_cb (
617         GSignondPluginRemote *self,
618         GVariant *error,
619         gpointer user_data)
620 {
621     g_return_if_fail (self && GSIGNOND_IS_PLUGIN_REMOTE (self));
622     GError *gerror = gsignond_error_new_from_variant (error);
623     gsignond_plugin_error (GSIGNOND_PLUGIN(self), gerror);
624     g_error_free (gerror);
625 }
626
627 static void
628 _user_action_required_cb (
629         GSignondPluginRemote *self,
630         GVariant *ui_data,
631         gpointer user_data)
632 {
633     g_return_if_fail (self && GSIGNOND_IS_PLUGIN_REMOTE (self));
634
635     GSignondSignonuiData *data = (GSignondSignonuiData *)
636             gsignond_dictionary_new_from_variant (ui_data);
637     gsignond_plugin_user_action_required (GSIGNOND_PLUGIN(self), data);
638     gsignond_dictionary_unref (data);
639 }
640
641 static void
642 _refreshed_cb(
643         GSignondPluginRemote *self,
644         GVariant *ui_data,
645         gpointer user_data)
646 {
647     g_return_if_fail (self && GSIGNOND_IS_PLUGIN_REMOTE (self));
648
649     GSignondSignonuiData *data = (GSignondSignonuiData *)
650             gsignond_dictionary_new_from_variant (ui_data);
651     gsignond_plugin_refreshed (GSIGNOND_PLUGIN(self), data);
652     gsignond_dictionary_unref (data);
653 }
654
655 static void
656 _status_changed_cb (
657         GSignondPluginRemote *self,
658         gint status,
659         gchar *message,
660         gpointer user_data)
661 {
662     g_return_if_fail (self && GSIGNOND_IS_PLUGIN_REMOTE (self));
663
664     gsignond_plugin_status_changed (GSIGNOND_PLUGIN(self),
665             (GSignondPluginState)status, message);
666 }
667
668 GSignondPluginRemote *
669 gsignond_plugin_remote_new (
670         GSignondConfig *config,
671         const gchar *plugin_type)
672 {
673     GError *error = NULL;
674     GPid cpid = 0;
675     gchar **argv;
676     gint cin_fd, cout_fd;
677     GSignondPluginRemote *plugin = NULL;
678     GSignondPipeStream *stream = NULL;
679     gboolean ret = FALSE;
680
681     /* This guarantees that writes to a pipe will never cause
682      * a process terminanation via SIGPIPE, and instead a proper
683      * error will be returned */
684     signal(SIGPIPE, SIG_IGN);
685
686     /* Spawn child process */
687     argv = g_malloc0 ((3 + 1) * sizeof (gchar *));
688     argv[0] = g_build_filename (gsignond_config_get_string (config,
689             GSIGNOND_CONFIG_GENERAL_BIN_DIR), GSIGNOND_PLUGIND_NAME, NULL);
690     argv[1] = g_module_build_path (gsignond_config_get_string (config,
691             GSIGNOND_CONFIG_GENERAL_PLUGINS_DIR), plugin_type);
692     argv[2] = g_strdup(plugin_type);
693     ret = g_spawn_async_with_pipes (NULL, argv, NULL,
694             G_SPAWN_DO_NOT_REAP_CHILD, NULL,
695             NULL, &cpid, &cin_fd, &cout_fd, NULL, &error);
696     g_strfreev (argv);
697     if (ret == FALSE || (kill(cpid, 0) != 0)) {
698         DBG ("failed to start plugind: error %s(%d)", 
699             error ? error->message : "(null)", ret);
700         if (error) g_error_free (error);
701         return NULL;
702     }
703
704     /* Create dbus plugin object */
705     plugin = GSIGNOND_PLUGIN_REMOTE (g_object_new (GSIGNOND_TYPE_PLUGIN_REMOTE,
706             NULL));
707
708     plugin->priv->child_watch_id = g_child_watch_add (cpid,
709             (GChildWatchFunc)_on_child_down_cb, plugin);
710     plugin->priv->cpid = cpid;
711
712     _run_main_loop_with_ready_watch (plugin, cout_fd, 1000);
713     if (!plugin->priv->is_plugind_up) {
714         DBG ("Plugind (%s) with pid %d process failed to start up", plugin_type,
715                 cpid);
716         /* moved unref'ng into the cb to avoid zombies */
717         plugin->priv->unref_in_down_cb = TRUE;
718         return NULL;
719     }
720
721     /* Create dbus connection */
722     stream = gsignond_pipe_stream_new (cout_fd, cin_fd, TRUE);
723     plugin->priv->connection = g_dbus_connection_new_sync (G_IO_STREAM (stream),
724             NULL, G_DBUS_CONNECTION_FLAGS_NONE, NULL, NULL, NULL);
725     g_object_unref (stream);
726
727     /* Create dbus proxy */
728     plugin->priv->dbus_plugin_proxy =
729             gsignond_dbus_remote_plugin_proxy_new_sync (
730                     plugin->priv->connection,
731                     G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES,
732                     NULL,
733                     GSIGNOND_PLUGIN_OBJECTPATH,
734                     NULL,
735                     &error);
736     if (error) {
737         DBG ("Failed to register object: %s", error->message);
738         g_error_free (error);
739         g_object_unref (plugin);
740         return NULL;
741     }
742     DBG("'%s' object exported(%p)", GSIGNOND_PLUGIN_OBJECTPATH, plugin);
743
744     plugin->priv->signal_response = g_signal_connect_swapped (
745             plugin->priv->dbus_plugin_proxy, "response",
746             G_CALLBACK (_response_cb), plugin);
747     plugin->priv->signal_response_final = g_signal_connect_swapped (
748             plugin->priv->dbus_plugin_proxy, "response-final",
749             G_CALLBACK(_response_final_cb), plugin);
750     plugin->priv->signal_store = g_signal_connect_swapped (
751             plugin->priv->dbus_plugin_proxy, "store",
752             G_CALLBACK(_store_cb), plugin);
753     plugin->priv->signal_error = g_signal_connect_swapped (
754             plugin->priv->dbus_plugin_proxy, "error",
755             G_CALLBACK(_error_cb), plugin);
756     plugin->priv->signal_user_action_required = g_signal_connect_swapped (
757             plugin->priv->dbus_plugin_proxy, "user-action-required",
758             G_CALLBACK(_user_action_required_cb), plugin);
759     plugin->priv->signal_refreshed = g_signal_connect_swapped (
760             plugin->priv->dbus_plugin_proxy, "refreshed",
761             G_CALLBACK(_refreshed_cb), plugin);
762     plugin->priv->signal_status_changed = g_signal_connect_swapped (
763             plugin->priv->dbus_plugin_proxy, "status-changed",
764             G_CALLBACK(_status_changed_cb), plugin);
765
766     return plugin;
767 }
768