1    	/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2    	/* NetworkManager -- Network link manager
3    	 *
4    	 * This program is free software; you can redistribute it and/or modify
5    	 * it under the terms of the GNU General Public License as published by
6    	 * the Free Software Foundation; either version 2 of the License, or
7    	 * (at your option) any later version.
8    	 *
9    	 * This program is distributed in the hope that it will be useful,
10   	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11   	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12   	 * GNU General Public License for more details.
13   	 *
14   	 * You should have received a copy of the GNU General Public License along
15   	 * with this program; if not, write to the Free Software Foundation, Inc.,
16   	 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17   	 *
18   	 * Copyright (C) 2004 - 2012 Red Hat, Inc.
19   	 * Copyright (C) 2005 - 2008 Novell, Inc.
20   	 */
21   	
22   	#include <glib.h>
23   	#include <string.h>
24   	
25   	#include "nm-dispatcher.h"
26   	#include "nm-dispatcher-action.h"
27   	#include "NetworkManagerUtils.h"
28   	#include "nm-utils.h"
29   	#include "nm-logging.h"
30   	#include "nm-dbus-manager.h"
31   	#include "nm-dbus-glib-types.h"
32   	#include "nm-glib-compat.h"
33   	
34   	static GSList *requests = NULL;
35   	
36   	static void
37   	dump_object_to_props (GObject *object, GHashTable *hash)
38   	{
39   		GParamSpec **pspecs;
40   		guint len = 0, i;
41   	
42   		pspecs = g_object_class_list_properties (G_OBJECT_GET_CLASS (object), &len);
43   		for (i = 0; i < len; i++) {
44   			value_hash_add_object_property (hash,
45   			                                pspecs[i]->name,
46   			                                object,
47   			                                pspecs[i]->name,
48   			                                pspecs[i]->value_type);
49   		}
50   		g_free (pspecs);
51   	}
52   	
53   	static void
54   	dump_dhcp4_to_props (NMDHCP4Config *config, GHashTable *hash)
55   	{
56   		GSList *options, *iter;
57   	
58   		options = nm_dhcp4_config_list_options (config);
59   		for (iter = options; iter; iter = g_slist_next (iter)) {
60   			const char *option = (const char *) iter->data;
61   			const char *val;
62   	
63   			val = nm_dhcp4_config_get_option (config, option);
64   			value_hash_add_str (hash, option, val);
65   		}
66   		g_slist_free (options);
67   	}
68   	
69   	static void
70   	dump_dhcp6_to_props (NMDHCP6Config *config, GHashTable *hash)
71   	{
72   		GSList *options, *iter;
73   	
74   		options = nm_dhcp6_config_list_options (config);
75   		for (iter = options; iter; iter = g_slist_next (iter)) {
76   			const char *option = (const char *) iter->data;
77   			const char *val;
78   	
79   			val = nm_dhcp6_config_get_option (config, option);
80   			value_hash_add_str (hash, option, val);
81   		}
82   		g_slist_free (options);
83   	}
84   	
85   	static void
86   	fill_device_props (NMDevice *device,
87   	                   GHashTable *dev_hash,
88   	                   GHashTable *ip4_hash,
89   	                   GHashTable *ip6_hash,
90   	                   GHashTable *dhcp4_hash,
91   	                   GHashTable *dhcp6_hash)
92   	{
93   		NMIP4Config *ip4_config;
94   		NMIP6Config *ip6_config;
95   		NMDHCP4Config *dhcp4_config;
96   		NMDHCP6Config *dhcp6_config;
97   	
98   		/* If the action is for a VPN, send the VPN's IP interface instead of the device's */
99   		value_hash_add_str (dev_hash, NMD_DEVICE_PROPS_IP_INTERFACE, nm_device_get_ip_iface (device));
100  		value_hash_add_str (dev_hash, NMD_DEVICE_PROPS_INTERFACE, nm_device_get_iface (device));
101  		value_hash_add_uint (dev_hash, NMD_DEVICE_PROPS_TYPE, nm_device_get_device_type (device));
102  		value_hash_add_uint (dev_hash, NMD_DEVICE_PROPS_STATE, nm_device_get_state (device));
103  		value_hash_add_object_path (dev_hash, NMD_DEVICE_PROPS_PATH, nm_device_get_path (device));
104  	
105  		ip4_config = nm_device_get_ip4_config (device);
106  		if (ip4_config)
107  			dump_object_to_props (G_OBJECT (ip4_config), ip4_hash);
108  	
109  		ip6_config = nm_device_get_ip6_config (device);
110  		if (ip6_config)
111  			dump_object_to_props (G_OBJECT (ip6_config), ip6_hash);
112  	
113  		dhcp4_config = nm_device_get_dhcp4_config (device);
114  		if (dhcp4_config)
115  			dump_dhcp4_to_props (dhcp4_config, dhcp4_hash);
116  	
117  		dhcp6_config = nm_device_get_dhcp6_config (device);
118  		if (dhcp6_config)
119  			dump_dhcp6_to_props (dhcp6_config, dhcp6_hash);
120  	}
121  	
122  	static void
123  	fill_vpn_props (NMIP4Config *ip4_config,
124  	                NMIP6Config *ip6_config,
125  	                GHashTable *ip4_hash,
126  	                GHashTable *ip6_hash)
127  	{
128  		if (ip4_config)
129  			dump_object_to_props (G_OBJECT (ip4_config), ip4_hash);
130  		if (ip6_config)
131  			dump_object_to_props (G_OBJECT (ip6_config), ip6_hash);
132  	}
133  	
134  	typedef struct {
135  		DispatcherFunc callback;
136  		gpointer user_data;
137  	} DispatchInfo;
138  	
139  	static void
140  	dispatcher_info_free (DispatchInfo *info)
141  	{
142  		requests = g_slist_remove (requests, info);
143  		g_free (info);
144  	}
145  	
146  	static const char *
147  	dispatch_result_to_string (DispatchResult result)
148  	{
149  		switch (result) {
150  		case DISPATCH_RESULT_UNKNOWN:
151  			return "unknown";
152  		case DISPATCH_RESULT_SUCCESS:
153  			return "success";
154  		case DISPATCH_RESULT_EXEC_FAILED:
155  			return "exec failed";
156  		case DISPATCH_RESULT_FAILED:
157  			return "failed";
158  		case DISPATCH_RESULT_TIMEOUT:
159  			return "timed out";
160  		}
161  		g_assert_not_reached ();
162  	}
163  	
164  	static void
165  	dispatcher_done_cb (DBusGProxy *proxy, DBusGProxyCall *call, gpointer user_data)
166  	{
167  		DispatchInfo *info = user_data;
168  		GError *error = NULL;
169  		GPtrArray *results = NULL;
170  		guint i;
171  	
172  		if (dbus_g_proxy_end_call (proxy, call, &error,
173  		                           DISPATCHER_TYPE_RESULT_ARRAY, &results,
174  		                           G_TYPE_INVALID)) {
175  			for (i = 0; results && (i < results->len); i++) {
176  				GValueArray *item = g_ptr_array_index (results, i);
177  				GValue *tmp;
178  				const char *script, *err;
179  				DispatchResult result;
180  	
181  				if (item->n_values != 3) {
182  					nm_log_dbg (LOGD_CORE, "Unexpected number of items in "
183  					                       "dispatcher result (got %d, expectd 3)",
184  					                       item->n_values);
185  					goto next;
186  				}
187  	
188  				/* Script */
189  				tmp = g_value_array_get_nth (item, 0);
190  				if (G_VALUE_TYPE (tmp) != G_TYPE_STRING) {
191  					nm_log_dbg (LOGD_CORE, "Dispatcher result %d element 0 invalid type %s",
192  					            i, G_VALUE_TYPE_NAME (tmp));
193  					goto next;
194  				}
195  				script = g_value_get_string (tmp);
196  	
197  				/* Result */
198  				tmp = g_value_array_get_nth (item, 1);
199  				if (G_VALUE_TYPE (tmp) != G_TYPE_UINT) {
200  					nm_log_dbg (LOGD_CORE, "Dispatcher result %d element 1 invalid type %s",
201  					            i, G_VALUE_TYPE_NAME (tmp));
202  					goto next;
203  				}
204  				result = g_value_get_uint (tmp);
205  	
206  				/* Error */
207  				tmp = g_value_array_get_nth (item, 2);
208  				if (G_VALUE_TYPE (tmp) != G_TYPE_STRING) {
209  					nm_log_dbg (LOGD_CORE, "Dispatcher result %d element 2 invalid type %s",
210  					            i, G_VALUE_TYPE_NAME (tmp));
211  					goto next;
212  				}
213  				err = g_value_get_string (tmp);
214  	
215  				if (result != DISPATCH_RESULT_SUCCESS) {
216  					nm_log_warn (LOGD_CORE, "Dispatcher script %s: %s",
217  					             dispatch_result_to_string (result), err);
218  				}
219  	
220  	next:
221  				g_value_array_free (item);
222  			}
223  			g_ptr_array_free (results, TRUE);
224  		} else {
225  			g_assert (error);
226  			nm_log_warn (LOGD_CORE, "Dispatcher failed: (%d) %s", error->code, error->message);
227  		}
228  	
229  		if (info->callback)
230  			info->callback (info, info->user_data);
231  	
232  		g_clear_error (&error);
233  		g_object_unref (proxy);
234  	}
235  	
236  	static const char *
237  	action_to_string (DispatcherAction action)
238  	{
239  		switch (action) {
240  		case DISPATCHER_ACTION_HOSTNAME:
241  			return "hostname";
242  		case DISPATCHER_ACTION_UP:
243  			return "up";
244  		case DISPATCHER_ACTION_PRE_DOWN:
245  			return "pre-down";
246  		case DISPATCHER_ACTION_DOWN:
247  			return "down";
248  		case DISPATCHER_ACTION_VPN_UP:
249  			return "vpn-up";
250  		case DISPATCHER_ACTION_VPN_PRE_DOWN:
251  			return "vpn-pre-down";
252  		case DISPATCHER_ACTION_VPN_DOWN:
253  			return "vpn-down";
254  		case DISPATCHER_ACTION_DHCP4_CHANGE:
255  			return "dhcp4-change";
256  		case DISPATCHER_ACTION_DHCP6_CHANGE:
257  			return "dhcp6-change";
258  		default:
259  			break;
260  		}
261  		g_assert_not_reached ();
262  	}
263  	
264  	static gconstpointer
265  	_dispatcher_call (DispatcherAction action,
266  	                  NMConnection *connection,
267  	                  NMDevice *device,
268  	                  const char *vpn_iface,
269  	                  NMIP4Config *vpn_ip4_config,
270  	                  NMIP6Config *vpn_ip6_config,
271  	                  DispatcherFunc callback,
272  	                  gpointer user_data)
273  	{
274  		DBusGProxy *proxy;
275  		DBusGConnection *g_connection;
276  		GHashTable *connection_hash;
277  		GHashTable *connection_props;
278  		GHashTable *device_props;
279  		GHashTable *device_ip4_props;
280  		GHashTable *device_ip6_props;
281  		GHashTable *device_dhcp4_props;
282  		GHashTable *device_dhcp6_props;
283  		GHashTable *vpn_ip4_props;
284  		GHashTable *vpn_ip6_props;
285  		DBusGProxyCall *call;
286  		DispatchInfo *info;
287  	
288  		/* All actions except 'hostname' require a device */
289  		if (action != DISPATCHER_ACTION_HOSTNAME)
290  			g_return_val_if_fail (NM_IS_DEVICE (device), NULL);
291  		/* VPN actions require at least an IPv4 config (for now) */
292  		if (action == DISPATCHER_ACTION_VPN_UP)
293  			g_return_val_if_fail (vpn_ip4_config != NULL, NULL);
294  	
295  		g_connection = nm_dbus_manager_get_connection (nm_dbus_manager_get ());
296  		proxy = dbus_g_proxy_new_for_name (g_connection,
297  		                                   NM_DISPATCHER_DBUS_SERVICE,
298  		                                   NM_DISPATCHER_DBUS_PATH,
299  		                                   NM_DISPATCHER_DBUS_IFACE);
300  		if (!proxy) {
301  			nm_log_err (LOGD_CORE, "could not get dispatcher proxy!");
302  			return NULL;
303  		}
304  	
305  		if (connection) {
306  			connection_hash = nm_connection_to_hash (connection, NM_SETTING_HASH_FLAG_NO_SECRETS);
307  	
308  			connection_props = value_hash_create ();
309  			value_hash_add_object_path (connection_props,
310  			                            NMD_CONNECTION_PROPS_PATH,
311  			                            nm_connection_get_path (connection));
312  		} else {
313  			connection_hash = value_hash_create ();
314  			connection_props = value_hash_create ();
315  		}
316  	
317  		device_props = value_hash_create ();
318  		device_ip4_props = value_hash_create ();
319  		device_ip6_props = value_hash_create ();
320  		device_dhcp4_props = value_hash_create ();
321  		device_dhcp6_props = value_hash_create ();
322  		vpn_ip4_props = value_hash_create ();
323  		vpn_ip6_props = value_hash_create ();
324  	
325  		/* hostname actions only send the hostname */
326  		if (action != DISPATCHER_ACTION_HOSTNAME) {
327  			fill_device_props (device,
328  			                   device_props,
329  			                   device_ip4_props,
330  			                   device_ip6_props,
331  			                   device_dhcp4_props,
332  			                   device_dhcp6_props);
333  			if (vpn_iface)
334  				fill_vpn_props (vpn_ip4_config, vpn_ip6_config, vpn_ip4_props, vpn_ip6_props);
335  		}
336  	
337  		info = g_malloc0 (sizeof (*info));
338  		info->callback = callback;
339  		info->user_data = user_data;
340  	
341  		/* Send the action to the dispatcher */
(1) Event returned_pointer: Pointer "call" returned by "dbus_g_proxy_begin_call_with_timeout(proxy, "Action", dispatcher_done_cb(DBusGProxy *, DBusGProxyCall *, gpointer), info, (GDestroyNotify)dispatcher_info_free(DispatchInfo *), 15000, 64UL, action_to_string(action), dbus_g_type_get_map("GHashTable", 64UL, dbus_g_type_get_map("GHashTable", 64UL, g_value_get_type())), connection_hash, dbus_g_type_get_map("GHashTable", 64UL, g_value_get_type()), connection_props, dbus_g_type_get_map("GHashTable", 64UL, g_value_get_type()), device_props, dbus_g_type_get_map("GHashTable", 64UL, g_value_get_type()), device_ip4_props, dbus_g_type_get_map("GHashTable", 64UL, g_value_get_type()), device_ip6_props, dbus_g_type_get_map("GHashTable", 64UL, g_value_get_type()), device_dhcp4_props, dbus_g_type_get_map("GHashTable", 64UL, g_value_get_type()), device_dhcp6_props, 64UL, (vpn_iface ? vpn_iface : ""), dbus_g_type_get_map("GHashTable", 64UL, g_value_get_type()), vpn_ip4_props, dbus_g_type_get_map("GHashTable", 64UL, g_value_get_type()), vpn_ip6_props, 0UL)" is never used.
342  		call = dbus_g_proxy_begin_call_with_timeout (proxy, "Action",
343  		                                             dispatcher_done_cb,
344  		                                             info,
345  		                                             (GDestroyNotify) dispatcher_info_free,
346  		                                             15000,
347  		                                             G_TYPE_STRING, action_to_string (action),
348  		                                             DBUS_TYPE_G_MAP_OF_MAP_OF_VARIANT, connection_hash,
349  		                                             DBUS_TYPE_G_MAP_OF_VARIANT, connection_props,
350  		                                             DBUS_TYPE_G_MAP_OF_VARIANT, device_props,
351  		                                             DBUS_TYPE_G_MAP_OF_VARIANT, device_ip4_props,
352  		                                             DBUS_TYPE_G_MAP_OF_VARIANT, device_ip6_props,
353  		                                             DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp4_props,
354  		                                             DBUS_TYPE_G_MAP_OF_VARIANT, device_dhcp6_props,
355  		                                             G_TYPE_STRING, vpn_iface ? vpn_iface : "",
356  		                                             DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip4_props,
357  		                                             DBUS_TYPE_G_MAP_OF_VARIANT, vpn_ip6_props,
358  		                                             G_TYPE_INVALID);
359  		g_hash_table_destroy (connection_hash);
360  		g_hash_table_destroy (connection_props);
361  		g_hash_table_destroy (device_props);
362  		g_hash_table_destroy (device_ip4_props);
363  		g_hash_table_destroy (device_ip6_props);
364  		g_hash_table_destroy (device_dhcp4_props);
365  		g_hash_table_destroy (device_dhcp6_props);
366  		g_hash_table_destroy (vpn_ip4_props);
367  		g_hash_table_destroy (vpn_ip6_props);
368  	
369  		/* Track the request in case of cancelation */
370  		requests = g_slist_append (requests, info);
371  	
372  		return info;
373  	}
374  	
375  	gconstpointer
376  	nm_dispatcher_call (DispatcherAction action,
377  	                    NMConnection *connection,
378  	                    NMDevice *device,
379  	                    DispatcherFunc callback,
380  	                    gpointer user_data)
381  	{
382  		return _dispatcher_call (action, connection, device, NULL, NULL, NULL, callback, user_data);
383  	}
384  	
385  	gconstpointer
386  	nm_dispatcher_call_vpn (DispatcherAction action,
387  	                        NMConnection *connection,
388  	                        NMDevice *device,
389  	                        const char *vpn_iface,
390  	                        NMIP4Config *vpn_ip4_config,
391  	                        NMIP6Config *vpn_ip6_config,
392  	                        DispatcherFunc callback,
393  	                        gpointer user_data)
394  	{
395  		return _dispatcher_call (action, connection, device, vpn_iface, vpn_ip4_config, vpn_ip6_config, callback, user_data);
396  	}
397  	
398  	void
399  	nm_dispatcher_call_cancel (gconstpointer call)
400  	{
401  		/* 'call' is really a DispatchInfo pointer, just opaque to callers.
402  		 * Look it up in our requests list, but don't access it directly before
403  		 * we've made sure it's a valid request,since it may have long since been
404  		 * freed.  Canceling just means the callback doesn't get called, so set
405  		 * the DispatcherInfo's callback to NULL.
406  		 */
407  		if (g_slist_find (requests, call))
408  			((DispatchInfo *) call)->callback = NULL;
409  	}
410  	
411