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
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 */
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