876d479817b2d75df4e3412d943ed6db179ff6ef
[NetworkManager.git] / libnm / nm-vpn-service-plugin.c
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2 /*
3  * This library is free software; you can redistribute it and/or
4  * modify it under the terms of the GNU Lesser General Public
5  * License as published by the Free Software Foundation; either
6  * version 2 of the License, or (at your option) any later version.
7  *
8  * This library is distributed in the hope that it will be useful,
9  * but WITHOUT ANY WARRANTY; without even the implied warranty of
10  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11  * Lesser General Public License for more details.
12  *
13  * You should have received a copy of the GNU Lesser General Public
14  * License along with this library; if not, write to the
15  * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
16  * Boston, MA 02110-1301 USA.
17  *
18  * Copyright 2007 - 2008 Novell, Inc.
19  * Copyright 2007 - 2015 Red Hat, Inc.
20  */
21
22 #include "nm-default.h"
23
24 #include "nm-vpn-service-plugin.h"
25
26 #include <errno.h>
27 #include <signal.h>
28 #include <stdlib.h>
29
30 #include "nm-enum-types.h"
31 #include "nm-utils.h"
32 #include "nm-connection.h"
33 #include "nm-dbus-helpers.h"
34 #include "nm-core-internal.h"
35 #include "nm-simple-connection.h"
36
37 #include "nmdbus-vpn-plugin.h"
38
39 #define NM_VPN_SERVICE_PLUGIN_QUIT_TIMER    180
40
41 static void nm_vpn_service_plugin_initable_iface_init (GInitableIface *iface);
42
43 G_DEFINE_ABSTRACT_TYPE_WITH_CODE (NMVpnServicePlugin, nm_vpn_service_plugin, G_TYPE_OBJECT,
44                                   G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_vpn_service_plugin_initable_iface_init);
45                                   )
46
47 typedef struct {
48         NMVpnServiceState state;
49
50         /* DBUS-y stuff */
51         GDBusConnection *connection;
52         NMDBusVpnPlugin *dbus_vpn_service_plugin;
53         char *dbus_service_name;
54         gboolean dbus_watch_peer;
55
56         /* Temporary stuff */
57         guint connect_timer;
58         guint quit_timer;
59         guint fail_stop_id;
60         guint peer_watch_id;
61         gboolean interactive;
62
63         gboolean got_config;
64         gboolean has_ip4, got_ip4;
65         gboolean has_ip6, got_ip6;
66
67         /* Config stuff copied from config to ip4config */
68         GVariant *banner, *tundev, *gateway, *mtu;
69 } NMVpnServicePluginPrivate;
70
71 #define NM_VPN_SERVICE_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_SERVICE_PLUGIN, NMVpnServicePluginPrivate))
72
73 enum {
74         STATE_CHANGED,
75         CONFIG,
76         IP4_CONFIG,
77         IP6_CONFIG,
78         LOGIN_BANNER,
79         FAILURE,
80         QUIT,
81         SECRETS_REQUIRED,
82
83         LAST_SIGNAL
84 };
85
86 static guint signals[LAST_SIGNAL] = { 0 };
87
88 enum {
89         PROP_0,
90         PROP_DBUS_SERVICE_NAME,
91         PROP_DBUS_WATCH_PEER,
92         PROP_STATE,
93
94         LAST_PROP
95 };
96
97 static GSList *active_plugins = NULL;
98
99
100 static void
101 nm_vpn_service_plugin_set_connection (NMVpnServicePlugin *plugin,
102                                       GDBusConnection *connection)
103 {
104         NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
105
106         g_clear_object (&priv->connection);
107
108         if (connection)
109                 priv->connection = g_object_ref (connection);
110 }
111
112 /**
113  * nm_vpn_service_plugin_get_connection:
114  *
115  * Returns: (transfer full):
116  *
117  * Since: 1.2
118  */
119 GDBusConnection *
120 nm_vpn_service_plugin_get_connection (NMVpnServicePlugin *plugin)
121 {
122         GDBusConnection *connection;
123
124         g_return_val_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin), NULL);
125
126         connection = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin)->connection;
127
128         if (connection)
129                 g_object_ref (connection);
130
131         return connection;
132 }
133
134 static NMVpnServiceState
135 nm_vpn_service_plugin_get_state (NMVpnServicePlugin *plugin)
136 {
137         g_return_val_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin), NM_VPN_SERVICE_STATE_UNKNOWN);
138
139         return NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin)->state;
140 }
141
142 static void
143 nm_vpn_service_plugin_set_state (NMVpnServicePlugin *plugin,
144                                  NMVpnServiceState state)
145 {
146         NMVpnServicePluginPrivate *priv;
147
148         g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin));
149
150         priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
151         if (priv->state != state) {
152                 priv->state = state;
153                 g_signal_emit (plugin, signals[STATE_CHANGED], 0, state);
154                 nmdbus_vpn_plugin_emit_state_changed (priv->dbus_vpn_service_plugin, state);
155         }
156 }
157
158 void
159 nm_vpn_service_plugin_set_login_banner (NMVpnServicePlugin *plugin,
160                                         const char *banner)
161 {
162         NMVpnServicePluginPrivate *priv;
163
164         g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin));
165         g_return_if_fail (banner != NULL);
166
167         priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
168         g_signal_emit (plugin, signals[LOGIN_BANNER], 0, banner);
169         nmdbus_vpn_plugin_emit_login_banner (priv->dbus_vpn_service_plugin, banner);
170 }
171
172 static void
173 _emit_failure (NMVpnServicePlugin *plugin,
174                NMVpnPluginFailure reason)
175 {
176         NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
177
178         g_signal_emit (plugin, signals[FAILURE], 0, reason);
179         nmdbus_vpn_plugin_emit_failure (priv->dbus_vpn_service_plugin, reason);
180 }
181
182 void
183 nm_vpn_service_plugin_failure (NMVpnServicePlugin *plugin,
184                                NMVpnPluginFailure reason)
185 {
186         g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin));
187
188         _emit_failure (plugin, reason);
189         nm_vpn_service_plugin_disconnect (plugin, NULL);
190 }
191
192 gboolean
193 nm_vpn_service_plugin_disconnect (NMVpnServicePlugin *plugin, GError **err)
194 {
195         NMVpnServicePluginPrivate *priv;
196         gboolean ret = FALSE;
197         NMVpnServiceState state;
198
199         g_return_val_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin), FALSE);
200
201         priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
202         state = nm_vpn_service_plugin_get_state (plugin);
203         switch (state) {
204         case NM_VPN_SERVICE_STATE_STOPPING:
205                 g_set_error (err,
206                              NM_VPN_PLUGIN_ERROR,
207                              NM_VPN_PLUGIN_ERROR_STOPPING_IN_PROGRESS,
208                              "%s",
209                              "Could not process the request because the VPN connection is already being stopped.");
210                 break;
211         case NM_VPN_SERVICE_STATE_STOPPED:
212                 g_set_error (err,
213                              NM_VPN_PLUGIN_ERROR,
214                              NM_VPN_PLUGIN_ERROR_ALREADY_STOPPED,
215                              "%s",
216                              "Could not process the request because no VPN connection was active.");
217                 break;
218         case NM_VPN_SERVICE_STATE_STARTING:
219                 _emit_failure (plugin, NM_VPN_PLUGIN_FAILURE_CONNECT_FAILED);
220                 /* fallthru */
221         case NM_VPN_SERVICE_STATE_STARTED:
222                 nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPING);
223                 ret = NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->disconnect (plugin, err);
224                 nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STOPPED);
225                 break;
226         case NM_VPN_SERVICE_STATE_INIT:
227                 ret = TRUE;
228                 break;
229
230         default:
231                 g_warning ("Unhandled VPN service state %d", state);
232                 g_assert_not_reached ();
233                 break;
234         }
235
236         return ret;
237 }
238
239 static void
240 nm_vpn_service_plugin_emit_quit (NMVpnServicePlugin *plugin)
241 {
242         g_signal_emit (plugin, signals[QUIT], 0);
243 }
244
245 static gboolean
246 connect_timer_expired (gpointer data)
247 {
248         NMVpnServicePlugin *plugin = NM_VPN_SERVICE_PLUGIN (data);
249         GError *err = NULL;
250
251         NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin)->connect_timer = 0;
252         g_message ("Connect timer expired, disconnecting.");
253         nm_vpn_service_plugin_disconnect (plugin, &err);
254         if (err) {
255                 g_warning ("Disconnect failed: %s", err->message);
256                 g_error_free (err);
257         }
258
259         return G_SOURCE_REMOVE;
260 }
261
262 static gboolean
263 quit_timer_expired (gpointer data)
264 {
265         NMVpnServicePlugin *self = NM_VPN_SERVICE_PLUGIN (data);
266
267         NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (self)->quit_timer = 0;
268         nm_vpn_service_plugin_emit_quit (self);
269         return G_SOURCE_REMOVE;
270 }
271
272 static void
273 schedule_quit_timer (NMVpnServicePlugin *self)
274 {
275         NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (self);
276
277         nm_clear_g_source (&priv->quit_timer);
278         priv->quit_timer = g_timeout_add_seconds (NM_VPN_SERVICE_PLUGIN_QUIT_TIMER,
279                                                   quit_timer_expired,
280                                                   self);
281 }
282
283 static gboolean
284 fail_stop (gpointer data)
285 {
286         NMVpnServicePlugin *self = NM_VPN_SERVICE_PLUGIN (data);
287
288         NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (self)->fail_stop_id = 0;
289         nm_vpn_service_plugin_set_state (self, NM_VPN_SERVICE_STATE_STOPPED);
290         return G_SOURCE_REMOVE;
291 }
292
293 static void
294 schedule_fail_stop (NMVpnServicePlugin *plugin, guint timeout_secs)
295 {
296         NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
297
298         nm_clear_g_source (&priv->fail_stop_id);
299         if (timeout_secs)
300                 priv->fail_stop_id = g_timeout_add_seconds (timeout_secs, fail_stop, plugin);
301         else
302                 priv->fail_stop_id = g_idle_add (fail_stop, plugin);
303 }
304
305 void
306 nm_vpn_service_plugin_set_config (NMVpnServicePlugin *plugin,
307                                   GVariant *config)
308 {
309         NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
310
311         g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin));
312         g_return_if_fail (config != NULL);
313
314         priv->got_config = TRUE;
315
316         (void) g_variant_lookup (config, NM_VPN_PLUGIN_CONFIG_HAS_IP4, "b", &priv->has_ip4);
317         (void) g_variant_lookup (config, NM_VPN_PLUGIN_CONFIG_HAS_IP6, "b", &priv->has_ip6);
318
319         /* Record the items that need to also be inserted into the
320          * ip4config, for compatibility with older daemons.
321          */
322         if (priv->banner)
323                 g_variant_unref (priv->banner);
324         priv->banner = g_variant_lookup_value (config, NM_VPN_PLUGIN_CONFIG_BANNER,
325                                                G_VARIANT_TYPE ("s"));
326         if (priv->tundev)
327                 g_variant_unref (priv->tundev);
328         priv->tundev = g_variant_lookup_value (config, NM_VPN_PLUGIN_CONFIG_TUNDEV,
329                                                G_VARIANT_TYPE ("s"));
330         if (priv->gateway)
331                 g_variant_unref (priv->gateway);
332         priv->gateway = g_variant_lookup_value (config, NM_VPN_PLUGIN_CONFIG_EXT_GATEWAY,
333                                                 G_VARIANT_TYPE ("u"));
334         if (priv->mtu)
335                 g_variant_unref (priv->mtu);
336         priv->mtu = g_variant_lookup_value (config, NM_VPN_PLUGIN_CONFIG_MTU,
337                                             G_VARIANT_TYPE ("u"));
338
339         g_signal_emit (plugin, signals[CONFIG], 0, config);
340         nmdbus_vpn_plugin_emit_config (priv->dbus_vpn_service_plugin, config);
341 }
342
343 void
344 nm_vpn_service_plugin_set_ip4_config (NMVpnServicePlugin *plugin,
345                                       GVariant *ip4_config)
346 {
347         NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
348         GVariant *combined_config;
349         GVariantBuilder builder;
350         GVariantIter iter;
351         const char *key;
352         GVariant *value;
353
354         g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin));
355         g_return_if_fail (ip4_config != NULL);
356
357         priv->got_ip4 = TRUE;
358
359         /* Old plugins won't send the "config" signal and thus can't send
360          * NM_VPN_SERVICE_PLUGIN_CONFIG_HAS_IP4 either.  But since they don't support IPv6,
361          * we can safely assume that, if we don't receive a "config" signal but do
362          * receive an "ip4-config" signal, the old plugin supports IPv4.
363          */
364         if (!priv->got_config)
365                 priv->has_ip4 = TRUE;
366
367         /* Older NetworkManager daemons expect all config info to be in
368          * the ip4 config, so they won't even notice the "config" signal
369          * being emitted. So just copy all of that data into the ip4
370          * config too.
371          */
372         g_variant_builder_init (&builder, G_VARIANT_TYPE ("a{sv}"));
373         g_variant_iter_init (&iter, ip4_config);
374         while (g_variant_iter_next (&iter, "{&sv}", &key, &value)) {
375                 g_variant_builder_add (&builder, "{sv}", key, value);
376                 g_variant_unref (value);
377         }
378
379         if (priv->banner)
380                 g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_BANNER, priv->banner);
381         if (priv->tundev)
382                 g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_TUNDEV, priv->tundev);
383         if (priv->gateway)
384                 g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_EXT_GATEWAY, priv->gateway);
385         if (priv->mtu)
386                 g_variant_builder_add (&builder, "{sv}", NM_VPN_PLUGIN_IP4_CONFIG_MTU, priv->mtu);
387
388         combined_config = g_variant_builder_end (&builder);
389         g_variant_ref_sink (combined_config);
390         g_signal_emit (plugin, signals[IP4_CONFIG], 0, combined_config);
391         nmdbus_vpn_plugin_emit_ip4_config (priv->dbus_vpn_service_plugin, combined_config);
392         g_variant_unref (combined_config);
393
394         if (   priv->has_ip4 == priv->got_ip4
395             && priv->has_ip6 == priv->got_ip6)
396                 nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED);
397 }
398
399 void
400 nm_vpn_service_plugin_set_ip6_config (NMVpnServicePlugin *plugin,
401                                       GVariant *ip6_config)
402 {
403         NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
404
405         g_return_if_fail (NM_IS_VPN_SERVICE_PLUGIN (plugin));
406         g_return_if_fail (ip6_config != NULL);
407
408         g_variant_ref_sink (ip6_config);
409
410         priv->got_ip6 = TRUE;
411         g_signal_emit (plugin, signals[IP6_CONFIG], 0, ip6_config);
412         nmdbus_vpn_plugin_emit_ip6_config (priv->dbus_vpn_service_plugin, ip6_config);
413
414         g_variant_unref (ip6_config);
415
416         if (   priv->has_ip4 == priv->got_ip4
417             && priv->has_ip6 == priv->got_ip6)
418                 nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTED);
419 }
420
421 static void
422 connect_timer_start (NMVpnServicePlugin *plugin)
423 {
424         NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
425
426         nm_clear_g_source (&priv->connect_timer);
427         priv->connect_timer = g_timeout_add_seconds (60, connect_timer_expired, plugin);
428 }
429
430 static void
431 peer_vanished (GDBusConnection *connection,
432                const gchar *sender_name,
433                const gchar *object_path,
434                const gchar *interface_name,
435                const gchar *signal_name,
436                GVariant *parameters,
437                gpointer user_data)
438 {
439         nm_vpn_service_plugin_disconnect (NM_VPN_SERVICE_PLUGIN (user_data), NULL);
440 }
441
442 static guint
443 watch_peer (NMVpnServicePlugin *plugin,
444             GDBusMethodInvocation *context)
445 {
446         GDBusConnection *connection = g_dbus_method_invocation_get_connection (context);
447         const gchar *peer = g_dbus_message_get_sender (g_dbus_method_invocation_get_message (context));
448
449         return g_dbus_connection_signal_subscribe (connection,
450                                                    "org.freedesktop.DBus",
451                                                    "org.freedesktop.DBus",
452                                                    "NameOwnerChanged",
453                                                    "/org/freedesktop/DBus",
454                                                    peer,
455                                                    G_DBUS_SIGNAL_FLAGS_NONE,
456                                                    peer_vanished,
457                                                    plugin,
458                                                    NULL);
459 }
460
461 static void
462 _connect_generic (NMVpnServicePlugin *plugin,
463                   GDBusMethodInvocation *context,
464                   GVariant *properties,
465                   GVariant *details)
466 {
467         NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
468         NMVpnServicePluginClass *vpn_class = NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin);
469         NMConnection *connection;
470         gboolean success = FALSE;
471         GError *error = NULL;
472         guint fail_stop_timeout = 0;
473
474         if (priv->state != NM_VPN_SERVICE_STATE_STOPPED &&
475             priv->state != NM_VPN_SERVICE_STATE_INIT) {
476                 g_dbus_method_invocation_return_error (context,
477                                                        NM_VPN_PLUGIN_ERROR,
478                                                        NM_VPN_PLUGIN_ERROR_WRONG_STATE,
479                                                        "Could not start connection: wrong plugin state %d",
480                                                        priv->state);
481                 return;
482         }
483
484         connection = nm_simple_connection_new_from_dbus (properties, &error);
485         if (!connection) {
486                 g_dbus_method_invocation_return_error (context,
487                                                        NM_VPN_PLUGIN_ERROR,
488                                                        NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
489                                                        "Invalid connection: %s",
490                                                        error->message);
491                 g_clear_error (&error);
492                 return;
493         }
494
495         priv->interactive = FALSE;
496         if (details && !vpn_class->connect_interactive) {
497                 g_dbus_method_invocation_return_error (context,
498                                                        NM_VPN_PLUGIN_ERROR,
499                                                        NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED,
500                                                        "Plugin does not implement ConnectInteractive()");
501                 return;
502         }
503
504         nm_clear_g_source (&priv->fail_stop_id);
505
506         if (priv->dbus_watch_peer)
507                 priv->peer_watch_id = watch_peer (plugin, context);
508
509         if (details) {
510                 priv->interactive = TRUE;
511                 success = vpn_class->connect_interactive (plugin, connection, details, &error);
512                 if (g_error_matches (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED)) {
513                         /* Give NetworkManager a bit of time to fall back to Connect() */
514                         fail_stop_timeout = 5;
515                 }
516         } else
517                 success = vpn_class->connect (plugin, connection, &error);
518
519         if (success) {
520                 nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_STARTING);
521
522                 g_dbus_method_invocation_return_value (context, NULL);
523
524                 /* Add a timer to make sure we do not wait indefinitely for the successful connect. */
525                 connect_timer_start (plugin);
526         } else {
527                 g_dbus_method_invocation_take_error (context, error);
528
529                 /* Stop the plugin from an idle handler so that the Connect
530                  * method return gets sent before the STOP StateChanged signal.
531                  */
532                 schedule_fail_stop (plugin, fail_stop_timeout);
533         }
534
535         g_object_unref (connection);
536 }
537
538 static void
539 impl_vpn_service_plugin_connect (NMVpnServicePlugin *plugin,
540                                  GDBusMethodInvocation *context,
541                                  GVariant *connection,
542                                  gpointer user_data)
543 {
544         _connect_generic (plugin, context, connection, NULL);
545 }
546
547 static void
548 impl_vpn_service_plugin_connect_interactive (NMVpnServicePlugin *plugin,
549                                              GDBusMethodInvocation *context,
550                                              GVariant *connection,
551                                              GVariant *details,
552                                              gpointer user_data)
553 {
554         _connect_generic (plugin, context, connection, details);
555 }
556
557 /***************************************************************/
558
559 static void
560 impl_vpn_service_plugin_need_secrets (NMVpnServicePlugin *plugin,
561                                       GDBusMethodInvocation *context,
562                                       GVariant *properties,
563                                       gpointer user_data)
564 {
565         NMConnection *connection;
566         const char *setting_name;
567         gboolean needed;
568         GError *error = NULL;
569
570         connection = nm_simple_connection_new_from_dbus (properties, &error);
571         if (!connection) {
572                 g_dbus_method_invocation_return_error (context,
573                                                        NM_VPN_PLUGIN_ERROR,
574                                                        NM_VPN_PLUGIN_ERROR_INVALID_CONNECTION,
575                                                        "The connection was invalid: %s",
576                                                        error->message);
577                 g_error_free (error);
578                 return;
579         }
580
581         if (!NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->need_secrets) {
582                 g_dbus_method_invocation_return_value (context,
583                                                        g_variant_new ("(s)", ""));
584                 return;
585         }
586
587         needed = NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->need_secrets (plugin, connection, &setting_name, &error);
588         if (error) {
589                 g_dbus_method_invocation_take_error (context, error);
590                 return;
591         }
592
593         if (needed) {
594                 /* Push back the quit timer so the VPN plugin doesn't quit in the
595                  * middle of asking the user for secrets.
596                  */
597                 schedule_quit_timer (plugin);
598
599                 g_assert (setting_name);
600                 g_dbus_method_invocation_return_value (context,
601                                                        g_variant_new ("(s)", setting_name));
602         } else {
603                 /* No secrets required */
604                 g_dbus_method_invocation_return_value (context,
605                                                        g_variant_new ("(s)", ""));
606         }
607 }
608
609 static void
610 impl_vpn_service_plugin_new_secrets (NMVpnServicePlugin *plugin,
611                                      GDBusMethodInvocation *context,
612                                      GVariant *properties,
613                                      gpointer user_data)
614 {
615         NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
616         NMConnection *connection;
617         GError *error = NULL;
618         gboolean success;
619
620         if (priv->state != NM_VPN_SERVICE_STATE_STARTING) {
621                 g_dbus_method_invocation_return_error (context,
622                                                        NM_VPN_PLUGIN_ERROR,
623                                                        NM_VPN_PLUGIN_ERROR_WRONG_STATE,
624                                                        "Could not accept new secrets: wrong plugin state %d",
625                                                        priv->state);
626                 return;
627         }
628
629         connection = nm_simple_connection_new_from_dbus (properties, &error);
630         if (!connection) {
631                 g_dbus_method_invocation_return_error (context,
632                                                        NM_VPN_PLUGIN_ERROR,
633                                                        NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
634                                                        "Invalid connection: %s",
635                                                        error->message);
636                 g_clear_error (&error);
637                 return;
638         }
639
640         if (!NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->new_secrets) {
641                 g_dbus_method_invocation_return_error (context,
642                                                        NM_VPN_PLUGIN_ERROR,
643                                                        NM_VPN_PLUGIN_ERROR_INTERACTIVE_NOT_SUPPORTED,
644                                                        "Could not accept new secrets: plugin cannot process interactive secrets");
645                 g_object_unref (connection);
646                 return;
647         }
648
649         success = NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->new_secrets (plugin, connection, &error);
650         if (success) {
651                 g_dbus_method_invocation_return_value (context, NULL);
652
653                 /* Add a timer to make sure we do not wait indefinitely for the successful connect. */
654                 connect_timer_start (plugin);
655         } else {
656                 g_dbus_method_invocation_take_error (context, error);
657
658                 /* Stop the plugin from and idle handler so that the NewSecrets
659                  * method return gets sent before the STOP StateChanged signal.
660                  */
661                 schedule_fail_stop (plugin, 0);
662         }
663
664         g_object_unref (connection);
665 }
666
667 /**
668  * nm_vpn_service_plugin_secrets_required:
669  * @plugin: the #NMVpnServicePlugin
670  * @message: an information message about why secrets are required, if any
671  * @hints: VPN specific secret names for required new secrets
672  *
673  * Called by VPN plugin implementations to signal to NetworkManager that secrets
674  * are required during the connection process.  This signal may be used to
675  * request new secrets when the secrets originally provided by NetworkManager
676  * are insufficient, or the VPN process indicates that it needs additional
677  * information to complete the request.
678  *
679  * Since: 1.2
680  */
681 void
682 nm_vpn_service_plugin_secrets_required (NMVpnServicePlugin *plugin,
683                                         const char *message,
684                                         const char **hints)
685 {
686         NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
687
688         /* Plugin must be able to accept the new secrets if it calls this method */
689         g_return_if_fail (NM_VPN_SERVICE_PLUGIN_GET_CLASS (plugin)->new_secrets);
690
691         /* Plugin cannot call this method if NetworkManager didn't originally call
692          * ConnectInteractive().
693          */
694         g_return_if_fail (priv->interactive == TRUE);
695
696         /* Cancel the connect timer since secrets might take a while.  It'll
697          * get restarted when the secrets come back via NewSecrets().
698          */
699         nm_clear_g_source (&priv->connect_timer);
700
701         g_signal_emit (plugin, signals[SECRETS_REQUIRED], 0, message, hints);
702         nmdbus_vpn_plugin_emit_secrets_required (priv->dbus_vpn_service_plugin, message, hints);
703 }
704
705 /***************************************************************/
706
707 #define DATA_KEY_TAG "DATA_KEY="
708 #define DATA_VAL_TAG "DATA_VAL="
709 #define SECRET_KEY_TAG "SECRET_KEY="
710 #define SECRET_VAL_TAG "SECRET_VAL="
711
712 static void
713 free_secret (gpointer data)
714 {
715         char *secret = data;
716
717         memset (secret, 0, strlen (secret));
718         g_free (secret);
719 }
720
721 /**
722  * nm_vpn_service_plugin_read_vpn_details:
723  * @fd: file descriptor to read from, usually stdin (0)
724  * @out_data: (out) (transfer full): on successful return, a hash table
725  * (mapping char*:char*) containing the key/value pairs of VPN data items
726  * @out_secrets: (out) (transfer full): on successful return, a hash table
727  * (mapping char*:char*) containing the key/value pairsof VPN secrets
728  *
729  * Parses key/value pairs from a file descriptor (normally stdin) passed by
730  * an applet when the applet calls the authentication dialog of the VPN plugin.
731  *
732  * Returns: %TRUE if reading values was successful, %FALSE if not
733  *
734  * Since: 1.2
735  **/
736 gboolean
737 nm_vpn_service_plugin_read_vpn_details (int fd,
738                                         GHashTable **out_data,
739                                         GHashTable **out_secrets)
740 {
741         GHashTable *data, *secrets;
742         gboolean success = FALSE;
743         char *key = NULL, *val = NULL;
744         GString *line;
745         gchar c;
746
747         if (out_data)
748                 g_return_val_if_fail (*out_data == NULL, FALSE);
749         if (out_secrets)
750                 g_return_val_if_fail (*out_secrets == NULL, FALSE);
751
752         data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
753         secrets = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_secret);
754
755         line = g_string_new (NULL);
756
757         /* Read stdin for data and secret items until we get a DONE */
758         while (1) {
759                 ssize_t nr;
760                 GHashTable *hash = NULL;
761
762                 errno = 0;
763                 nr = read (fd, &c, 1);
764                 if (nr == -1) {
765                         if (errno == EAGAIN) {
766                                 g_usleep (100);
767                                 continue;
768                         }
769                         break;
770                 }
771
772                 if (c != '\n') {
773                         g_string_append_c (line, c);
774                         continue;
775                 }
776
777                 /* Check for the finish marker */
778                 if (strcmp (line->str, "DONE") == 0)
779                         break;
780
781                 /* Otherwise it's a data/secret item */
782                 if (strncmp (line->str, DATA_KEY_TAG, strlen (DATA_KEY_TAG)) == 0) {
783                         hash = data;
784                         key = g_strdup (line->str + strlen (DATA_KEY_TAG));
785                 } else if (strncmp (line->str, DATA_VAL_TAG, strlen (DATA_VAL_TAG)) == 0) {
786                         hash = data;
787                         val = g_strdup (line->str + strlen (DATA_VAL_TAG));
788                 } else if (strncmp (line->str, SECRET_KEY_TAG, strlen (SECRET_KEY_TAG)) == 0) {
789                         hash = secrets;
790                         key = g_strdup (line->str + strlen (SECRET_KEY_TAG));
791                 } else if (strncmp (line->str, SECRET_VAL_TAG, strlen (SECRET_VAL_TAG)) == 0) {
792                         hash = secrets;
793                         val = g_strdup (line->str + strlen (SECRET_VAL_TAG));
794                 }
795                 g_string_truncate (line, 0);
796
797                 if (key && val && hash) {
798                         g_hash_table_insert (hash, key, val);
799                         key = NULL;
800                         val = NULL;
801                         success = TRUE;  /* Got at least one value */
802                 }
803         }
804
805         if (success) {
806                 if (out_data)
807                         *out_data = data;
808                 else
809                         g_hash_table_destroy (data);
810
811                 if (out_secrets)
812                         *out_secrets = secrets;
813                 else
814                         g_hash_table_destroy (secrets);
815         } else {
816                 g_hash_table_destroy (data);
817                 g_hash_table_destroy (secrets);
818         }
819
820         g_string_free (line, TRUE);
821         return success;
822 }
823
824 /**
825  * nm_vpn_service_plugin_get_secret_flags:
826  * @data: hash table containing VPN key/value pair data items
827  * @secret_name: VPN secret key name for which to retrieve flags for
828  * @out_flags: (out): on success, the flags associated with @secret_name
829  *
830  * Given a VPN secret key name, attempts to find the corresponding flags data
831  * item in @data.  If found, converts the flags data item to
832  * #NMSettingSecretFlags and returns it.
833  *
834  * Returns: %TRUE if the flag data item was found and successfully converted
835  * to flags, %FALSE if not
836  *
837  * Since: 1.2
838  **/
839 gboolean
840 nm_vpn_service_plugin_get_secret_flags (GHashTable *data,
841                                         const char *secret_name,
842                                         NMSettingSecretFlags *out_flags)
843 {
844         char *flag_name;
845         const char *val;
846         unsigned long tmp;
847         gboolean success = FALSE;
848
849         g_return_val_if_fail (data != NULL, FALSE);
850         g_return_val_if_fail (secret_name != NULL, FALSE);
851         g_return_val_if_fail (out_flags != NULL, FALSE);
852         g_return_val_if_fail (*out_flags == NM_SETTING_SECRET_FLAG_NONE, FALSE);
853
854         flag_name = g_strdup_printf ("%s-flags", secret_name);
855
856         /* Try new flags value first */
857         val = g_hash_table_lookup (data, flag_name);
858         if (val) {
859                 errno = 0;
860                 tmp = strtoul (val, NULL, 10);
861                 if (errno == 0 && tmp <= NM_SETTING_SECRET_FLAGS_ALL) {
862                         *out_flags = (NMSettingSecretFlags) tmp;
863                         success = TRUE;
864                 }
865         }
866
867         g_free (flag_name);
868         return success;
869 }
870
871 /***************************************************************/
872
873 static void
874 impl_vpn_service_plugin_disconnect (NMVpnServicePlugin *plugin,
875                                     GDBusMethodInvocation *context,
876                                     gpointer user_data)
877 {
878         GError *error = NULL;
879
880         if (nm_vpn_service_plugin_disconnect (plugin, &error))
881                 g_dbus_method_invocation_return_value (context, NULL);
882         else
883                 g_dbus_method_invocation_take_error (context, error);
884 }
885
886 static void
887 impl_vpn_service_plugin_set_config (NMVpnServicePlugin *plugin,
888                                     GDBusMethodInvocation *context,
889                                     GVariant *config,
890                                     gpointer user_data)
891 {
892         nm_vpn_service_plugin_set_config (plugin, config);
893         g_dbus_method_invocation_return_value (context, NULL);
894 }
895
896 static void
897 impl_vpn_service_plugin_set_ip4_config (NMVpnServicePlugin *plugin,
898                                         GDBusMethodInvocation *context,
899                                         GVariant *config,
900                                         gpointer user_data)
901 {
902         nm_vpn_service_plugin_set_ip4_config (plugin, config);
903         g_dbus_method_invocation_return_value (context, NULL);
904 }
905
906 static void
907 impl_vpn_service_plugin_set_ip6_config (NMVpnServicePlugin *plugin,
908                                         GDBusMethodInvocation *context,
909                                         GVariant *config,
910                                         gpointer user_data)
911 {
912         nm_vpn_service_plugin_set_ip6_config (plugin, config);
913         g_dbus_method_invocation_return_value (context, NULL);
914 }
915
916 static void
917 impl_vpn_service_plugin_set_failure (NMVpnServicePlugin *plugin,
918                                      GDBusMethodInvocation *context,
919                                      char *reason,
920                                      gpointer user_data)
921 {
922         nm_vpn_service_plugin_failure (plugin, NM_VPN_PLUGIN_FAILURE_BAD_IP_CONFIG);
923         g_dbus_method_invocation_return_value (context, NULL);
924 }
925
926 /*********************************************************************/
927
928 static void
929 sigterm_handler (int signum)
930 {
931         g_slist_foreach (active_plugins, (GFunc) nm_vpn_service_plugin_emit_quit, NULL);
932 }
933
934 static void
935 setup_unix_signal_handler (void)
936 {
937         struct sigaction action;
938         sigset_t block_mask;
939
940         action.sa_handler = sigterm_handler;
941         sigemptyset (&block_mask);
942         action.sa_mask = block_mask;
943         action.sa_flags = 0;
944         sigaction (SIGINT, &action, NULL);
945         sigaction (SIGTERM, &action, NULL);
946 }
947
948 /*********************************************************************/
949
950 static void
951 one_plugin_destroyed (gpointer data,
952                       GObject *object)
953 {
954         active_plugins = g_slist_remove (active_plugins, object);
955 }
956
957 static void
958 nm_vpn_service_plugin_init (NMVpnServicePlugin *plugin)
959 {
960         active_plugins = g_slist_append (active_plugins, plugin);
961         g_object_weak_ref (G_OBJECT (plugin),
962                            one_plugin_destroyed,
963                            NULL);
964 }
965
966 static gboolean
967 init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
968 {
969         NMVpnServicePlugin *plugin = NM_VPN_SERVICE_PLUGIN (initable);
970         NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
971         GDBusConnection *connection = NULL;
972         GDBusProxy *proxy;
973         GVariant *ret;
974         gboolean success = FALSE;
975
976         if (!priv->dbus_service_name) {
977                 g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
978                                      _("No service name specified"));
979                 return FALSE;
980         }
981
982         connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, error);
983         if (!connection)
984                 return FALSE;
985
986         proxy = g_dbus_proxy_new_sync (connection,
987                                        G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES |
988                                        G_DBUS_PROXY_FLAGS_DO_NOT_CONNECT_SIGNALS,
989                                        NULL,
990                                        DBUS_SERVICE_DBUS,
991                                        DBUS_PATH_DBUS,
992                                        DBUS_INTERFACE_DBUS,
993                                        cancellable, error);
994         if (!proxy)
995                 goto out;
996
997         ret = g_dbus_proxy_call_sync (proxy,
998                                       "RequestName",
999                                       g_variant_new ("(su)", priv->dbus_service_name, 0),
1000                                       G_DBUS_CALL_FLAGS_NONE, -1,
1001                                       cancellable, error);
1002         g_object_unref (proxy);
1003         if (!ret) {
1004                 if (error && *error)
1005                         g_dbus_error_strip_remote_error (*error);
1006                 goto out;
1007         }
1008         g_variant_unref (ret);
1009
1010         priv->dbus_vpn_service_plugin = nmdbus_vpn_plugin_skeleton_new ();
1011         if (!g_dbus_interface_skeleton_export (G_DBUS_INTERFACE_SKELETON (priv->dbus_vpn_service_plugin),
1012                                                connection,
1013                                                NM_VPN_DBUS_PLUGIN_PATH,
1014                                                error))
1015                 goto out;
1016
1017         _nm_dbus_bind_properties (plugin, priv->dbus_vpn_service_plugin);
1018         _nm_dbus_bind_methods (plugin, priv->dbus_vpn_service_plugin,
1019                                "Connect", impl_vpn_service_plugin_connect,
1020                                "ConnectInteractive", impl_vpn_service_plugin_connect_interactive,
1021                                "NeedSecrets", impl_vpn_service_plugin_need_secrets,
1022                                "NewSecrets", impl_vpn_service_plugin_new_secrets,
1023                                "Disconnect", impl_vpn_service_plugin_disconnect,
1024                                "SetConfig", impl_vpn_service_plugin_set_config,
1025                                "SetIp4Config", impl_vpn_service_plugin_set_ip4_config,
1026                                "SetIp6Config", impl_vpn_service_plugin_set_ip6_config,
1027                                "SetFailure", impl_vpn_service_plugin_set_failure,
1028                                NULL);
1029
1030         nm_vpn_service_plugin_set_connection (plugin, connection);
1031         nm_vpn_service_plugin_set_state (plugin, NM_VPN_SERVICE_STATE_INIT);
1032
1033         success = TRUE;
1034
1035  out:
1036         g_clear_object (&connection);
1037
1038         return success;
1039 }
1040
1041 static void
1042 set_property (GObject *object, guint prop_id,
1043               const GValue *value, GParamSpec *pspec)
1044 {
1045         NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (object);
1046
1047         switch (prop_id) {
1048         case PROP_DBUS_SERVICE_NAME:
1049                 /* Construct-only */
1050                 priv->dbus_service_name = g_value_dup_string (value);
1051                 break;
1052         case PROP_DBUS_WATCH_PEER:
1053                 /* Construct-only */
1054                 priv->dbus_watch_peer = g_value_get_boolean (value);
1055                 break;
1056         case PROP_STATE:
1057                 nm_vpn_service_plugin_set_state (NM_VPN_SERVICE_PLUGIN (object),
1058                                              (NMVpnServiceState) g_value_get_enum (value));
1059                 break;
1060         default:
1061                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1062                 break;
1063         }
1064 }
1065
1066 static void
1067 get_property (GObject *object, guint prop_id,
1068               GValue *value, GParamSpec *pspec)
1069 {
1070         NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (object);
1071
1072         switch (prop_id) {
1073         case PROP_DBUS_SERVICE_NAME:
1074                 g_value_set_string (value, priv->dbus_service_name);
1075                 break;
1076         case PROP_DBUS_WATCH_PEER:
1077                 g_value_set_boolean (value, priv->dbus_watch_peer);
1078                 break;
1079         case PROP_STATE:
1080                 g_value_set_enum (value, nm_vpn_service_plugin_get_state (NM_VPN_SERVICE_PLUGIN (object)));
1081                 break;
1082         default:
1083                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
1084                 break;
1085         }
1086 }
1087
1088 static void
1089 dispose (GObject *object)
1090 {
1091         NMVpnServicePlugin *plugin = NM_VPN_SERVICE_PLUGIN (object);
1092         NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
1093         NMVpnServiceState state;
1094         GError *err = NULL;
1095
1096         nm_clear_g_source (&priv->fail_stop_id);
1097         nm_clear_g_source (&priv->quit_timer);
1098         nm_clear_g_source (&priv->connect_timer);
1099
1100         state = nm_vpn_service_plugin_get_state (plugin);
1101
1102         if (state == NM_VPN_SERVICE_STATE_STARTED ||
1103             state == NM_VPN_SERVICE_STATE_STARTING)
1104                 nm_vpn_service_plugin_disconnect (plugin, &err);
1105
1106         if (err) {
1107                 g_warning ("Error disconnecting VPN connection: %s", err->message);
1108                 g_error_free (err);
1109         }
1110
1111         G_OBJECT_CLASS (nm_vpn_service_plugin_parent_class)->dispose (object);
1112 }
1113
1114 static void
1115 finalize (GObject *object)
1116 {
1117         NMVpnServicePlugin *plugin = NM_VPN_SERVICE_PLUGIN (object);
1118         NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
1119
1120         nm_vpn_service_plugin_set_connection (plugin, NULL);
1121         g_free (priv->dbus_service_name);
1122
1123         g_clear_pointer (&priv->banner, g_variant_unref);
1124         g_clear_pointer (&priv->tundev, g_variant_unref);
1125         g_clear_pointer (&priv->gateway, g_variant_unref);
1126         g_clear_pointer (&priv->mtu, g_variant_unref);
1127
1128         G_OBJECT_CLASS (nm_vpn_service_plugin_parent_class)->finalize (object);
1129 }
1130
1131 static void
1132 state_changed (NMVpnServicePlugin *plugin, NMVpnServiceState state)
1133 {
1134         NMVpnServicePluginPrivate *priv = NM_VPN_SERVICE_PLUGIN_GET_PRIVATE (plugin);
1135
1136         switch (state) {
1137         case NM_VPN_SERVICE_STATE_STARTING:
1138                 nm_clear_g_source (&priv->quit_timer);
1139                 nm_clear_g_source (&priv->fail_stop_id);
1140                 break;
1141         case NM_VPN_SERVICE_STATE_STOPPED:
1142                 if (priv->dbus_watch_peer)
1143                         nm_vpn_service_plugin_emit_quit (plugin);
1144                 else
1145                         schedule_quit_timer (plugin);
1146                 if (priv->peer_watch_id) {
1147                         g_dbus_connection_signal_unsubscribe (nm_vpn_service_plugin_get_connection (plugin),
1148                                                               priv->peer_watch_id);
1149                         priv->peer_watch_id = 0;
1150                 }
1151                 break;
1152         default:
1153                 /* Clean up all timers we might have set up. */
1154                 nm_clear_g_source (&priv->connect_timer);
1155                 nm_clear_g_source (&priv->quit_timer);
1156                 nm_clear_g_source (&priv->fail_stop_id);
1157                 break;
1158         }
1159 }
1160
1161 static void
1162 nm_vpn_service_plugin_class_init (NMVpnServicePluginClass *plugin_class)
1163 {
1164         GObjectClass *object_class = G_OBJECT_CLASS (plugin_class);
1165
1166         g_type_class_add_private (object_class, sizeof (NMVpnServicePluginPrivate));
1167
1168         /* virtual methods */
1169         object_class->set_property = set_property;
1170         object_class->get_property = get_property;
1171         object_class->dispose      = dispose;
1172         object_class->finalize     = finalize;
1173
1174         plugin_class->state_changed = state_changed;
1175
1176         /* properties */
1177
1178         /**
1179          * NMVpnServicePlugin:service-name:
1180          *
1181          * The D-Bus service name of this plugin.
1182          *
1183          * Since: 1.2
1184          */
1185         g_object_class_install_property
1186                 (object_class, PROP_DBUS_SERVICE_NAME,
1187                  g_param_spec_string (NM_VPN_SERVICE_PLUGIN_DBUS_SERVICE_NAME, "", "",
1188                                       NULL,
1189                                       G_PARAM_READWRITE |
1190                                       G_PARAM_CONSTRUCT_ONLY |
1191                                       G_PARAM_STATIC_STRINGS));
1192
1193         /**
1194          * NMVpnServicePlugin:watch-peer:
1195          *
1196          * Whether to watch for D-Bus peer's changes.
1197          *
1198          * Since: 1.2
1199          */
1200         g_object_class_install_property
1201                 (object_class, PROP_DBUS_WATCH_PEER,
1202                  g_param_spec_boolean (NM_VPN_SERVICE_PLUGIN_DBUS_WATCH_PEER, "", "",
1203                                        FALSE,
1204                                        G_PARAM_READWRITE |
1205                                        G_PARAM_CONSTRUCT_ONLY));
1206
1207         /**
1208          * NMVpnServicePlugin:state:
1209          *
1210          * The state of the plugin.
1211          *
1212          * Since: 1.2
1213          */
1214         g_object_class_install_property
1215                 (object_class, PROP_STATE,
1216                  g_param_spec_enum (NM_VPN_SERVICE_PLUGIN_STATE, "", "",
1217                                     NM_TYPE_VPN_SERVICE_STATE,
1218                                     NM_VPN_SERVICE_STATE_INIT,
1219                                     G_PARAM_READWRITE |
1220                                     G_PARAM_STATIC_STRINGS));
1221
1222         /* signals */
1223         signals[STATE_CHANGED] =
1224                 g_signal_new ("state-changed",
1225                               G_OBJECT_CLASS_TYPE (object_class),
1226                               G_SIGNAL_RUN_FIRST,
1227                               G_STRUCT_OFFSET (NMVpnServicePluginClass, state_changed),
1228                               NULL, NULL,
1229                               NULL,
1230                               G_TYPE_NONE, 1,
1231                               G_TYPE_UINT);
1232
1233         signals[SECRETS_REQUIRED] =
1234                 g_signal_new ("secrets-required",
1235                               G_OBJECT_CLASS_TYPE (object_class),
1236                               G_SIGNAL_RUN_FIRST,
1237                               0, NULL, NULL,
1238                               NULL,
1239                               G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRV);
1240
1241         signals[CONFIG] =
1242                 g_signal_new ("config",
1243                               G_OBJECT_CLASS_TYPE (object_class),
1244                               G_SIGNAL_RUN_FIRST,
1245                               G_STRUCT_OFFSET (NMVpnServicePluginClass, config),
1246                               NULL, NULL,
1247                               NULL,
1248                               G_TYPE_NONE, 1,
1249                               G_TYPE_VARIANT);
1250
1251         signals[IP4_CONFIG] =
1252                 g_signal_new ("ip4-config",
1253                               G_OBJECT_CLASS_TYPE (object_class),
1254                               G_SIGNAL_RUN_FIRST,
1255                               G_STRUCT_OFFSET (NMVpnServicePluginClass, ip4_config),
1256                               NULL, NULL,
1257                               NULL,
1258                               G_TYPE_NONE, 1,
1259                               G_TYPE_VARIANT);
1260
1261         signals[IP6_CONFIG] =
1262                 g_signal_new ("ip6-config",
1263                               G_OBJECT_CLASS_TYPE (object_class),
1264                               G_SIGNAL_RUN_FIRST,
1265                               G_STRUCT_OFFSET (NMVpnServicePluginClass, ip6_config),
1266                               NULL, NULL,
1267                               NULL,
1268                               G_TYPE_NONE, 1,
1269                               G_TYPE_VARIANT);
1270
1271         signals[LOGIN_BANNER] =
1272                 g_signal_new ("login-banner",
1273                               G_OBJECT_CLASS_TYPE (object_class),
1274                               G_SIGNAL_RUN_FIRST,
1275                               G_STRUCT_OFFSET (NMVpnServicePluginClass, login_banner),
1276                               NULL, NULL,
1277                               NULL,
1278                               G_TYPE_NONE, 1,
1279                               G_TYPE_STRING);
1280
1281         signals[FAILURE] =
1282                 g_signal_new ("failure",
1283                               G_OBJECT_CLASS_TYPE (object_class),
1284                               G_SIGNAL_RUN_FIRST,
1285                               G_STRUCT_OFFSET (NMVpnServicePluginClass, failure),
1286                               NULL, NULL,
1287                               NULL,
1288                               G_TYPE_NONE, 1,
1289                               G_TYPE_UINT);
1290
1291         signals[QUIT] =
1292                 g_signal_new ("quit",
1293                               G_OBJECT_CLASS_TYPE (object_class),
1294                               G_SIGNAL_RUN_FIRST,
1295                               G_STRUCT_OFFSET (NMVpnServicePluginClass, quit),
1296                               NULL, NULL,
1297                               NULL,
1298                               G_TYPE_NONE, 0,
1299                               G_TYPE_NONE);
1300
1301         setup_unix_signal_handler ();
1302 }
1303
1304 static void
1305 nm_vpn_service_plugin_initable_iface_init (GInitableIface *iface)
1306 {
1307         iface->init = init_sync;
1308 }