device: renew dhcp leases on awake for software devices
[NetworkManager.git] / libnm / nm-dbus-helpers.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 2013 Red Hat, Inc.
19  */
20
21 #include "nm-default.h"
22
23 #include "nm-dbus-helpers.h"
24
25 #include <string.h>
26
27 #include "nm-dbus-interface.h"
28
29 static GBusType nm_bus = G_BUS_TYPE_SYSTEM;
30
31 GBusType
32 _nm_dbus_bus_type (void)
33 {
34         static gsize init_value = 0;
35
36         if (g_once_init_enter (&init_value)) {
37                 if (g_getenv ("LIBNM_USE_SESSION_BUS"))
38                         nm_bus = G_BUS_TYPE_SESSION;
39
40                 g_once_init_leave (&init_value, 1);
41         }
42
43         return nm_bus;
44 }
45
46 GDBusConnection *
47 _nm_dbus_new_connection (GCancellable *cancellable, GError **error)
48 {
49         return g_bus_get_sync (_nm_dbus_bus_type (), cancellable, error);
50 }
51
52 static void
53 new_connection_async_got_system (GObject *source, GAsyncResult *result, gpointer user_data)
54 {
55         GSimpleAsyncResult *simple = user_data;
56         GDBusConnection *connection;
57         GError *error = NULL;
58
59         connection = g_bus_get_finish (result, &error);
60         if (connection)
61                 g_simple_async_result_set_op_res_gpointer (simple, connection, g_object_unref);
62         else
63                 g_simple_async_result_take_error (simple, error);
64
65         g_simple_async_result_complete (simple);
66         g_object_unref (simple);
67 }
68
69 void
70 _nm_dbus_new_connection_async (GCancellable *cancellable,
71                                GAsyncReadyCallback callback,
72                                gpointer user_data)
73 {
74         GSimpleAsyncResult *simple;
75
76         simple = g_simple_async_result_new (NULL, callback, user_data, _nm_dbus_new_connection_async);
77
78         g_bus_get (_nm_dbus_bus_type (),
79                    cancellable,
80                    new_connection_async_got_system, simple);
81 }
82
83 GDBusConnection *
84 _nm_dbus_new_connection_finish (GAsyncResult *result,
85                                 GError **error)
86 {
87         GSimpleAsyncResult *simple = G_SIMPLE_ASYNC_RESULT (result);
88
89         if (g_simple_async_result_propagate_error (simple, error))
90                 return NULL;
91
92         return g_object_ref (g_simple_async_result_get_op_res_gpointer (simple));
93 }
94
95 gboolean
96 _nm_dbus_is_connection_private (GDBusConnection *connection)
97 {
98         return g_dbus_connection_get_unique_name (connection) == NULL;
99 }
100
101 static GHashTable *proxy_types;
102
103 #undef _nm_dbus_register_proxy_type
104 void
105 _nm_dbus_register_proxy_type (const char *interface,
106                               GType       proxy_type)
107 {
108         if (!proxy_types)
109                 proxy_types = g_hash_table_new (g_str_hash, g_str_equal);
110
111         g_assert (g_hash_table_lookup (proxy_types, interface) == NULL);
112         g_hash_table_insert (proxy_types, (char *) interface, GSIZE_TO_POINTER (proxy_type));
113 }
114
115 /* We don't (currently) use GDBus's property-handling code */
116 #define NM_DBUS_PROXY_FLAGS (G_DBUS_PROXY_FLAGS_DO_NOT_LOAD_PROPERTIES | \
117                              G_DBUS_PROXY_FLAGS_DO_NOT_AUTO_START)
118
119 /* D-Bus has an upper limit on number of Match rules and it's rather easy
120  * to hit as the proxy likes to add one for each object. Let's remove the Match
121  * rule the proxy added and ensure a less granular rule is present instead.
122  *
123  * Also, don't do this immediately since it has a performance penalty.
124  * Still better than loosing the signals altogether.
125  *
126  * Ideally, we should be able to tell glib not to hook its rules:
127  * https://bugzilla.gnome.org/show_bug.cgi?id=758749
128  */
129 static void
130 _nm_dbus_proxy_replace_match (GDBusProxy *proxy)
131 {
132         GDBusConnection *connection = g_dbus_proxy_get_connection (proxy);
133         static unsigned match_counter = 1024;
134         gchar *match;
135
136         nm_assert (!g_strcmp0 (g_dbus_proxy_get_name (proxy), NM_DBUS_SERVICE));
137
138         if (match_counter == 1) {
139                 /* If we hit the low matches watermark, install a
140                  * less granular one. */
141                 g_dbus_connection_call (connection,
142                                         "org.freedesktop.DBus",
143                                         "/org/freedesktop/DBus",
144                                         "org.freedesktop.DBus",
145                                         "AddMatch",
146                                         g_variant_new ("(s)", "type='signal',sender='" NM_DBUS_SERVICE "'"),
147                                         NULL,
148                                         G_DBUS_CALL_FLAGS_NONE,
149                                         -1,
150                                         NULL,
151                                         NULL,
152                                         NULL);
153         }
154
155         if (match_counter)
156                 match_counter--;
157         if (match_counter)
158                 return;
159
160         /* Remove what this proxy added. */
161         match = g_strdup_printf ("type='signal',sender='" NM_DBUS_SERVICE "',"
162                                  "interface='%s',path='%s'",
163                                  g_dbus_proxy_get_interface_name (proxy),
164                                  g_dbus_proxy_get_object_path (proxy));
165         g_dbus_connection_call (connection,
166                                 "org.freedesktop.DBus",
167                                 "/org/freedesktop/DBus",
168                                 "org.freedesktop.DBus",
169                                 "RemoveMatch",
170                                 g_variant_new ("(s)", match),
171                                 NULL,
172                                 G_DBUS_CALL_FLAGS_NONE,
173                                 -1,
174                                 NULL,
175                                 NULL,
176                                 NULL);
177         g_free (match);
178 }
179
180 GDBusProxy *
181 _nm_dbus_new_proxy_for_connection (GDBusConnection *connection,
182                                    const char *path,
183                                    const char *interface,
184                                    GCancellable *cancellable,
185                                    GError **error)
186 {
187         GDBusProxy *proxy;
188         GType proxy_type;
189         const char *name;
190
191         proxy_type = GPOINTER_TO_SIZE (g_hash_table_lookup (proxy_types, interface));
192         if (!proxy_type)
193                 proxy_type = G_TYPE_DBUS_PROXY;
194
195         if (_nm_dbus_is_connection_private (connection))
196                 name = NULL;
197         else
198                 name = NM_DBUS_SERVICE;
199
200         proxy = g_initable_new (proxy_type, cancellable, error,
201                                 "g-connection", connection,
202                                 "g-flags", NM_DBUS_PROXY_FLAGS,
203                                 "g-name", name,
204                                 "g-object-path", path,
205                                 "g-interface-name", interface,
206                                 NULL);
207         _nm_dbus_proxy_replace_match (proxy);
208
209         return proxy;
210 }
211
212 void
213 _nm_dbus_new_proxy_for_connection_async (GDBusConnection *connection,
214                                          const char *path,
215                                          const char *interface,
216                                          GCancellable *cancellable,
217                                          GAsyncReadyCallback callback,
218                                          gpointer user_data)
219 {
220         GType proxy_type;
221         const char *name;
222
223         proxy_type = GPOINTER_TO_SIZE (g_hash_table_lookup (proxy_types, interface));
224         if (!proxy_type)
225                 proxy_type = G_TYPE_DBUS_PROXY;
226
227         if (_nm_dbus_is_connection_private (connection))
228                 name = NULL;
229         else
230                 name = NM_DBUS_SERVICE;
231
232         g_async_initable_new_async (proxy_type, G_PRIORITY_DEFAULT,
233                                     cancellable, callback, user_data,
234                                     "g-connection", connection,
235                                     "g-flags", NM_DBUS_PROXY_FLAGS,
236                                     "g-name", name,
237                                     "g-object-path", path,
238                                     "g-interface-name", interface,
239                                     NULL);
240 }
241
242 GDBusProxy *
243 _nm_dbus_new_proxy_for_connection_finish (GAsyncResult *result,
244                                           GError **error)
245 {
246         GObject *source;
247         GDBusProxy *proxy;
248
249         source = g_async_result_get_source_object (result);
250         proxy = G_DBUS_PROXY (g_async_initable_new_finish (G_ASYNC_INITABLE (source), result, error));
251         g_object_unref (source);
252         _nm_dbus_proxy_replace_match (proxy);
253
254         return proxy;
255 }
256
257 /* Binds the properties on a generated server-side GDBus object to the
258  * corresponding properties on the public object.
259  */
260 void
261 _nm_dbus_bind_properties (gpointer object, gpointer skeleton)
262 {
263         GParamSpec **properties;
264         guint n_properties;
265         int i;
266
267         properties = g_object_class_list_properties (G_OBJECT_GET_CLASS (skeleton), &n_properties);
268         for (i = 0; i < n_properties; i++) {
269                 if (g_str_has_prefix (properties[i]->name, "g-"))
270                         continue;
271
272                 g_object_bind_property (object, properties[i]->name,
273                                         skeleton, properties[i]->name,
274                                         G_BINDING_BIDIRECTIONAL | G_BINDING_SYNC_CREATE);
275         }
276         g_free (properties);
277 }
278
279 static char *
280 signal_name_from_method_name (const char *method_name)
281 {
282         GString *signal_name;
283         const char *p;
284
285         signal_name = g_string_new ("handle");
286         for (p = method_name; *p; p++) {
287                 if (g_ascii_isupper (*p))
288                         g_string_append_c (signal_name, '-');
289                 g_string_append_c (signal_name, g_ascii_tolower (*p));
290         }
291
292         return g_string_free (signal_name, FALSE);
293 }
294
295 static void
296 _nm_dbus_method_meta_marshal (GClosure *closure, GValue *return_value,
297                               guint n_param_values, const GValue *param_values,
298                               gpointer invocation_hint, gpointer marshal_data)
299 {
300         closure->marshal (closure, return_value, n_param_values,
301                           param_values, invocation_hint,
302                           ((GCClosure *)closure)->callback);
303
304         g_value_set_boolean (return_value, TRUE);
305 }
306
307 /* Takes (method_name, handler_func) pairs and connects the handlers to the
308  * signals on skeleton, with object as the user_data, but swapped so it comes
309  * first in the argument list, and handling the return value automatically.
310  */
311 void
312 _nm_dbus_bind_methods (gpointer object, gpointer skeleton, ...)
313 {
314         va_list ap;
315         const char *method_name;
316         char *signal_name;
317         GCallback handler;
318         GClosure *closure;
319
320         va_start (ap, skeleton);
321         while (   (method_name = va_arg (ap, const char *))
322                && (handler = va_arg (ap, GCallback))) {
323                 signal_name = signal_name_from_method_name (method_name);
324                 closure = g_cclosure_new_swap (handler, object, NULL);
325                 g_closure_set_meta_marshal (closure, NULL, _nm_dbus_method_meta_marshal);
326                 g_signal_connect_closure (skeleton, signal_name, closure, FALSE);
327                 g_free (signal_name);
328         }
329         va_end (ap);
330 }