1    	/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2    	/* NetworkManager system settings service
3    	 *
4    	 * Dan Williams <dcbw@redhat.com>
5    	 * S��ren Sandmann <sandmann@daimi.au.dk>
6    	 *
7    	 * This program is free software; you can redistribute it and/or modify
8    	 * it under the terms of the GNU General Public License as published by
9    	 * the Free Software Foundation; either version 2 of the License, or
10   	 * (at your option) any later version.
11   	 *
12   	 * This program is distributed in the hope that it will be useful,
13   	 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   	 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   	 * GNU General Public License for more details.
16   	 *
17   	 * You should have received a copy of the GNU General Public License along
18   	 * with this program; if not, write to the Free Software Foundation, Inc.,
19   	 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20   	 *
21   	 * Copyright (C) 2007 - 2011 Red Hat, Inc.
22   	 */
23   	
24   	#include <config.h>
25   	#include <string.h>
26   	#include <unistd.h>
27   	#include <errno.h>
28   	#include <net/ethernet.h>
29   	#include <netinet/ether.h>
30   	
31   	#include <gmodule.h>
32   	#include <glib-object.h>
33   	#include <glib/gi18n.h>
34   	#include <gio/gio.h>
35   	
36   	#include <dbus/dbus.h>
37   	#include <dbus/dbus-glib.h>
38   	#include <dbus/dbus-glib-lowlevel.h>
39   	
40   	#include <nm-setting-connection.h>
41   	
42   	#include "common.h"
43   	#include "nm-dbus-glib-types.h"
44   	#include "plugin.h"
45   	#include "nm-system-config-interface.h"
46   	#include "nm-settings-error.h"
47   	#include "nm-config.h"
48   	
49   	#include "nm-ifcfg-connection.h"
50   	#include "nm-inotify-helper.h"
51   	#include "shvar.h"
52   	#include "reader.h"
53   	#include "writer.h"
54   	#include "utils.h"
55   	
56   	#define DBUS_SERVICE_NAME "com.redhat.ifcfgrh1"
57   	#define DBUS_OBJECT_PATH "/com/redhat/ifcfgrh1"
58   	
59   	static gboolean impl_ifcfgrh_get_ifcfg_details (SCPluginIfcfg *plugin,
60   	                                                const char *in_ifcfg,
61   	                                                const char **out_uuid,
62   	                                                const char **out_path,
63   	                                                GError **error);
64   	
65   	#include "nm-ifcfg-rh-glue.h"
66   	
67   	static void connection_new_or_changed (SCPluginIfcfg *plugin,
68   	                                       const char *path,
69   	                                       NMIfcfgConnection *existing,
70   	                                       char **out_old_path);
71   	
72   	static void system_config_interface_init (NMSystemConfigInterface *system_config_interface_class);
73   	
74   	G_DEFINE_TYPE_EXTENDED (SCPluginIfcfg, sc_plugin_ifcfg, G_TYPE_OBJECT, 0,
75   							G_IMPLEMENT_INTERFACE (NM_TYPE_SYSTEM_CONFIG_INTERFACE,
76   												   system_config_interface_init))
77   	
78   	#define SC_PLUGIN_IFCFG_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SC_TYPE_PLUGIN_IFCFG, SCPluginIfcfgPrivate))
79   	
80   	
81   	typedef struct {
82   		GHashTable *connections;  /* uuid::connection */
83   	
84   		gboolean initialized;
85   		gulong ih_event_id;
86   		int sc_network_wd;
87   		GFileMonitor *hostname_monitor;
88   		guint hostname_monitor_id;
89   		char *hostname;
90   	
91   		GFileMonitor *ifcfg_monitor;
92   		guint ifcfg_monitor_id;
93   	
94   		DBusGConnection *bus;
95   	} SCPluginIfcfgPrivate;
96   	
97   	
98   	static void
99   	connection_unmanaged_changed (NMIfcfgConnection *connection,
100  	                              GParamSpec *pspec,
101  	                              gpointer user_data)
102  	{
103  		g_signal_emit_by_name (SC_PLUGIN_IFCFG (user_data), NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED);
104  	}
105  	
106  	static void
107  	connection_ifcfg_changed (NMIfcfgConnection *connection, gpointer user_data)
108  	{
109  		SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (user_data);
110  		const char *path;
111  	
112  		path = nm_ifcfg_connection_get_path (connection);
113  		g_return_if_fail (path != NULL);
114  	
115  		connection_new_or_changed (plugin, path, connection, NULL);
116  	}
117  	
118  	static void
119  	connection_removed_cb (NMSettingsConnection *obj, gpointer user_data)
120  	{
121  		g_hash_table_remove (SC_PLUGIN_IFCFG_GET_PRIVATE (user_data)->connections,
122  		                     nm_connection_get_uuid (NM_CONNECTION (obj)));
123  	}
124  	
125  	static NMIfcfgConnection *
126  	_internal_new_connection (SCPluginIfcfg *self,
127  	                          const char *path,
128  	                          NMConnection *source,
129  	                          GError **error)
130  	{
131  		SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (self);
132  		NMIfcfgConnection *connection;
133  		const char *cid;
134  		GError *local = NULL;
135  		gboolean ignore_error = FALSE;
136  	
137  		if (!source) {
138  			PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "parsing %s ... ", path);
139  		}
140  	
141  		connection = nm_ifcfg_connection_new (source, path, &local, &ignore_error);
142  		if (!connection) {
143  			if (!ignore_error) {
144  				PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "    error: %s",
145  				              (local && local->message) ? local->message : "(unknown)");
146  			}
147  			g_propagate_error (error, local);
148  			return NULL;
149  		}
150  	
151  		cid = nm_connection_get_id (NM_CONNECTION (connection));
152  		g_assert (cid);
153  	
154  		g_hash_table_insert (priv->connections,
155  		                     g_strdup (nm_connection_get_uuid (NM_CONNECTION (connection))),
156  		                     connection);
157  		PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "    read connection '%s'", cid);
158  		g_signal_connect (connection, NM_SETTINGS_CONNECTION_REMOVED,
159  		                  G_CALLBACK (connection_removed_cb),
160  		                  self);
161  	
162  		if (nm_ifcfg_connection_get_unmanaged_spec (connection)) {
163  			const char *spec;
164  			const char *device_id;
165  	
166  			spec = nm_ifcfg_connection_get_unmanaged_spec (connection);
167  			device_id = strchr (spec, ':');
168  			if (device_id)
169  				device_id++;
170  			else
171  				device_id = spec;
172  			PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "Ignoring connection '%s' / device '%s' "
173  			              "due to NM_CONTROLLED=no.", cid, device_id);
174  		} else {
175  			/* Wait for the connection to become unmanaged once it knows the
176  			 * hardware IDs of its device, if/when the device gets plugged in.
177  			 */
178  			g_signal_connect (G_OBJECT (connection), "notify::" NM_IFCFG_CONNECTION_UNMANAGED,
179  			                  G_CALLBACK (connection_unmanaged_changed), self);
180  		}
181  	
182  		/* watch changes of ifcfg hardlinks */
183  		g_signal_connect (G_OBJECT (connection), "ifcfg-changed",
184  		                  G_CALLBACK (connection_ifcfg_changed), self);
185  	
186  		return connection;
187  	}
188  	
189  	/* Monitoring */
190  	
191  	static void
192  	remove_connection (SCPluginIfcfg *self, NMIfcfgConnection *connection)
193  	{
194  		SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (self);
195  		gboolean managed = FALSE;
196  	
197  		g_return_if_fail (self != NULL);
198  		g_return_if_fail (connection != NULL);
199  	
200  		managed = !nm_ifcfg_connection_get_unmanaged_spec (connection);
201  	
202  		g_object_ref (connection);
203  		g_hash_table_remove (priv->connections, nm_connection_get_uuid (NM_CONNECTION (connection)));
204  		nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (connection));
205  		g_object_unref (connection);
206  	
207  		/* Emit unmanaged changes _after_ removing the connection */
208  		if (managed == FALSE)
209  			g_signal_emit_by_name (self, NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED);
210  	}
211  	
212  	static NMIfcfgConnection *
213  	find_by_path (SCPluginIfcfg *self, const char *path)
214  	{
215  		SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (self);
216  		GHashTableIter iter;
217  		NMIfcfgConnection *candidate = NULL;
218  	
219  		g_return_val_if_fail (path != NULL, NULL);
220  	
221  		g_hash_table_iter_init (&iter, priv->connections);
222  		while (g_hash_table_iter_next (&iter, NULL, (gpointer) &candidate)) {
223  			if (g_str_equal (path, nm_ifcfg_connection_get_path (candidate)))
224  				return candidate;
225  		}
226  		return NULL;
227  	}
228  	
229  	static NMIfcfgConnection *
230  	find_by_uuid_from_path (SCPluginIfcfg *self, const char *path)
231  	{
232  		SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (self);
233  		char *uuid;
234  	
235  		g_return_val_if_fail (path != NULL, NULL);
236  	
237  		uuid = uuid_from_file (path);
238  		if (uuid)
239  			return g_hash_table_lookup (priv->connections, uuid);
240  		else
241  			return NULL;
242  	}
243  	
244  	static void
245  	connection_new_or_changed (SCPluginIfcfg *self,
246  	                           const char *path,
247  	                           NMIfcfgConnection *existing,
248  	                           char **out_old_path)
249  	{
250  		NMIfcfgConnection *new;
251  		GError *error = NULL;
252  		gboolean ignore_error = FALSE;
253  		const char *new_unmanaged = NULL, *old_unmanaged = NULL;
254  	
255  		g_return_if_fail (self != NULL);
256  		g_return_if_fail (path != NULL);
257  	
258  		if (out_old_path)
259  			*out_old_path = NULL;
260  	
261  		if (!existing) {
262  			/* See if it's a rename */
263  			existing = find_by_uuid_from_path (self, path);
264  			if (existing) {
265  				const char *old_path = nm_ifcfg_connection_get_path (existing);
266  				PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "renaming %s -> %s", old_path, path);
267  				if (out_old_path)
268  					*out_old_path = g_strdup (old_path);
269  				nm_ifcfg_connection_set_path (existing, path);
270  			}
271  		}
272  	
273  		if (!existing) {
274  			/* New connection */
275  			new = _internal_new_connection (self, path, NULL, NULL);
276  			if (new) {
277  				if (nm_ifcfg_connection_get_unmanaged_spec (new)) {
278  					g_signal_emit_by_name (self, NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED);
279  				} else {
280  					/* Only managed connections are announced to the settings service */
281  					g_signal_emit_by_name (self, NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED, new);
282  				}
283  			}
284  			return;
285  		}
286  	
287  		new = (NMIfcfgConnection *) nm_ifcfg_connection_new (NULL, path, &error, &ignore_error);
288  		if (!new) {
289  			/* errors reading connection; remove it */
290  			if (!ignore_error) {
291  				PLUGIN_WARN (IFCFG_PLUGIN_NAME, "    error: %s",
292  				             (error && error->message) ? error->message : "(unknown)");
293  			}
294  			g_clear_error (&error);
295  	
296  			PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "removed %s.", path);
297  			remove_connection (self, existing);
298  			return;
299  		}
300  	
301  		/* Successfully read connection changes */
302  	
303  		old_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (NM_IFCFG_CONNECTION (existing));
304  		new_unmanaged = nm_ifcfg_connection_get_unmanaged_spec (NM_IFCFG_CONNECTION (new));
305  	
306  		/* When interface is unmanaged or the connections and unmanaged specs are the same
307  		 * there's nothing to do */
308  		if (   (g_strcmp0 (old_unmanaged, new_unmanaged) == 0 && new_unmanaged != NULL)
309  		    || (   nm_connection_compare (NM_CONNECTION (existing),
310  		                                  NM_CONNECTION (new),
311  		                                  NM_SETTING_COMPARE_FLAG_IGNORE_AGENT_OWNED_SECRETS |
312  		                                    NM_SETTING_COMPARE_FLAG_IGNORE_NOT_SAVED_SECRETS)
313  		        && g_strcmp0 (old_unmanaged, new_unmanaged) == 0)) {
314  	
315  			g_object_unref (new);
316  			return;
317  		}
318  	
319  		PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "updating %s", path);
320  	
321  		if (new_unmanaged) {
322  			if (!old_unmanaged) {
323  				g_object_ref (existing);
324  				/* Unexport the connection by telling the settings service it's
325  				 * been removed, and notify the settings service by signalling that
326  				 * unmanaged specs have changed.
327  				 */
328  				nm_settings_connection_signal_remove (NM_SETTINGS_CONNECTION (existing));
329  				/* Remove the path so that claim_connection() doesn't complain later when
330  				 * interface gets managed and connection is re-added. */
331  				nm_connection_set_path (NM_CONNECTION (existing), NULL);
332  	
333  				g_object_set (existing, NM_IFCFG_CONNECTION_UNMANAGED, new_unmanaged, NULL);
334  				g_signal_emit_by_name (self, NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED);
335  				g_object_unref (existing);
336  			}
337  		} else {
338  			if (old_unmanaged) {  /* now managed */
339  				const char *cid = nm_connection_get_id (NM_CONNECTION (new));
340  	
341  				PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "Managing connection '%s' and its "
342  				              "device because NM_CONTROLLED was true.", cid);
343  				g_signal_emit_by_name (self, NM_SYSTEM_CONFIG_INTERFACE_CONNECTION_ADDED, existing);
344  			}
345  	
346  			if (!nm_settings_connection_replace_settings (NM_SETTINGS_CONNECTION (existing),
347  			                                              NM_CONNECTION (new),
348  			                                              FALSE,  /* don't set Unsaved */
349  			                                              &error)) {
350  				/* Shouldn't ever get here as 'new' was verified by the reader already */
351  				g_assert_no_error (error);
352  			}
353  	
354  			/* Update unmanaged status */
355  			g_object_set (existing, NM_IFCFG_CONNECTION_UNMANAGED, new_unmanaged, NULL);
356  			g_signal_emit_by_name (self, NM_SYSTEM_CONFIG_INTERFACE_UNMANAGED_SPECS_CHANGED);
357  		}
358  		g_object_unref (new);
359  	}
360  	
361  	static void
362  	ifcfg_dir_changed (GFileMonitor *monitor,
363  	                   GFile *file,
364  	                   GFile *other_file,
365  	                   GFileMonitorEvent event_type,
366  	                   gpointer user_data)
367  	{
368  		SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (user_data);
369  		char *path, *ifcfg_path;
370  		NMIfcfgConnection *connection;
371  	
372  		path = g_file_get_path (file);
373  		if (utils_should_ignore_file (path, FALSE)) {
374  			g_free (path);
375  			return;
376  		}
377  	
378  		/* Given any ifcfg, keys, or routes file, get the ifcfg file path */
379  		ifcfg_path = utils_get_ifcfg_path (path);
380  		if (ifcfg_path) {
381  			connection = find_by_path (plugin, ifcfg_path);
382  			switch (event_type) {
383  			case G_FILE_MONITOR_EVENT_DELETED:
384  				PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "removed %s.", ifcfg_path);
385  				if (connection)
386  					remove_connection (plugin, connection);
387  				break;
388  			case G_FILE_MONITOR_EVENT_CREATED:
389  			case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
390  				/* Update or new */
391  				connection_new_or_changed (plugin, ifcfg_path, connection, NULL);
392  				break;
393  			default:
394  				break;
395  			}
396  			g_free (ifcfg_path);
397  		}
398  		g_free (path);
399  	}
400  	
401  	static void
402  	setup_ifcfg_monitoring (SCPluginIfcfg *plugin)
403  	{
404  		SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (plugin);
405  		GFile *file;
406  		GFileMonitor *monitor;
407  	
408  		file = g_file_new_for_path (IFCFG_DIR "/");
409  		monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
410  		g_object_unref (file);
411  	
412  		if (monitor) {
413  			priv->ifcfg_monitor_id = g_signal_connect (monitor, "changed",
414  			                                           G_CALLBACK (ifcfg_dir_changed), plugin);
415  			priv->ifcfg_monitor = monitor;
416  		}
417  	}
418  	
419  	static void
420  	read_connections (SCPluginIfcfg *plugin)
421  	{
422  		SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (plugin);
423  		GDir *dir;
424  		GError *err = NULL;
425  		const char *item;
426  		GHashTable *oldconns;
427  		GHashTableIter iter;
428  		gpointer key, value;
429  		NMIfcfgConnection *connection;
430  	
431  		dir = g_dir_open (IFCFG_DIR, 0, &err);
432  		if (!dir) {
433  			PLUGIN_WARN (IFCFG_PLUGIN_NAME, "Can not read directory '%s': %s", IFCFG_DIR, err->message);
434  			g_error_free (err);
435  			return;
436  		}
437  	
438  		oldconns = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, NULL);
439  		g_hash_table_iter_init (&iter, priv->connections);
440  		while (g_hash_table_iter_next (&iter, NULL, &value))
441  			g_hash_table_insert (oldconns, g_strdup (nm_ifcfg_connection_get_path (value)), value);
442  	
443  		while ((item = g_dir_read_name (dir))) {
444  			char *full_path, *old_path;
445  	
446  			if (utils_should_ignore_file (item, TRUE))
447  				continue;
448  	
449  			full_path = g_build_filename (IFCFG_DIR, item, NULL);
450  			if (!utils_get_ifcfg_name (full_path, TRUE))
451  				goto next;
452  	
453  			connection = g_hash_table_lookup (oldconns, full_path);
454  			g_hash_table_remove (oldconns, full_path);
455  			connection_new_or_changed (plugin, full_path, connection, &old_path);
456  	
457  			if (old_path) {
458  				g_hash_table_remove (oldconns, old_path);
459  				g_free (old_path);
460  			}
461  	
462  		next:
463  			g_free (full_path);
464  		}
465  	
466  		g_dir_close (dir);
467  	
468  		g_hash_table_iter_init (&iter, oldconns);
469  		while (g_hash_table_iter_next (&iter, &key, &value)) {
470  			PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "removed %s.", (char *)key);
471  			g_hash_table_iter_remove (&iter);
472  			remove_connection (plugin, value);
473  		}
474  	
475  		g_hash_table_destroy (oldconns);
476  	}
477  	
478  	static GSList *
479  	get_connections (NMSystemConfigInterface *config)
480  	{
481  		SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (config);
482  		SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (plugin);
483  		GSList *list = NULL;
484  		GHashTableIter iter;
485  		NMIfcfgConnection *connection;
486  	
487  		if (!priv->initialized) {
488  			if (nm_config_get_monitor_connection_files (nm_config_get ()))
489  				setup_ifcfg_monitoring (plugin);
490  			read_connections (plugin);
491  			priv->initialized = TRUE;
492  		}
493  	
494  		g_hash_table_iter_init (&iter, priv->connections);
495  		while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) {
496  			if (!nm_ifcfg_connection_get_unmanaged_spec (connection))
497  				list = g_slist_prepend (list, connection);
498  		}
499  	
500  		return list;
501  	}
502  	
503  	static void
504  	reload_connections (NMSystemConfigInterface *config)
505  	{
506  		SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (config);
507  	
508  		read_connections (plugin);
509  	}
510  	
511  	static GSList *
512  	get_unmanaged_specs (NMSystemConfigInterface *config)
513  	{
514  		SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (config);
515  		GSList *list = NULL, *list_iter;
516  		GHashTableIter iter;
517  		NMIfcfgConnection *connection;
518  		const char *spec;
519  		gboolean found;
520  	
521  		g_hash_table_iter_init (&iter, priv->connections);
522  		while (g_hash_table_iter_next (&iter, NULL, (gpointer) &connection)) {
523  			spec = nm_ifcfg_connection_get_unmanaged_spec (connection);
524  			if (spec) {
525  				/* Ignore duplicates */
526  				for (list_iter = list, found = FALSE; list_iter; list_iter = g_slist_next (list_iter)) {
527  					if (g_str_equal (list_iter->data, spec)) {
528  						found = TRUE;
529  						break;
530  					}
531  				}
532  				if (!found)
533  					list = g_slist_prepend (list, g_strdup (spec));
534  			}
535  		}
536  		return list;
537  	}
538  	
539  	static NMSettingsConnection *
540  	add_connection (NMSystemConfigInterface *config,
541  	                NMConnection *connection,
542  	                gboolean save_to_disk,
543  	                GError **error)
544  	{
545  		SCPluginIfcfg *self = SC_PLUGIN_IFCFG (config);
546  		NMIfcfgConnection *added = NULL;
547  		char *path = NULL;
548  	
549  		/* Ensure we reject attempts to add the connection long before we're
550  		 * asked to write it to disk.
551  		 */
552  		if (!writer_can_write_connection (connection, error))
553  			return NULL;
554  	
555  		if (save_to_disk) {
556  			if (!writer_new_connection (connection, IFCFG_DIR, &path, error))
557  				return NULL;
558  		}
559  	
560  		added = _internal_new_connection (self, path, connection, error);
561  		g_free (path);
562  		return (NMSettingsConnection *) added;
563  	}
564  	
565  	#define SC_NETWORK_FILE "/etc/sysconfig/network"
566  	#define HOSTNAME_FILE   "/etc/hostname"
567  	
568  	static char *
569  	plugin_get_hostname (SCPluginIfcfg *plugin)
570  	{
571  		shvarFile *network;
572  		char *hostname;
573  		gboolean ignore_localhost;
574  	
575  		if (g_file_get_contents (HOSTNAME_FILE, &hostname, NULL, NULL)) {
576  			g_strchomp (hostname);
577  			return hostname;
578  		}
579  	
580  		network = svNewFile (SC_NETWORK_FILE);
581  		if (!network) {
582  			PLUGIN_WARN (IFCFG_PLUGIN_NAME, "Could not get hostname: failed to read " SC_NETWORK_FILE);
583  			return NULL;
584  		}
585  	
(2) Event example_assign: Example1: Assigning: "hostname" = return value from "svGetValue(network, "HOSTNAME", 0)".
Also see events: [returned_null][example_checked][example_assign][example_checked][example_assign][example_checked][example_assign][example_checked][example_assign][example_checked][var_assigned][dereference]
586  		hostname = svGetValue (network, "HOSTNAME", FALSE);
587  		ignore_localhost = svTrueValue (network, "NM_IGNORE_HOSTNAME_LOCALHOST", FALSE);
588  		if (ignore_localhost) {
589  			/* Ignore a hostname of 'localhost' or 'localhost.localdomain' to preserve
590  			 * 'network' service behavior.
591  			 */
(3) Event example_checked: Example1 (cont.): "hostname" has its value checked in "hostname".
Also see events: [returned_null][example_assign][example_assign][example_checked][example_assign][example_checked][example_assign][example_checked][example_assign][example_checked][var_assigned][dereference]
592  			if (hostname && (!strcmp (hostname, "localhost") || !strcmp (hostname, "localhost.localdomain"))) {
593  				g_free (hostname);
594  				hostname = NULL;
595  			}
596  		}
597  	
598  		svCloseFile (network);
599  		return hostname;
600  	}
601  	
602  	static gboolean
603  	plugin_set_hostname (SCPluginIfcfg *plugin, const char *hostname)
604  	{
605  		SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (plugin);
606  		shvarFile *network;
607  	
608  		if (!g_file_set_contents (HOSTNAME_FILE, hostname, -1, NULL)) {
609  			PLUGIN_WARN (IFCFG_PLUGIN_NAME, "Could not save hostname: failed to create/open " HOSTNAME_FILE);
610  			return FALSE;
611  		}
612  	
613  		g_free (priv->hostname);
614  		priv->hostname = g_strdup (hostname);
615  	
616  		/* Remove "HOSTNAME" from SC_NETWORK_FILE, if present */
617  		network = svNewFile (SC_NETWORK_FILE);
618  		if (network) {
619  			svSetValue (network, "HOSTNAME", NULL, FALSE);
620  			svWriteFile (network, 0644);
621  			svCloseFile (network);
622  		}
623  	
624  		return TRUE;
625  	}
626  	
627  	static void
628  	hostname_maybe_changed (SCPluginIfcfg *plugin)
629  	{
630  		SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (plugin);
631  		char *new_hostname;
632  	
633  		new_hostname = plugin_get_hostname (plugin);
634  		if (   (new_hostname && !priv->hostname)
635  		    || (!new_hostname && priv->hostname)
636  		    || (priv->hostname && new_hostname && strcmp (priv->hostname, new_hostname))) {
637  			g_free (priv->hostname);
638  			priv->hostname = new_hostname;
639  			g_object_notify (G_OBJECT (plugin), NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME);
640  		} else
641  			g_free (new_hostname);
642  	}
643  	
644  	static void
645  	sc_network_changed_cb (NMInotifyHelper *ih,
646  	                       struct inotify_event *evt,
647  	                       const char *path,
648  	                       gpointer user_data)
649  	{
650  		SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (user_data);
651  		SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (plugin);
652  	
653  		if (evt->wd != priv->sc_network_wd)
654  			return;
655  	
656  		hostname_maybe_changed (plugin);
657  	}
658  	
659  	static void
660  	hostname_changed_cb (GFileMonitor *monitor,
661  	                     GFile *file,
662  	                     GFile *other_file,
663  	                     GFileMonitorEvent event_type,
664  	                     gpointer user_data)
665  	{
666  		SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (user_data);
667  	
668  		hostname_maybe_changed (plugin);
669  	}
670  	
671  	static gboolean
672  	impl_ifcfgrh_get_ifcfg_details (SCPluginIfcfg *plugin,
673  	                                const char *in_ifcfg,
674  	                                const char **out_uuid,
675  	                                const char **out_path,
676  	                                GError **error)
677  	{
678  		NMIfcfgConnection *connection;
679  		NMSettingConnection *s_con;
680  		const char *uuid;
681  		const char *path;
682  	
683  		if (!g_path_is_absolute (in_ifcfg)) {
684  			g_set_error (error,
685  			             NM_SETTINGS_ERROR,
686  			             NM_SETTINGS_ERROR_INVALID_CONNECTION,
687  			             "ifcfg path '%s' is not absolute", in_ifcfg);
688  			return FALSE;
689  		}
690  	
691  		connection = find_by_path (plugin, in_ifcfg);
692  		if (!connection || nm_ifcfg_connection_get_unmanaged_spec (connection)) {
693  			g_set_error (error,
694  			             NM_SETTINGS_ERROR,
695  			             NM_SETTINGS_ERROR_INVALID_CONNECTION,
696  			             "ifcfg file '%s' unknown", in_ifcfg);
697  			return FALSE;
698  		}
699  	
700  		s_con = nm_connection_get_setting_connection (NM_CONNECTION (connection));
701  		if (!s_con) {
702  			g_set_error (error,
703  			             NM_SETTINGS_ERROR,
704  			             NM_SETTINGS_ERROR_INTERNAL_ERROR,
705  			             "unable to retrieve the connection setting");
706  			return FALSE;
707  		}
708  	
709  		uuid = nm_setting_connection_get_uuid (s_con);
710  		if (!uuid) {
711  			g_set_error (error,
712  			             NM_SETTINGS_ERROR,
713  			             NM_SETTINGS_ERROR_INTERNAL_ERROR,
714  			             "unable to get the UUID");
715  			return FALSE;
716  		}
717  		
718  		path = nm_connection_get_path (NM_CONNECTION (connection));
719  		if (!path) {
720  			g_set_error (error,
721  			             NM_SETTINGS_ERROR,
722  			             NM_SETTINGS_ERROR_INTERNAL_ERROR,
723  			             "unable to get the connection D-Bus path");
724  			return FALSE;
725  		}
726  	
727  		*out_uuid = g_strdup (uuid);
728  		*out_path = g_strdup (path);
729  	
730  		return TRUE;
731  	}
732  	
733  	static void
734  	init (NMSystemConfigInterface *config)
735  	{
736  	}
737  	
738  	static void
739  	sc_plugin_ifcfg_init (SCPluginIfcfg *plugin)
740  	{
741  		SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (plugin);
742  		NMInotifyHelper *ih;
743  		GError *error = NULL;
744  		gboolean success = FALSE;
745  		GFile *file;
746  		GFileMonitor *monitor;
747  	
748  		priv->connections = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
749  	
750  		/* We watch SC_NETWORK_FILE via NMInotifyHelper (which doesn't track file creation but
751  		 * *does* track modifications made via other hard links), since we expect it to always
752  		 * exist. But we watch HOSTNAME_FILE via GFileMonitor (which has the opposite
753  		 * semantics), since /etc/hostname might not exist, but is unlikely to have hard
754  		 * links. bgo 532815 is the bug for being able to just use GFileMonitor for both.
755  		 */
756  	
757  		ih = nm_inotify_helper_get ();
758  		priv->ih_event_id = g_signal_connect (ih, "event", G_CALLBACK (sc_network_changed_cb), plugin);
759  		priv->sc_network_wd = nm_inotify_helper_add_watch (ih, SC_NETWORK_FILE);
760  	
761  		file = g_file_new_for_path (HOSTNAME_FILE);
762  		monitor = g_file_monitor_file (file, G_FILE_MONITOR_NONE, NULL, NULL);
763  		g_object_unref (file);
764  		if (monitor) {
765  			priv->hostname_monitor_id =
766  				g_signal_connect (monitor, "changed", G_CALLBACK (hostname_changed_cb), plugin);
767  			priv->hostname_monitor = monitor;
768  		}
769  	
770  		priv->hostname = plugin_get_hostname (plugin);
771  	
772  		priv->bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, &error);
773  		if (!priv->bus) {
774  			PLUGIN_WARN (IFCFG_PLUGIN_NAME, "Couldn't connect to D-Bus: %s",
775  			             error->message);
776  			g_clear_error (&error);
777  		} else {
778  			DBusConnection *tmp;
779  			DBusGProxy *proxy;
780  			int result;
781  	
782  			tmp = dbus_g_connection_get_connection (priv->bus);
783  			dbus_connection_set_exit_on_disconnect (tmp, FALSE);
784  	
785  			proxy = dbus_g_proxy_new_for_name (priv->bus,
786  			                                   "org.freedesktop.DBus",
787  			                                   "/org/freedesktop/DBus",
788  			                                   "org.freedesktop.DBus");
789  	
790  			if (!dbus_g_proxy_call (proxy, "RequestName", &error,
791  			                        G_TYPE_STRING, DBUS_SERVICE_NAME,
792  			                        G_TYPE_UINT, DBUS_NAME_FLAG_DO_NOT_QUEUE,
793  			                        G_TYPE_INVALID,
794  			                        G_TYPE_UINT, &result,
795  			                        G_TYPE_INVALID)) {
796  				PLUGIN_WARN (IFCFG_PLUGIN_NAME, "Couldn't acquire D-Bus service: %s",
797  				             error->message);
798  				g_clear_error (&error);
799  			} else if (result != DBUS_REQUEST_NAME_REPLY_PRIMARY_OWNER) {
800  				PLUGIN_WARN (IFCFG_PLUGIN_NAME, "Couldn't acquire ifcfgrh1 D-Bus service (already taken)");
801  			} else
802  				success = TRUE;
803  		}
804  	
805  		if (!success) {
806  			if (priv->bus) {
807  				dbus_g_connection_unref (priv->bus);
808  				priv->bus = NULL;
809  			}
810  		}
811  	}
812  	
813  	static void
814  	dispose (GObject *object)
815  	{
816  		SCPluginIfcfg *plugin = SC_PLUGIN_IFCFG (object);
817  		SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (plugin);
818  		NMInotifyHelper *ih;
819  	
820  		if (priv->bus) {
821  			dbus_g_connection_unref (priv->bus);
822  			priv->bus = NULL;
823  		}
824  	
825  		if (priv->ih_event_id) {
826  			ih = nm_inotify_helper_get ();
827  	
828  			g_signal_handler_disconnect (ih, priv->ih_event_id);
829  			priv->ih_event_id = 0;
830  	
831  			if (priv->sc_network_wd >= 0)
832  				nm_inotify_helper_remove_watch (ih, priv->sc_network_wd);
833  		}
834  	
835  		if (priv->hostname_monitor) {
836  			if (priv->hostname_monitor_id)
837  				g_signal_handler_disconnect (priv->hostname_monitor, priv->hostname_monitor_id);
838  	
839  			g_file_monitor_cancel (priv->hostname_monitor);
840  			g_object_unref (priv->hostname_monitor);
841  		}
842  	
843  		g_free (priv->hostname);
844  	
845  		if (priv->connections) {
846  			g_hash_table_destroy (priv->connections);
847  			priv->connections = NULL;
848  		}
849  	
850  		if (priv->ifcfg_monitor) {
851  			if (priv->ifcfg_monitor_id)
852  				g_signal_handler_disconnect (priv->ifcfg_monitor, priv->ifcfg_monitor_id);
853  	
854  			g_file_monitor_cancel (priv->ifcfg_monitor);
855  			g_object_unref (priv->ifcfg_monitor);
856  		}
857  	
858  		G_OBJECT_CLASS (sc_plugin_ifcfg_parent_class)->dispose (object);
859  	}
860  	
861  	static void
862  	get_property (GObject *object, guint prop_id,
863  				  GValue *value, GParamSpec *pspec)
864  	{
865  		SCPluginIfcfgPrivate *priv = SC_PLUGIN_IFCFG_GET_PRIVATE (object);
866  	
867  		switch (prop_id) {
868  		case NM_SYSTEM_CONFIG_INTERFACE_PROP_NAME:
869  			g_value_set_string (value, IFCFG_PLUGIN_NAME);
870  			break;
871  		case NM_SYSTEM_CONFIG_INTERFACE_PROP_INFO:
872  			g_value_set_string (value, IFCFG_PLUGIN_INFO);
873  			break;
874  		case NM_SYSTEM_CONFIG_INTERFACE_PROP_CAPABILITIES:
875  			g_value_set_uint (value, NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_CONNECTIONS | NM_SYSTEM_CONFIG_INTERFACE_CAP_MODIFY_HOSTNAME);
876  			break;
877  		case NM_SYSTEM_CONFIG_INTERFACE_PROP_HOSTNAME:
878  			g_value_set_string (value, priv->hostname);
879  			break;
880  		default:
881  			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
882  			break;
883  		}
884  	}
885  	
886  	static void
887  	set_property (GObject *object, guint prop_id,
888  				  const GValue *value, GParamSpec *pspec)
889  	{
890  		const char *hostname;
891  	
892  		switch (prop_id) {
893  		case NM_SYSTEM_CONFIG_INTERFACE_PROP_HOSTNAME:
894  			hostname = g_value_get_string (value);
895  			if (hostname && strlen (hostname) < 1)
896  				hostname = NULL;
897  			plugin_set_hostname (SC_PLUGIN_IFCFG (object), hostname);
898  			break;
899  		default:
900  			G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
901  			break;
902  		}
903  	}
904  	
905  	static void
906  	sc_plugin_ifcfg_class_init (SCPluginIfcfgClass *req_class)
907  	{
908  		GObjectClass *object_class = G_OBJECT_CLASS (req_class);
909  	
910  		g_type_class_add_private (req_class, sizeof (SCPluginIfcfgPrivate));
911  	
912  		object_class->dispose = dispose;
913  		object_class->get_property = get_property;
914  		object_class->set_property = set_property;
915  	
916  		g_object_class_override_property (object_class,
917  		                                  NM_SYSTEM_CONFIG_INTERFACE_PROP_NAME,
918  		                                  NM_SYSTEM_CONFIG_INTERFACE_NAME);
919  	
920  		g_object_class_override_property (object_class,
921  		                                  NM_SYSTEM_CONFIG_INTERFACE_PROP_INFO,
922  		                                  NM_SYSTEM_CONFIG_INTERFACE_INFO);
923  	
924  		g_object_class_override_property (object_class,
925  		                                  NM_SYSTEM_CONFIG_INTERFACE_PROP_CAPABILITIES,
926  		                                  NM_SYSTEM_CONFIG_INTERFACE_CAPABILITIES);
927  	
928  		g_object_class_override_property (object_class,
929  		                                  NM_SYSTEM_CONFIG_INTERFACE_PROP_HOSTNAME,
930  		                                  NM_SYSTEM_CONFIG_INTERFACE_HOSTNAME);
931  	
932  		dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (req_class),
933  										 &dbus_glib_nm_ifcfg_rh_object_info);
934  	}
935  	
936  	static void
937  	system_config_interface_init (NMSystemConfigInterface *system_config_interface_class)
938  	{
939  		/* interface implementation */
940  		system_config_interface_class->get_connections = get_connections;
941  		system_config_interface_class->add_connection = add_connection;
942  		system_config_interface_class->reload_connections = reload_connections;
943  		system_config_interface_class->get_unmanaged_specs = get_unmanaged_specs;
944  		system_config_interface_class->init = init;
945  	}
946  	
947  	G_MODULE_EXPORT GObject *
948  	nm_system_config_factory (void)
949  	{
950  		static SCPluginIfcfg *singleton = NULL;
951  		SCPluginIfcfgPrivate *priv;
952  	
953  		if (!singleton) {
954  			singleton = SC_PLUGIN_IFCFG (g_object_new (SC_TYPE_PLUGIN_IFCFG, NULL));
955  			priv = SC_PLUGIN_IFCFG_GET_PRIVATE (singleton);
956  			if (priv->bus)
957  				dbus_g_connection_register_g_object (priv->bus,
958  				                                     DBUS_OBJECT_PATH,
959  				                                     G_OBJECT (singleton));
960  			PLUGIN_PRINT (IFCFG_PLUGIN_NAME, "Acquired D-Bus service %s", DBUS_SERVICE_NAME);
961  		} else
962  			g_object_ref (singleton);
963  	
964  		return G_OBJECT (singleton);
965  	}
966