summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2011-01-02 17:24:23 -0600
committerDan Williams <dcbw@redhat.com>2011-01-02 17:24:23 -0600
commit0587ef1179c7abb1c83ff5d12ce8e1b587ed0625 (patch)
tree7e66edf56183e99552e17044433e6eb234359d0b /src
parentb2f9747dba513fd34dac2b427ad1e0ac6ccb31e9 (diff)
parent15a9f29a14783fcd1b8e936a8338452737825726 (diff)
downloadNetworkManager-0587ef1179c7abb1c83ff5d12ce8e1b587ed0625.tar.gz
Merge remote branch 'origin/master' into wimax
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am40
-rw-r--r--src/NetworkManager.conf24
-rw-r--r--src/NetworkManagerUtils.c96
-rw-r--r--src/NetworkManagerUtils.h3
-rw-r--r--src/backends/Makefile.am10
-rw-r--r--src/backends/NetworkManagerDebian.c7
-rw-r--r--src/backends/NetworkManagerExherbo.c64
-rw-r--r--src/backends/NetworkManagerGentoo.c49
-rw-r--r--src/backends/NetworkManagerLinexa.c66
-rw-r--r--src/bluez-manager/nm-bluez-manager.c6
-rw-r--r--src/dhcp-manager/Makefile.am40
-rw-r--r--src/dhcp-manager/nm-dhcp-client.c292
-rw-r--r--src/dhcp-manager/nm-dhcp-client.h17
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient-utils.c217
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient-utils.h35
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient.c280
-rw-r--r--src/dhcp-manager/nm-dhcp-dhcpcd.c87
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.c50
-rw-r--r--src/dhcp-manager/tests/Makefile.am28
-rw-r--r--src/dhcp-manager/tests/test-dhcp-dhclient.c249
-rw-r--r--src/dns-manager/Makefile.am30
-rw-r--r--src/dns-manager/nm-dns-bind.c528
-rw-r--r--src/dns-manager/nm-dns-bind.h47
-rw-r--r--src/dns-manager/nm-dns-dnsmasq.c385
-rw-r--r--src/dns-manager/nm-dns-dnsmasq.h47
-rw-r--r--src/dns-manager/nm-dns-manager.c (renamed from src/named-manager/nm-named-manager.c)558
-rw-r--r--src/dns-manager/nm-dns-manager.h95
-rw-r--r--src/dns-manager/nm-dns-plugin.c319
-rw-r--r--src/dns-manager/nm-dns-plugin.h112
-rw-r--r--src/dns-manager/nm-dns-utils.c99
-rw-r--r--src/dns-manager/nm-dns-utils.h28
-rw-r--r--src/dnsmasq-manager/nm-dnsmasq-manager.c21
-rw-r--r--src/ip6-manager/Makefile.am3
-rw-r--r--src/ip6-manager/nm-ip6-manager.c398
-rw-r--r--src/logging/nm-logging.c6
-rw-r--r--src/logging/nm-logging.h1
-rw-r--r--src/main.c80
-rw-r--r--src/modem-manager/Makefile.am2
-rw-r--r--src/modem-manager/nm-modem.c6
-rw-r--r--src/named-manager/Makefile.am21
-rw-r--r--src/named-manager/nm-named-manager.h92
-rw-r--r--src/nm-device-ethernet.c698
-rw-r--r--src/nm-device-ethernet.h3
-rw-r--r--src/nm-device-interface.c42
-rw-r--r--src/nm-device-interface.h42
-rw-r--r--src/nm-device-modem.c12
-rw-r--r--src/nm-device-modem.h2
-rw-r--r--src/nm-device-private.h4
-rw-r--r--src/nm-device-wifi.c982
-rw-r--r--src/nm-device-wifi.h16
-rw-r--r--src/nm-device.c205
-rw-r--r--src/nm-device.h4
-rw-r--r--src/nm-ip4-config.c79
-rw-r--r--src/nm-ip4-config.h11
-rw-r--r--src/nm-ip6-config.c23
-rw-r--r--src/nm-manager-auth.c424
-rw-r--r--src/nm-manager-auth.h94
-rw-r--r--src/nm-manager.c2436
-rw-r--r--src/nm-manager.h15
-rw-r--r--src/nm-netlink-monitor.c4
-rw-r--r--src/nm-policy-hostname.c128
-rw-r--r--src/nm-policy-hostname.h10
-rw-r--r--src/nm-policy-hosts.c214
-rw-r--r--src/nm-policy-hosts.h12
-rw-r--r--src/nm-policy.c280
-rw-r--r--src/nm-system.c192
-rw-r--r--src/nm-system.h15
-rw-r--r--src/nm-wifi-ap.c326
-rw-r--r--src/nm-wifi-ap.h9
-rw-r--r--src/ppp-manager/Makefile.am2
-rw-r--r--src/ppp-manager/nm-ppp-manager.c33
-rw-r--r--src/supplicant-manager/Makefile.am3
-rw-r--r--src/supplicant-manager/nm-supplicant-config.c24
-rw-r--r--src/supplicant-manager/nm-supplicant-interface.c1583
-rw-r--r--src/supplicant-manager/nm-supplicant-interface.h77
-rw-r--r--src/supplicant-manager/nm-supplicant-manager.c427
-rw-r--r--src/supplicant-manager/nm-supplicant-manager.h44
-rw-r--r--src/supplicant-manager/nm-supplicant-settings-verify.c1
-rw-r--r--src/system-settings/Makefile.am2
-rw-r--r--src/system-settings/nm-default-wired-connection.c1
-rw-r--r--src/system-settings/nm-polkit-helpers.h17
-rw-r--r--src/system-settings/nm-sysconfig-connection.c12
-rw-r--r--src/system-settings/nm-sysconfig-settings.c31
-rw-r--r--src/system-settings/nm-system-config-interface.h8
-rw-r--r--src/tests/test-dhcp-options.c478
-rw-r--r--src/tests/test-policy-hosts.c326
-rw-r--r--src/vpn-manager/Makefile.am6
-rw-r--r--src/vpn-manager/nm-vpn-connection.c36
-rw-r--r--src/vpn-manager/nm-vpn-manager.c342
-rw-r--r--src/vpn-manager/nm-vpn-manager.h3
-rw-r--r--src/vpn-manager/nm-vpn-service.c308
-rw-r--r--src/vpn-manager/nm-vpn-service.h14
92 files changed, 9956 insertions, 4642 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 6bc2a404bb..47d9c6acda 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -1,6 +1,6 @@
SUBDIRS= \
logging \
- named-manager \
+ dns-manager \
vpn-manager \
dhcp-manager \
ip6-manager \
@@ -19,7 +19,7 @@ INCLUDES = -I${top_srcdir} \
-I${top_srcdir}/include \
-I${top_builddir}/marshallers \
-I${top_srcdir}/src/logging \
- -I${top_srcdir}/src/named-manager \
+ -I${top_srcdir}/src/dns-manager \
-I${top_srcdir}/src/vpn-manager \
-I${top_srcdir}/src/dhcp-manager \
-I${top_srcdir}/src/ip6-manager \
@@ -135,6 +135,8 @@ NetworkManager_SOURCES = \
nm-system.h \
nm-manager.c \
nm-manager.h \
+ nm-manager-auth.c \
+ nm-manager-auth.h \
nm-netlink-monitor.c \
nm-netlink-monitor.h \
nm-activation-request.c \
@@ -150,46 +152,46 @@ NetworkManager_SOURCES = \
nm-rfkill.h
nm-access-point-glue.h: $(top_srcdir)/introspection/nm-access-point.xml
- dbus-binding-tool --prefix=nm_access_point --mode=glib-server --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_access_point --mode=glib-server --output=$@ $<
nm-manager-glue.h: $(top_srcdir)/introspection/nm-manager.xml
- dbus-binding-tool --prefix=nm_manager --mode=glib-server --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_manager --mode=glib-server --output=$@ $<
nm-device-interface-glue.h: $(top_srcdir)/introspection/nm-device.xml
- dbus-binding-tool --prefix=nm_device_interface --mode=glib-server --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_device_interface --mode=glib-server --output=$@ $<
nm-device-ethernet-glue.h: $(top_srcdir)/introspection/nm-device-ethernet.xml
- dbus-binding-tool --prefix=nm_device_ethernet --mode=glib-server --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_device_ethernet --mode=glib-server --output=$@ $<
nm-device-wifi-glue.h: $(top_srcdir)/introspection/nm-device-wifi.xml
- dbus-binding-tool --prefix=nm_device_wifi --mode=glib-server --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_device_wifi --mode=glib-server --output=$@ $<
nm-device-bt-glue.h: $(top_srcdir)/introspection/nm-device-bt.xml
- dbus-binding-tool --prefix=nm_device_bt --mode=glib-server --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_device_bt --mode=glib-server --output=$@ $<
nm-device-olpc-mesh-glue.h: $(top_srcdir)/introspection/nm-device-olpc-mesh.xml
- dbus-binding-tool --prefix=nm_device_olpc_mesh --mode=glib-server --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_device_olpc_mesh --mode=glib-server --output=$@ $<
nm-ip4-config-glue.h: $(top_srcdir)/introspection/nm-ip4-config.xml
- dbus-binding-tool --prefix=nm_ip4_config --mode=glib-server --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_ip4_config --mode=glib-server --output=$@ $<
nm-ip6-config-glue.h: $(top_srcdir)/introspection/nm-ip6-config.xml
- dbus-binding-tool --prefix=nm_ip6_config --mode=glib-server --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_ip6_config --mode=glib-server --output=$@ $<
nm-active-connection-glue.h: $(top_srcdir)/introspection/nm-active-connection.xml
- dbus-binding-tool --prefix=nm_active_connection --mode=glib-server --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_active_connection --mode=glib-server --output=$@ $<
nm-dhcp4-config-glue.h: $(top_srcdir)/introspection/nm-dhcp4-config.xml
- dbus-binding-tool --prefix=nm_dhcp4_config --mode=glib-server --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_dhcp4_config --mode=glib-server --output=$@ $<
nm-dhcp6-config-glue.h: $(top_srcdir)/introspection/nm-dhcp6-config.xml
- dbus-binding-tool --prefix=nm_dhcp6_config --mode=glib-server --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_dhcp6_config --mode=glib-server --output=$@ $<
nm-device-cdma-glue.h: $(top_srcdir)/introspection/nm-device-cdma.xml
- dbus-binding-tool --prefix=nm_device_cdma --mode=glib-server --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_device_cdma --mode=glib-server --output=$@ $<
nm-device-gsm-glue.h: $(top_srcdir)/introspection/nm-device-gsm.xml
- dbus-binding-tool --prefix=nm_device_gsm --mode=glib-server --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_device_gsm --mode=glib-server --output=$@ $<
BUILT_SOURCES = \
nm-access-point-glue.h \
@@ -214,6 +216,7 @@ NetworkManager_CPPFLAGS = \
$(LIBNL_CFLAGS) \
$(GMODULE_CFLAGS) \
$(WIMAX_CFLAGS) \
+ $(POLKIT_CFLAGS) \
-DG_DISABLE_DEPRECATED \
-DBINDIR=\"$(bindir)\" \
-DSBINDIR=\"$(sbindir)\" \
@@ -228,7 +231,7 @@ NetworkManager_CPPFLAGS = \
NetworkManager_LDADD = \
$(top_builddir)/marshallers/libmarshallers.la \
./logging/libnm-logging.la \
- ./named-manager/libnamed-manager.la \
+ ./dns-manager/libdns-manager.la \
./vpn-manager/libvpn-manager.la \
./dhcp-manager/libdhcp-manager.la \
./ip6-manager/libip6-manager.la \
@@ -246,6 +249,7 @@ NetworkManager_LDADD = \
$(GUDEV_LIBS) \
$(LIBNL_LIBS) \
$(GMODULE_LIBS) \
+ $(POLKIT_LIBS) \
$(LIBM) \
$(LIBDL)
@@ -275,8 +279,10 @@ EXTRA_DIST = \
$(NetworkManager_DATA)
rundir=$(localstatedir)/run/NetworkManager
+statedir=$(localstatedir)/lib/NetworkManager
install-data-hook:
$(mkinstalldirs) -m 0700 $(DESTDIR)$(rundir)
+ $(mkinstalldirs) -m 0700 $(DESTDIR)$(statedir)
CLEANFILES = $(BUILT_SOURCES)
diff --git a/src/NetworkManager.conf b/src/NetworkManager.conf
index 8d08314002..1f1ed49b46 100644
--- a/src/NetworkManager.conf
+++ b/src/NetworkManager.conf
@@ -60,6 +60,18 @@
<deny send_destination="org.freedesktop.NetworkManager"
send_interface="org.freedesktop.NetworkManager"
send_member="SetLogging"/>
+
+ <deny send_destination="org.freedesktop.NetworkManager"
+ send_interface="org.freedesktop.NetworkManager"
+ send_member="Sleep"/>
+
+ <deny send_destination="org.freedesktop.NetworkManager"
+ send_interface="org.freedesktop.NetworkManager"
+ send_member="sleep"/>
+
+ <deny send_destination="org.freedesktop.NetworkManager"
+ send_interface="org.freedesktop.NetworkManager"
+ send_member="wake"/>
</policy>
<policy context="default">
<deny own="org.freedesktop.NetworkManager"/>
@@ -72,6 +84,18 @@
send_interface="org.freedesktop.NetworkManager"
send_member="SetLogging"/>
+ <deny send_destination="org.freedesktop.NetworkManager"
+ send_interface="org.freedesktop.NetworkManager"
+ send_member="Sleep"/>
+
+ <deny send_destination="org.freedesktop.NetworkManager"
+ send_interface="org.freedesktop.NetworkManager"
+ send_member="sleep"/>
+
+ <deny send_destination="org.freedesktop.NetworkManager"
+ send_interface="org.freedesktop.NetworkManager"
+ send_member="wake"/>
+
<!-- The org.freedesktop.NetworkManagerSettings.Connection.Secrets
interface is secured via PolicyKit.
-->
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index 22cf2fa0d8..10240960b0 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -25,6 +25,7 @@
#include <stdio.h>
#include <string.h>
#include <unistd.h>
+#include <ctype.h>
#include "NetworkManagerUtils.h"
#include "nm-utils.h"
@@ -487,6 +488,101 @@ nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr)
return FALSE;
}
+#define BUFSIZE 10
+
+static gboolean
+parse_subchannels (const char *subchannels, guint32 *a, guint32 *b, guint32 *c)
+{
+ long unsigned int tmp;
+ char buf[BUFSIZE + 1];
+ const char *p = subchannels;
+ int i = 0;
+ char *pa = NULL, *pb = NULL, *pc = NULL;
+
+ g_return_val_if_fail (subchannels != NULL, FALSE);
+ g_return_val_if_fail (a != NULL, FALSE);
+ g_return_val_if_fail (*a == 0, FALSE);
+ g_return_val_if_fail (b != NULL, FALSE);
+ g_return_val_if_fail (*b == 0, FALSE);
+ g_return_val_if_fail (c != NULL, FALSE);
+ g_return_val_if_fail (*c == 0, FALSE);
+
+ /* sanity check */
+ if (!isxdigit (subchannels[0]))
+ return FALSE;
+
+ /* Get the first channel */
+ while (*p && (*p != ',')) {
+ if (!isxdigit (*p) && (*p != '.'))
+ return FALSE; /* Invalid chars */
+ if (i >= BUFSIZE)
+ return FALSE; /* Too long to be a subchannel */
+ buf[i++] = *p++;
+ }
+ buf[i] = '\0';
+
+ /* and grab each of its elements, there should be 3 */
+ pa = &buf[0];
+ pb = strchr (buf, '.');
+ if (pb)
+ pc = strchr (pb + 1, '.');
+ if (!pa || !pb || !pc)
+ return FALSE;
+
+ /* Split the string */
+ *pb++ = '\0';
+ *pc++ = '\0';
+
+ errno = 0;
+ tmp = strtoul (pa, NULL, 16);
+ if (errno)
+ return FALSE;
+ *a = (guint32) tmp;
+
+ errno = 0;
+ tmp = strtoul (pb, NULL, 16);
+ if (errno)
+ return FALSE;
+ *b = (guint32) tmp;
+
+ errno = 0;
+ tmp = strtoul (pc, NULL, 16);
+ if (errno)
+ return FALSE;
+ *c = (guint32) tmp;
+
+ return TRUE;
+}
+
+#define SUBCHAN_TAG "s390-subchannels:"
+
+gboolean
+nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels)
+{
+ const GSList *iter;
+ guint32 a = 0, b = 0, c = 0;
+ guint32 spec_a = 0, spec_b = 0, spec_c = 0;
+
+ g_return_val_if_fail (subchannels != NULL, FALSE);
+
+ if (!parse_subchannels (subchannels, &a, &b, &c))
+ return FALSE;
+
+ for (iter = specs; iter; iter = g_slist_next (iter)) {
+ const char *spec = iter->data;
+
+ if (!strncmp (spec, SUBCHAN_TAG, strlen (SUBCHAN_TAG))) {
+ spec += strlen (SUBCHAN_TAG);
+ if (parse_subchannels (spec, &spec_a, &spec_b, &spec_c)) {
+ if (a == spec_a && b == spec_b && c == spec_c)
+ return TRUE;
+ }
+ }
+ }
+
+ return FALSE;
+}
+
/*********************************/
static void
diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
index e3d1793b4d..72c0e532b8 100644
--- a/src/NetworkManagerUtils.h
+++ b/src/NetworkManagerUtils.h
@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2004 - 2008 Red Hat, Inc.
+ * Copyright (C) 2004 - 2010 Red Hat, Inc.
* Copyright (C) 2005 - 2008 Novell, Inc.
*/
@@ -48,6 +48,7 @@ void nm_utils_call_dispatcher (const char *action,
const char *vpn_iface);
gboolean nm_match_spec_hwaddr (const GSList *specs, const char *hwaddr);
+gboolean nm_match_spec_s390_subchannels (const GSList *specs, const char *subchannels);
GHashTable *value_hash_create (void);
diff --git a/src/backends/Makefile.am b/src/backends/Makefile.am
index c3485e5f9b..fbec9aaf87 100644
--- a/src/backends/Makefile.am
+++ b/src/backends/Makefile.am
@@ -3,8 +3,6 @@ INCLUDES = \
-I${top_srcdir}/src/logging \
-I${top_srcdir}/include \
-I${top_srcdir}/src \
- -I${top_srcdir}/src/vpn-manager \
- -I${top_srcdir}/src/named-manager \
-I${top_srcdir}/libnm-util
noinst_LTLIBRARIES = libnmbackend.la
@@ -55,6 +53,14 @@ if TARGET_PARDUS
libnmbackend_la_SOURCES += NetworkManagerPardus.c
endif
+if TARGET_LINEXA
+libnmbackend_la_SOURCES += NetworkManagerLinexa.c
+endif
+
+if TARGET_EXHERBO
+libnmbackend_la_SOURCES += NetworkManagerExherbo.c
+endif
+
libnmbackend_la_LIBADD += \
$(top_builddir)/src/logging/libnm-logging.la \
$(DBUS_LIBS) \
diff --git a/src/backends/NetworkManagerDebian.c b/src/backends/NetworkManagerDebian.c
index ef9475af6d..37eb6457b0 100644
--- a/src/backends/NetworkManagerDebian.c
+++ b/src/backends/NetworkManagerDebian.c
@@ -1,3 +1,4 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
/* NetworkManager -- Network link manager
*
* Matthew Garrett <mjg59@srcf.ucam.org>
@@ -44,7 +45,11 @@
*/
void nm_system_enable_loopback (void)
{
- nm_spawn_process ("/sbin/ifup lo");
+ /* ifupdown isn't always installed (bgo #625427) */
+ if (g_file_test ("/sbin/ifup", G_FILE_TEST_EXISTS))
+ nm_spawn_process ("/sbin/ifup lo");
+ else
+ nm_generic_enable_loopback ();
}
/*
diff --git a/src/backends/NetworkManagerExherbo.c b/src/backends/NetworkManagerExherbo.c
new file mode 100644
index 0000000000..d7e24cf603
--- /dev/null
+++ b/src/backends/NetworkManagerExherbo.c
@@ -0,0 +1,64 @@
+/* NetworkManager -- Network link manager
+ *
+ * Dan Williams <dcbw@redhat.com>
+ * Dan Willemsen <dan@willemsen.us>
+ * Robert Paskowitz
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2004 Red Hat, Inc.
+ * (C) Copyright 2004 Dan Willemsen
+ * (C) Copyright 2004 Robert Paskowitz
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "NetworkManagerGeneric.h"
+#include "nm-system.h"
+#include "NetworkManagerUtils.h"
+#include "nm-logging.h"
+
+/*
+ * nm_system_enable_loopback
+ *
+ * Bring up the loopback interface
+ *
+ */
+void nm_system_enable_loopback (void)
+{
+ nm_generic_enable_loopback ();
+}
+
+/*
+ * nm_system_update_dns
+ *
+ * Make glibc/nscd aware of any changes to the resolv.conf file by
+ * restarting nscd. Only restart if already running.
+ *
+ */
+void nm_system_update_dns (void)
+{
+ if (g_file_test ("/usr/sbin/nscd", G_FILE_TEST_IS_EXECUTABLE)) {
+ nm_log_info (LOGD_DNS, "Clearing nscd hosts cache.");
+ nm_spawn_process ("/usr/sbin/nscd -i hosts");
+ }
+}
+
diff --git a/src/backends/NetworkManagerGentoo.c b/src/backends/NetworkManagerGentoo.c
index 8d9e68c59c..2854901a56 100644
--- a/src/backends/NetworkManagerGentoo.c
+++ b/src/backends/NetworkManagerGentoo.c
@@ -30,12 +30,22 @@
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
+#include <gio/gio.h>
#include "NetworkManagerGeneric.h"
#include "nm-system.h"
#include "NetworkManagerUtils.h"
#include "nm-logging.h"
+#define BUFFER_SIZE 512
+
+static void openrc_start_lo_if_necessary()
+{
+ /* No need to run net.lo if it is already running */
+ if (nm_spawn_process ("/etc/init.d/net.lo status") != 0)
+ nm_spawn_process ("/etc/init.d/net.lo start");
+}
+
/*
* nm_system_enable_loopback
*
@@ -44,9 +54,42 @@
*/
void nm_system_enable_loopback (void)
{
- /* No need to run net.lo if it is already running */
- if (nm_spawn_process ("/etc/init.d/net.lo status") != 0)
- nm_spawn_process("/etc/init.d/net.lo start");
+ GFile *file;
+ GFileInputStream *in;
+ gchar buffer[BUFFER_SIZE];
+ gchar *comm, *readed, *tmp;
+ gssize r;
+
+ file = g_file_new_for_path ("/proc/1/comm");
+ in = g_file_read (file, NULL, NULL);
+
+ /* If anything goes wrong trying to open /proc/1/comm,
+ we will assume OpenRC. */
+ if (!in) {
+ openrc_start_lo_if_necessary ();
+ return;
+ }
+
+ comm = g_strdup("");
+ while ((r = g_input_stream_read (G_INPUT_STREAM(in), buffer, BUFFER_SIZE, NULL, NULL)) > 0) {
+ readed = g_strndup (buffer, r);
+ tmp = g_strconcat (comm, readed, NULL);
+ g_free (comm);
+ g_free (readed);
+ comm = tmp;
+ }
+
+ if (g_strstr_len (comm, -1, "systemd")) {
+ /* We use the generic loopback enabler if using systemd. */
+ nm_log_info (LOGD_CORE, "NetworkManager is running with systemd...");
+ nm_generic_enable_loopback ();
+ } else {
+ /* OpenRC otherwise. */
+ nm_log_info (LOGD_CORE, "NetworkManager is running with OpenRC...");
+ openrc_start_lo_if_necessary();
+ }
+
+ g_free (comm);
}
/*
diff --git a/src/backends/NetworkManagerLinexa.c b/src/backends/NetworkManagerLinexa.c
new file mode 100644
index 0000000000..015aa17c68
--- /dev/null
+++ b/src/backends/NetworkManagerLinexa.c
@@ -0,0 +1,66 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * Matthew Garrett <mjg59@srcf.ucam.org>
+ *
+ * Heavily based on NetworkManagerRedhat.c by Dan Williams <dcbw@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * (C) Copyright 2004 Tom Parker
+ * (C) Copyright 2004 Matthew Garrett
+ * (C) Copyright 2004 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+
+#include "NetworkManagerGeneric.h"
+#include "nm-system.h"
+#include "NetworkManagerUtils.h"
+#include "nm-logging.h"
+
+/*
+ * nm_system_enable_loopback
+ *
+ * Bring up the loopback interface
+ *
+ */
+void nm_system_enable_loopback (void)
+{
+ nm_generic_enable_loopback ();
+}
+
+/*
+ * nm_system_update_dns
+ *
+ * Invalidate the nscd host cache, if it exists, since
+ * we changed resolv.conf.
+ *
+ */
+void nm_system_update_dns (void)
+{
+ if (g_file_test ("/usr/sbin/nscd", G_FILE_TEST_IS_EXECUTABLE)) {
+ nm_log_info (LOGD_DNS, "Clearing nscd hosts cache.");
+ nm_spawn_process ("/usr/sbin/nscd -i hosts");
+ }
+}
+
+
diff --git a/src/bluez-manager/nm-bluez-manager.c b/src/bluez-manager/nm-bluez-manager.c
index fe027c4ff5..59849d3bd5 100644
--- a/src/bluez-manager/nm-bluez-manager.c
+++ b/src/bluez-manager/nm-bluez-manager.c
@@ -251,11 +251,13 @@ name_owner_changed_cb (NMDBusManager *dbus_mgr,
gboolean old_owner_good = (old_owner && strlen (old_owner));
gboolean new_owner_good = (new_owner && strlen (new_owner));
- /* Can't handle the signal if its not from the supplicant service */
+ /* Can't handle the signal if its not from the Bluez */
if (strcmp (BLUEZ_SERVICE, name))
return;
- if (old_owner_good && !new_owner_good)
+ if (!old_owner_good && new_owner_good)
+ query_default_adapter (self);
+ else if (old_owner_good && !new_owner_good)
remove_all_devices (self, TRUE);
}
diff --git a/src/dhcp-manager/Makefile.am b/src/dhcp-manager/Makefile.am
index f75e6b3d07..14ddde0bd2 100644
--- a/src/dhcp-manager/Makefile.am
+++ b/src/dhcp-manager/Makefile.am
@@ -1,21 +1,47 @@
+SUBDIRS=. tests
+
INCLUDES = \
-I${top_srcdir} \
-I${top_srcdir}/include \
-I${top_builddir}/marshallers \
-I${top_srcdir}/src/logging \
-I${top_srcdir}/libnm-util \
- -I${top_srcdir}/src \
- -I${top_srcdir}/src/named-manager
+ -I${top_srcdir}/src
+
+noinst_LTLIBRARIES = libdhcp-manager.la libdhcp-dhclient.la
+
+################## dhclient ##################
+
+libdhcp_dhclient_la_SOURCES = \
+ nm-dhcp-dhclient-utils.h \
+ nm-dhcp-dhclient-utils.c \
+ nm-dhcp-dhclient.h \
+ nm-dhcp-dhclient.c
+
+libdhcp_dhclient_la_CPPFLAGS = \
+ $(DBUS_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ -DG_DISABLE_DEPRECATED \
+ -DSYSCONFDIR=\"$(sysconfdir)\" \
+ -DLIBEXECDIR=\"$(libexecdir)\" \
+ -DLOCALSTATEDIR=\"$(localstatedir)\" \
+ -DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \
+ -DDHCLIENT_V$(DHCLIENT_VERSION)
+
+libdhcp_dhclient_la_LIBADD = \
+ $(top_builddir)/marshallers/libmarshallers.la \
+ $(top_builddir)/src/logging/libnm-logging.la \
+ $(top_builddir)/libnm-util/libnm-util.la \
+ $(DBUS_LIBS) \
+ $(GLIB_LIBS)
-noinst_LTLIBRARIES = libdhcp-manager.la
+################## main lib ##################
libdhcp_manager_la_SOURCES = \
nm-dhcp-client.c \
nm-dhcp-client.h \
nm-dhcp-manager.c \
nm-dhcp-manager.h \
- nm-dhcp-dhclient.h \
- nm-dhcp-dhclient.c \
nm-dhcp-dhcpcd.h \
nm-dhcp-dhcpcd.c
@@ -23,9 +49,6 @@ libdhcp_manager_la_CPPFLAGS = \
$(DBUS_CFLAGS) \
$(GLIB_CFLAGS) \
-DG_DISABLE_DEPRECATED \
- -DBINDIR=\"$(bindir)\" \
- -DDATADIR=\"$(datadir)\" \
- -DSYSCONFDIR=\"$(sysconfdir)\" \
-DLIBEXECDIR=\"$(libexecdir)\" \
-DLOCALSTATEDIR=\"$(localstatedir)\" \
-DDHCLIENT_PATH=\"$(DHCLIENT_PATH)\" \
@@ -34,6 +57,7 @@ libdhcp_manager_la_CPPFLAGS = \
libdhcp_manager_la_LIBADD = \
$(top_builddir)/marshallers/libmarshallers.la \
$(top_builddir)/src/logging/libnm-logging.la \
+ $(builddir)/libdhcp-dhclient.la \
$(DBUS_LIBS) \
$(GLIB_LIBS)
diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c
index 5cebaa84ee..3a8b194081 100644
--- a/src/dhcp-manager/nm-dhcp-client.c
+++ b/src/dhcp-manager/nm-dhcp-client.c
@@ -17,6 +17,8 @@
*
*/
+#include <config.h>
+#include <ctype.h>
#include <glib.h>
#include <string.h>
#include <sys/types.h>
@@ -154,11 +156,15 @@ stop_process (GPid pid, const char *iface)
if (ret == -1) {
/* Child already exited */
- if (errno == ECHILD)
+ if (errno == ECHILD) {
+ /* Was it really our child and it exited? */
+ if (kill (pid, 0) < 0 && errno == ESRCH)
+ break;
+ } else {
+ /* Took too long; shoot it in the head */
+ i = 0;
break;
- /* Took too long; shoot it in the head */
- i = 0;
- break;
+ }
}
g_usleep (G_USEC_PER_SEC / 5);
}
@@ -293,7 +299,8 @@ start_monitor (NMDHCPClient *self)
gboolean
nm_dhcp_client_start_ip4 (NMDHCPClient *self,
NMSettingIP4Config *s_ip4,
- guint8 *dhcp_anycast_addr)
+ guint8 *dhcp_anycast_addr,
+ const char *hostname)
{
NMDHCPClientPrivate *priv;
@@ -308,7 +315,7 @@ nm_dhcp_client_start_ip4 (NMDHCPClient *self,
nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv4 transaction (timeout in %d seconds)",
priv->iface, priv->timeout);
- priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, s_ip4, dhcp_anycast_addr);
+ priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, s_ip4, dhcp_anycast_addr, hostname);
if (priv->pid)
start_monitor (self);
@@ -319,6 +326,7 @@ gboolean
nm_dhcp_client_start_ip6 (NMDHCPClient *self,
NMSettingIP6Config *s_ip6,
guint8 *dhcp_anycast_addr,
+ const char *hostname,
gboolean info_only)
{
NMDHCPClientPrivate *priv;
@@ -336,7 +344,7 @@ nm_dhcp_client_start_ip6 (NMDHCPClient *self,
nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv6 transaction (timeout in %d seconds)",
priv->iface, priv->timeout);
- priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self, s_ip6, dhcp_anycast_addr, info_only);
+ priv->pid = NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self, s_ip6, dhcp_anycast_addr, hostname, info_only);
if (priv->pid > 0)
start_monitor (self);
@@ -644,6 +652,243 @@ nm_dhcp_client_foreach_option (NMDHCPClient *self,
/********************************************/
+static gboolean
+ip4_process_dhcpcd_rfc3442_routes (const char *str,
+ NMIP4Config *ip4_config,
+ guint32 *gwaddr)
+{
+ char **routes, **r;
+ gboolean have_routes = FALSE;
+
+ routes = g_strsplit (str, " ", 0);
+ if (g_strv_length (routes) == 0)
+ goto out;
+
+ if ((g_strv_length (routes) % 2) != 0) {
+ nm_log_warn (LOGD_DHCP4, " classless static routes provided, but invalid");
+ goto out;
+ }
+
+ for (r = routes; *r; r += 2) {
+ char *slash;
+ NMIP4Route *route;
+ int rt_cidr = 32;
+ struct in_addr rt_addr;
+ struct in_addr rt_route;
+
+ slash = strchr(*r, '/');
+ if (slash) {
+ *slash = '\0';
+ errno = 0;
+ rt_cidr = strtol (slash + 1, NULL, 10);
+ if ((errno == EINVAL) || (errno == ERANGE)) {
+ nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route cidr: '%s'", slash + 1);
+ continue;
+ }
+ }
+ if (inet_pton (AF_INET, *r, &rt_addr) <= 0) {
+ nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route address: '%s'", *r);
+ continue;
+ }
+ if (inet_pton (AF_INET, *(r + 1), &rt_route) <= 0) {
+ nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route gateway: '%s'", *(r + 1));
+ continue;
+ }
+
+ have_routes = TRUE;
+ if (rt_cidr == 0 && rt_addr.s_addr == 0) {
+ /* FIXME: how to handle multiple routers? */
+ *gwaddr = rt_route.s_addr;
+ } else {
+ route = nm_ip4_route_new ();
+ nm_ip4_route_set_dest (route, (guint32) rt_addr.s_addr);
+ nm_ip4_route_set_prefix (route, rt_cidr);
+ nm_ip4_route_set_next_hop (route, (guint32) rt_route.s_addr);
+
+ nm_ip4_config_take_route (ip4_config, route);
+ nm_log_info (LOGD_DHCP4, " classless static route %s/%d gw %s", *r, rt_cidr, *(r + 1));
+ }
+ }
+
+out:
+ g_strfreev (routes);
+ return have_routes;
+}
+
+static const char **
+process_dhclient_rfc3442_route (const char **octets, NMIP4Route **out_route)
+{
+ const char **o = octets;
+ int addr_len = 0, i = 0;
+ long int tmp;
+ NMIP4Route *route;
+ char *next_hop;
+ struct in_addr tmp_addr;
+
+ if (!*o)
+ return o; /* no prefix */
+
+ tmp = strtol (*o, NULL, 10);
+ if (tmp < 0 || tmp > 32) /* 32 == max IP4 prefix length */
+ return o;
+
+ route = nm_ip4_route_new ();
+ nm_ip4_route_set_prefix (route, (guint32) tmp);
+ o++;
+
+ if (tmp > 0)
+ addr_len = ((tmp - 1) / 8) + 1;
+
+ /* ensure there's at least the address + next hop left */
+ if (g_strv_length ((char **) o) < addr_len + 4)
+ goto error;
+
+ if (tmp) {
+ const char *addr[4] = { "0", "0", "0", "0" };
+ char *str_addr;
+
+ for (i = 0; i < addr_len; i++)
+ addr[i] = *o++;
+
+ str_addr = g_strjoin (".", addr[0], addr[1], addr[2], addr[3], NULL);
+ if (inet_pton (AF_INET, str_addr, &tmp_addr) <= 0) {
+ g_free (str_addr);
+ goto error;
+ }
+ tmp_addr.s_addr &= nm_utils_ip4_prefix_to_netmask ((guint32) tmp);
+ nm_ip4_route_set_dest (route, tmp_addr.s_addr);
+ }
+
+ /* Handle next hop */
+ next_hop = g_strjoin (".", o[0], o[1], o[2], o[3], NULL);
+ if (inet_pton (AF_INET, next_hop, &tmp_addr) <= 0) {
+ g_free (next_hop);
+ goto error;
+ }
+ nm_ip4_route_set_next_hop (route, tmp_addr.s_addr);
+ g_free (next_hop);
+
+ *out_route = route;
+ return o + 4; /* advance to past the next hop */
+
+error:
+ nm_ip4_route_unref (route);
+ return o;
+}
+
+static gboolean
+ip4_process_dhclient_rfc3442_routes (const char *str,
+ NMIP4Config *ip4_config,
+ guint32 *gwaddr)
+{
+ char **octets, **o;
+ gboolean have_routes = FALSE;
+ NMIP4Route *route = NULL;
+
+ o = octets = g_strsplit_set (str, " .", 0);
+ if (g_strv_length (octets) < 5) {
+ nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes '%s'", str);
+ goto out;
+ }
+
+ while (*o) {
+ route = NULL;
+ o = (char **) process_dhclient_rfc3442_route ((const char **) o, &route);
+ if (!route) {
+ nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes");
+ break;
+ }
+
+ have_routes = TRUE;
+ if (nm_ip4_route_get_prefix (route) == 0) {
+ /* gateway passed as classless static route */
+ *gwaddr = nm_ip4_route_get_next_hop (route);
+ nm_ip4_route_unref (route);
+ } else {
+ char addr[INET_ADDRSTRLEN + 1];
+ char nh[INET_ADDRSTRLEN + 1];
+ struct in_addr tmp;
+
+ /* normal route */
+ nm_ip4_config_take_route (ip4_config, route);
+
+ tmp.s_addr = nm_ip4_route_get_dest (route);
+ inet_ntop (AF_INET, &tmp, addr, sizeof (addr));
+ tmp.s_addr = nm_ip4_route_get_next_hop (route);
+ inet_ntop (AF_INET, &tmp, nh, sizeof (nh));
+ nm_log_info (LOGD_DHCP4, " classless static route %s/%d gw %s",
+ addr, nm_ip4_route_get_prefix (route), nh);
+ }
+ }
+
+out:
+ g_strfreev (octets);
+ return have_routes;
+}
+
+static gboolean
+ip4_process_classless_routes (GHashTable *options,
+ NMIP4Config *ip4_config,
+ guint32 *gwaddr)
+{
+ const char *str, *p;
+
+ g_return_val_if_fail (options != NULL, FALSE);
+ g_return_val_if_fail (ip4_config != NULL, FALSE);
+
+ *gwaddr = 0;
+
+ /* dhcpd/dhclient in Fedora has support for rfc3442 implemented using a
+ * slightly different format:
+ *
+ * option classless-static-routes = array of (destination-descriptor ip-address);
+ *
+ * which results in:
+ *
+ * 0 192.168.0.113 25.129.210.177.132 192.168.0.113 7.2 10.34.255.6
+ *
+ * dhcpcd supports classless static routes natively and uses this same
+ * option identifier with the following format:
+ *
+ * 192.168.10.0/24 192.168.1.1 10.0.0.0/8 10.17.66.41
+ */
+ str = g_hash_table_lookup (options, "new_classless_static_routes");
+
+ /* dhclient doesn't have actual support for rfc3442 classless static routes
+ * upstream. Thus, people resort to defining the option in dhclient.conf
+ * and using arbitrary formats like so:
+ *
+ * option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;
+ *
+ * See https://lists.isc.org/pipermail/dhcp-users/2008-December/007629.html
+ */
+ if (!str)
+ str = g_hash_table_lookup (options, "new_rfc3442_classless_static_routes");
+
+ /* Microsoft version; same as rfc3442 but with a different option # (249) */
+ if (!str)
+ str = g_hash_table_lookup (options, "new_ms_classless_static_routes");
+
+ if (!str || !strlen (str))
+ return FALSE;
+
+ p = str;
+ while (*p) {
+ if (!isdigit (*p) && (*p != ' ') && (*p != '.') && (*p != '/')) {
+ nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes '%s'", str);
+ return FALSE;
+ }
+ p++;
+ };
+
+ if (strchr (str, '/')) {
+ /* dhcpcd format */
+ return ip4_process_dhcpcd_rfc3442_routes (str, ip4_config, gwaddr);
+ }
+
+ return ip4_process_dhclient_rfc3442_routes (str, ip4_config, gwaddr);
+}
+
static void
process_classful_routes (GHashTable *options, NMIP4Config *ip4_config)
{
@@ -744,7 +989,6 @@ ip4_options_to_config (NMDHCPClient *self)
NMIP4Address *addr = NULL;
char *str = NULL;
guint32 gwaddr = 0, prefix = 0;
- gboolean have_classless = FALSE;
g_return_val_if_fail (self != NULL, NULL);
g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
@@ -785,17 +1029,8 @@ ip4_options_to_config (NMDHCPClient *self)
/* Routes: if the server returns classless static routes, we MUST ignore
* the 'static_routes' option.
*/
- if (NM_DHCP_CLIENT_GET_CLASS (self)->ip4_process_classless_routes) {
- have_classless = NM_DHCP_CLIENT_GET_CLASS (self)->ip4_process_classless_routes (self,
- priv->options,
- ip4_config,
- &gwaddr);
- }
-
- if (!have_classless) {
- gwaddr = 0; /* Ensure client code doesn't lie */
+ if (!ip4_process_classless_routes (priv->options, ip4_config, &gwaddr))
process_classful_routes (priv->options, ip4_config);
- }
if (gwaddr) {
char buf[INET_ADDRSTRLEN + 1];
@@ -891,6 +1126,27 @@ ip4_options_to_config (NMDHCPClient *self)
nm_ip4_config_set_mtu (ip4_config, int_mtu);
}
+ str = g_hash_table_lookup (priv->options, "new_nis_domain");
+ if (str) {
+ nm_log_info (LOGD_DHCP4, " NIS domain '%s'", str);
+ nm_ip4_config_set_nis_domain (ip4_config, str);
+ }
+
+ str = g_hash_table_lookup (priv->options, "new_nis_servers");
+ if (str) {
+ char **searches = g_strsplit (str, " ", 0);
+ char **s;
+
+ for (s = searches; *s; s++) {
+ if (inet_pton (AF_INET, *s, &tmp_addr) > 0) {
+ nm_ip4_config_add_nis_server (ip4_config, tmp_addr.s_addr);
+ nm_log_info (LOGD_DHCP4, " nis '%s'", *s);
+ } else
+ nm_log_warn (LOGD_DHCP4, "ignoring invalid NIS server '%s'", *s);
+ }
+ g_strfreev (searches);
+ }
+
return ip4_config;
error:
diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h
index 92b2b8fe51..f357170b9c 100644
--- a/src/dhcp-manager/nm-dhcp-client.h
+++ b/src/dhcp-manager/nm-dhcp-client.h
@@ -76,22 +76,15 @@ typedef struct {
/* Methods */
- /* Given the options table, extract any classless routes, add them to
- * the IP4 config and return TRUE if any existed. If a gateway was sent
- * as a classless route return that in out_gwaddr.
- */
- gboolean (*ip4_process_classless_routes) (NMDHCPClient *self,
- GHashTable *options,
- NMIP4Config *ip4_config,
- guint32 *out_gwaddr);
-
GPid (*ip4_start) (NMDHCPClient *self,
NMSettingIP4Config *s_ip4,
- guint8 *anycast_addr);
+ guint8 *anycast_addr,
+ const char *hostname);
GPid (*ip6_start) (NMDHCPClient *self,
NMSettingIP6Config *s_ip6,
guint8 *anycast_addr,
+ const char *hostname,
gboolean info_only);
void (*stop) (NMDHCPClient *self);
@@ -114,11 +107,13 @@ const char *nm_dhcp_client_get_uuid (NMDHCPClient *self);
gboolean nm_dhcp_client_start_ip4 (NMDHCPClient *self,
NMSettingIP4Config *s_ip4,
- guint8 *dhcp_anycast_addr);
+ guint8 *dhcp_anycast_addr,
+ const char *hostname);
gboolean nm_dhcp_client_start_ip6 (NMDHCPClient *self,
NMSettingIP6Config *s_ip6,
guint8 *dhcp_anycast_addr,
+ const char *hostname,
gboolean info_only);
void nm_dhcp_client_stop (NMDHCPClient *self);
diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.c b/src/dhcp-manager/nm-dhcp-dhclient-utils.c
new file mode 100644
index 0000000000..cc5255ab59
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.c
@@ -0,0 +1,217 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include <config.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <string.h>
+#include <ctype.h>
+
+#include "nm-dhcp-dhclient-utils.h"
+
+#define CLIENTID_TAG "send dhcp-client-identifier"
+#define CLIENTID_FORMAT CLIENTID_TAG " \"%s\"; # added by NetworkManager"
+#define CLIENTID_FORMAT_OCTETS CLIENTID_TAG " %s; # added by NetworkManager"
+
+#define HOSTNAME_TAG "send host-name"
+#define HOSTNAME_FORMAT HOSTNAME_TAG " \"%s\"; # added by NetworkManager"
+
+#define ALSOREQ_TAG "also request "
+
+static void
+add_also_request (GPtrArray *array, const char *item)
+{
+ int i;
+
+ for (i = 0; i < array->len; i++) {
+ if (!strcmp (g_ptr_array_index (array, i), item))
+ return;
+ }
+ g_ptr_array_add (array, g_strdup (item));
+}
+
+char *
+nm_dhcp_dhclient_create_config (const char *interface,
+ NMSettingIP4Config *s_ip4,
+ guint8 *anycast_addr,
+ const char *hostname,
+ const char *orig_path,
+ const char *orig_contents)
+{
+ GString *new_contents;
+ GPtrArray *alsoreq;
+ int i;
+
+ new_contents = g_string_new (_("# Created by NetworkManager\n"));
+ alsoreq = g_ptr_array_sized_new (5);
+
+ if (orig_contents) {
+ char **lines, **line;
+ gboolean in_alsoreq = FALSE;
+
+ g_string_append_printf (new_contents, _("# Merged from %s\n\n"), orig_path);
+
+ lines = g_strsplit_set (orig_contents, "\n\r", 0);
+ for (line = lines; lines && *line; line++) {
+ char *p = *line;
+
+ if (!strlen (g_strstrip (p)))
+ continue;
+
+ /* Override config file "dhcp-client-id" and use one from the
+ * connection.
+ */
+ if ( s_ip4
+ && nm_setting_ip4_config_get_dhcp_client_id (s_ip4)
+ && !strncmp (p, CLIENTID_TAG, strlen (CLIENTID_TAG)))
+ continue;
+
+ /* Override config file hostname and use one from the connection */
+ if (hostname && !strncmp (p, HOSTNAME_TAG, strlen (HOSTNAME_TAG)))
+ continue;
+
+ /* Check for "also require" */
+ if (!strncmp (p, ALSOREQ_TAG, strlen (ALSOREQ_TAG))) {
+ in_alsoreq = TRUE;
+ p += strlen (ALSOREQ_TAG);
+ }
+
+ if (in_alsoreq) {
+ char **areq, **aiter;
+
+ /* Grab each 'also require' option and save for later */
+ areq = g_strsplit_set (p, "\t ,", -1);
+ for (aiter = areq; aiter && *aiter; aiter++) {
+ if (!strlen (g_strstrip (*aiter)))
+ continue;
+
+ if (*aiter[0] == ';') {
+ /* all done */
+ in_alsoreq = FALSE;
+ break;
+ }
+
+ if (!isalnum ((*aiter)[0]))
+ continue;
+
+ if ((*aiter)[strlen (*aiter) - 1] == ';') {
+ /* Remove the EOL marker */
+ (*aiter)[strlen (*aiter) - 1] = '\0';
+ in_alsoreq = FALSE;
+ }
+
+ add_also_request (alsoreq, *aiter);
+ }
+
+ if (areq)
+ g_strfreev (areq);
+
+ continue;
+ }
+
+ /* Existing configuration line is OK, add it to new configuration */
+ g_string_append (new_contents, *line);
+ g_string_append_c (new_contents, '\n');
+ }
+
+ if (lines)
+ g_strfreev (lines);
+ } else
+ g_string_append_c (new_contents, '\n');
+
+ /* Add NM options from connection */
+ if (s_ip4) {
+ const char *tmp;
+ gboolean added = FALSE;
+
+ tmp = nm_setting_ip4_config_get_dhcp_client_id (s_ip4);
+ if (tmp) {
+ gboolean is_octets = TRUE;
+ const char *p = tmp;
+
+ while (*p) {
+ if (!isxdigit (*p) && (*p != ':')) {
+ is_octets = FALSE;
+ break;
+ }
+ p++;
+ }
+
+ /* If the client ID is just hex digits and : then don't use quotes,
+ * because dhclient expects either a quoted ASCII string, or a byte
+ * array formated as hex octets separated by :
+ */
+ if (is_octets)
+ g_string_append_printf (new_contents, CLIENTID_FORMAT_OCTETS "\n", tmp);
+ else
+ g_string_append_printf (new_contents, CLIENTID_FORMAT "\n", tmp);
+ added = TRUE;
+ }
+
+ if (hostname) {
+ g_string_append_printf (new_contents, HOSTNAME_FORMAT "\n", hostname);
+ added = TRUE;
+ }
+
+ if (added)
+ g_string_append_c (new_contents, '\n');
+ }
+
+ /* Define options for classless static routes */
+ g_string_append (new_contents,
+ "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n");
+ g_string_append (new_contents,
+ "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n");
+ /* Web Proxy Auto-Discovery option (bgo #368423) */
+ g_string_append (new_contents, "option wpad code 252 = string;\n");
+
+ g_string_append_c (new_contents, '\n');
+
+ /* Everything we want to request from the DHCP server */
+ add_also_request (alsoreq, "rfc3442-classless-static-routes");
+ add_also_request (alsoreq, "ms-classless-static-routes");
+ add_also_request (alsoreq, "wpad");
+ add_also_request (alsoreq, "ntp-servers");
+
+ /* And add it to the dhclient configuration */
+ for (i = 0; i < alsoreq->len; i++) {
+ char *t = g_ptr_array_index (alsoreq, i);
+
+ g_string_append_printf (new_contents, "also request %s;\n", t);
+ g_free (t);
+ }
+ g_ptr_array_free (alsoreq, TRUE);
+
+ g_string_append_c (new_contents, '\n');
+
+ if (anycast_addr) {
+ g_string_append_printf (new_contents, "interface \"%s\" {\n"
+ " initial-interval 1; \n"
+ " anycast-mac ethernet %02x:%02x:%02x:%02x:%02x:%02x;\n"
+ "}\n",
+ interface,
+ anycast_addr[0], anycast_addr[1],
+ anycast_addr[2], anycast_addr[3],
+ anycast_addr[4], anycast_addr[5]);
+ }
+
+ return g_string_free (new_contents, FALSE);
+}
+
diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.h b/src/dhcp-manager/nm-dhcp-dhclient-utils.h
new file mode 100644
index 0000000000..77def55182
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_DHCP_DHCLIENT_UTILS_H
+#define NM_DHCP_DHCLIENT_UTILS_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include <nm-setting-ip4-config.h>
+
+char *nm_dhcp_dhclient_create_config (const char *interface,
+ NMSettingIP4Config *s_ip4,
+ guint8 *anycast_addr,
+ const char *hostname,
+ const char *orig_path,
+ const char *orig_contents);
+
+#endif /* NM_DHCP_DHCLIENT_UTILS_H */
+
diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c
index d7a6e32fb3..f6f2a540dc 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient.c
+++ b/src/dhcp-manager/nm-dhcp-dhclient.c
@@ -39,15 +39,18 @@
#include "nm-dhcp-dhclient.h"
#include "nm-utils.h"
#include "nm-logging.h"
+#include "nm-dhcp-dhclient-utils.h"
G_DEFINE_TYPE (NMDHCPDhclient, nm_dhcp_dhclient, NM_TYPE_DHCP_CLIENT)
#define NM_DHCP_DHCLIENT_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_DHCLIENT, NMDHCPDhclientPrivate))
-#if defined(TARGET_DEBIAN)
-#define NM_DHCLIENT_LEASE_DIR LOCALSTATEDIR "/lib/dhcp3"
-#elif defined(TARGET_SUSE) || defined(TARGET_MANDRIVA)
+#if defined(TARGET_DEBIAN) || defined(TARGET_SUSE) || defined(TARGET_MANDRIVA)
+#if defined(DHCLIENT_V3)
+#define NM_DHCLIENT_LEASE_DIR LOCALSTATEDIR "/lib/dhcp3"
+#else
#define NM_DHCLIENT_LEASE_DIR LOCALSTATEDIR "/lib/dhcp"
+#endif
#else
#define NM_DHCLIENT_LEASE_DIR LOCALSTATEDIR "/lib/dhclient"
#endif
@@ -300,121 +303,37 @@ out:
}
-#define DHCP_CLIENT_ID_TAG "send dhcp-client-identifier"
-#define DHCP_CLIENT_ID_FORMAT DHCP_CLIENT_ID_TAG " \"%s\"; # added by NetworkManager"
-#define DHCP_CLIENT_ID_FORMAT_OCTETS DHCP_CLIENT_ID_TAG " %s; # added by NetworkManager"
-
-#define DHCP_HOSTNAME_TAG "send host-name"
-#define DHCP_HOSTNAME_FORMAT DHCP_HOSTNAME_TAG " \"%s\"; # added by NetworkManager"
static gboolean
merge_dhclient_config (const char *iface,
const char *conf_file,
NMSettingIP4Config *s_ip4,
guint8 *anycast_addr,
+ const char *hostname,
const char *orig_path,
GError **error)
{
- GString *new_contents;
- char *orig_contents = NULL;
+ char *orig = NULL, *new;
gboolean success = FALSE;
g_return_val_if_fail (iface != NULL, FALSE);
g_return_val_if_fail (conf_file != NULL, FALSE);
- new_contents = g_string_new (_("# Created by NetworkManager\n"));
-
if (g_file_test (orig_path, G_FILE_TEST_EXISTS)) {
GError *read_error = NULL;
- if (!g_file_get_contents (orig_path, &orig_contents, NULL, &read_error)) {
+ if (!g_file_get_contents (orig_path, &orig, NULL, &read_error)) {
nm_log_warn (LOGD_DHCP, "(%s): error reading dhclient configuration %s: %s",
iface, orig_path, read_error->message);
g_error_free (read_error);
}
}
- /* Add existing options, if any, but ignore stuff NM will replace. */
- if (orig_contents) {
- char **lines = NULL, **line;
-
- g_string_append_printf (new_contents, _("# Merged from %s\n\n"), orig_path);
-
- lines = g_strsplit_set (orig_contents, "\n\r", 0);
- for (line = lines; lines && *line; line++) {
- gboolean ignore = FALSE;
-
- if (!strlen (g_strstrip (*line)))
- continue;
-
- if ( s_ip4
- && nm_setting_ip4_config_get_dhcp_client_id (s_ip4)
- && !strncmp (*line, DHCP_CLIENT_ID_TAG, strlen (DHCP_CLIENT_ID_TAG)))
- ignore = TRUE;
-
- if ( s_ip4
- && nm_setting_ip4_config_get_dhcp_hostname (s_ip4)
- && !strncmp (*line, DHCP_HOSTNAME_TAG, strlen (DHCP_HOSTNAME_TAG)))
- ignore = TRUE;
-
- if (!ignore) {
- g_string_append (new_contents, *line);
- g_string_append_c (new_contents, '\n');
- }
- }
-
- if (lines)
- g_strfreev (lines);
- g_free (orig_contents);
- } else
- g_string_append_c (new_contents, '\n');
-
- /* Add NM options from connection */
- if (s_ip4) {
- const char *tmp;
-
- tmp = nm_setting_ip4_config_get_dhcp_client_id (s_ip4);
- if (tmp) {
- gboolean is_octets = TRUE;
- const char *p = tmp;
-
- while (*p) {
- if (!isxdigit (*p) && (*p != ':')) {
- is_octets = FALSE;
- break;
- }
- p++;
- }
+ new = nm_dhcp_dhclient_create_config (iface, s_ip4, anycast_addr, hostname, orig_path, orig);
+ g_assert (new);
+ success = g_file_set_contents (conf_file, new, -1, error);
+ g_free (new);
- /* If the client ID is just hex digits and : then don't use quotes,
- * becuase dhclient expects either a quoted ASCII string, or a byte
- * array formated as hex octets separated by :
- */
- if (is_octets)
- g_string_append_printf (new_contents, DHCP_CLIENT_ID_FORMAT_OCTETS "\n", tmp);
- else
- g_string_append_printf (new_contents, DHCP_CLIENT_ID_FORMAT "\n", tmp);
- }
-
- tmp = nm_setting_ip4_config_get_dhcp_hostname (s_ip4);
- if (tmp)
- g_string_append_printf (new_contents, DHCP_HOSTNAME_FORMAT "\n", tmp);
- }
-
- if (anycast_addr) {
- g_string_append_printf (new_contents, "interface \"%s\" {\n"
- " initial-interval 1; \n"
- " anycast-mac ethernet %02x:%02x:%02x:%02x:%02x:%02x;\n"
- "}\n",
- iface,
- anycast_addr[0], anycast_addr[1],
- anycast_addr[2], anycast_addr[3],
- anycast_addr[4], anycast_addr[5]);
- }
-
- success = g_file_set_contents (conf_file, new_contents->str, -1, error);
-
- g_string_free (new_contents, TRUE);
return success;
}
@@ -427,7 +346,8 @@ merge_dhclient_config (const char *iface,
static char *
create_dhclient_config (const char *iface,
NMSettingIP4Config *s_ip4,
- guint8 *dhcp_anycast_addr)
+ guint8 *dhcp_anycast_addr,
+ const char *hostname)
{
char *orig = NULL, *tmp, *conf_file = NULL;
GError *error = NULL;
@@ -437,10 +357,12 @@ create_dhclient_config (const char *iface,
#if defined(TARGET_SUSE)
orig = g_strdup (SYSCONFDIR "/dhclient.conf");
-#elif defined(TARGET_DEBIAN)
+#elif defined(TARGET_DEBIAN) || defined(TARGET_GENTOO)
+#if defined(DHCLIENT_V3)
orig = g_strdup (SYSCONFDIR "/dhcp3/dhclient.conf");
-#elif defined(TARGET_GENTOO)
+#else
orig = g_strdup (SYSCONFDIR "/dhcp/dhclient.conf");
+#endif
#else
orig = g_strdup_printf (SYSCONFDIR "/dhclient-%s.conf", iface);
#endif
@@ -450,12 +372,24 @@ create_dhclient_config (const char *iface,
return FALSE;
}
+#if !defined(TARGET_SUSE) && !defined(TARGET_DEBIAN) && !defined(TARGET_GENTOO)
+ /* Try /etc/dhcp/ too (rh #607759) */
+ if (!g_file_test (orig, G_FILE_TEST_EXISTS)) {
+ g_free (orig);
+ orig = g_strdup_printf (SYSCONFDIR "/dhcp/dhclient-%s.conf", iface);
+ if (!orig) {
+ nm_log_warn (LOGD_DHCP, "(%s): not enough memory for dhclient options.", iface);
+ return FALSE;
+ }
+ }
+#endif
+
tmp = g_strdup_printf ("nm-dhclient-%s.conf", iface);
conf_file = g_build_filename ("/var", "run", tmp, NULL);
g_free (tmp);
error = NULL;
- success = merge_dhclient_config (iface, conf_file, s_ip4, dhcp_anycast_addr, orig, &error);
+ success = merge_dhclient_config (iface, conf_file, s_ip4, dhcp_anycast_addr, hostname, orig, &error);
if (!success) {
nm_log_warn (LOGD_DHCP, "(%s): error creating dhclient configuration: %s",
iface, error->message);
@@ -490,7 +424,6 @@ dhclient_start (NMDHCPClient *client,
guint log_domain;
g_return_val_if_fail (priv->pid_file == NULL, -1);
- g_return_val_if_fail (ip_opt != NULL, -1);
iface = nm_dhcp_client_get_iface (client);
uuid = nm_dhcp_client_get_uuid (client);
@@ -498,6 +431,15 @@ dhclient_start (NMDHCPClient *client,
log_domain = ipv6 ? LOGD_DHCP6 : LOGD_DHCP4;
+#if defined(DHCLIENT_V3)
+ if (ipv6) {
+ nm_log_warn (log_domain, "(%s): ISC dhcp3 does not support IPv6", iface);
+ return -1;
+ }
+#else
+ g_return_val_if_fail (ip_opt != NULL, -1);
+#endif
+
priv->pid_file = g_strdup_printf (LOCALSTATEDIR "/run/dhclient%s-%s.pid",
ipv6 ? "6" : "",
iface);
@@ -527,10 +469,11 @@ dhclient_start (NMDHCPClient *client,
g_ptr_array_add (argv, (gpointer) "-d");
+#if !defined(DHCLIENT_V3)
g_ptr_array_add (argv, (gpointer) ip_opt);
-
if (mode_opt)
g_ptr_array_add (argv, (gpointer) mode_opt);
+#endif
g_ptr_array_add (argv, (gpointer) "-sf"); /* Set script file */
g_ptr_array_add (argv, (gpointer) ACTION_SCRIPT_PATH );
@@ -568,14 +511,15 @@ dhclient_start (NMDHCPClient *client,
static GPid
real_ip4_start (NMDHCPClient *client,
NMSettingIP4Config *s_ip4,
- guint8 *dhcp_anycast_addr)
+ guint8 *dhcp_anycast_addr,
+ const char *hostname)
{
NMDHCPDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
const char *iface;
iface = nm_dhcp_client_get_iface (client);
- priv->conf_file = create_dhclient_config (iface, s_ip4, dhcp_anycast_addr);
+ priv->conf_file = create_dhclient_config (iface, s_ip4, dhcp_anycast_addr, hostname);
if (!priv->conf_file) {
nm_log_warn (LOGD_DHCP4, "(%s): error creating dhclient configuration file.", iface);
return -1;
@@ -588,6 +532,7 @@ static GPid
real_ip6_start (NMDHCPClient *client,
NMSettingIP6Config *s_ip6,
guint8 *dhcp_anycast_addr,
+ const char *hostname,
gboolean info_only)
{
return dhclient_start (client, "-6", info_only ? "-S" : "-N");
@@ -607,136 +552,6 @@ real_stop (NMDHCPClient *client)
remove (priv->pid_file);
}
-static const char **
-process_rfc3442_route (const char **octets, NMIP4Route **out_route)
-{
- const char **o = octets;
- int addr_len = 0, i = 0;
- long int tmp;
- NMIP4Route *route;
- char *next_hop;
- struct in_addr tmp_addr;
-
- if (!*o)
- return o; /* no prefix */
-
- tmp = strtol (*o, NULL, 10);
- if (tmp < 0 || tmp > 32) /* 32 == max IP4 prefix length */
- return o;
-
- route = nm_ip4_route_new ();
- nm_ip4_route_set_prefix (route, (guint32) tmp);
- o++;
-
- if (tmp > 0)
- addr_len = ((tmp - 1) / 8) + 1;
-
- /* ensure there's at least the address + next hop left */
- if (g_strv_length ((char **) o) < addr_len + 4)
- goto error;
-
- if (tmp) {
- const char *addr[4] = { "0", "0", "0", "0" };
- char *str_addr;
-
- for (i = 0; i < addr_len; i++)
- addr[i] = *o++;
-
- str_addr = g_strjoin (".", addr[0], addr[1], addr[2], addr[3], NULL);
- if (inet_pton (AF_INET, str_addr, &tmp_addr) <= 0) {
- g_free (str_addr);
- goto error;
- }
- tmp_addr.s_addr &= nm_utils_ip4_prefix_to_netmask ((guint32) tmp);
- nm_ip4_route_set_dest (route, tmp_addr.s_addr);
- }
-
- /* Handle next hop */
- next_hop = g_strjoin (".", o[0], o[1], o[2], o[3], NULL);
- if (inet_pton (AF_INET, next_hop, &tmp_addr) <= 0) {
- g_free (next_hop);
- goto error;
- }
- nm_ip4_route_set_next_hop (route, tmp_addr.s_addr);
- g_free (next_hop);
-
- *out_route = route;
- return o + 4; /* advance to past the next hop */
-
-error:
- nm_ip4_route_unref (route);
- return o;
-}
-
-static gboolean
-real_ip4_process_classless_routes (NMDHCPClient *client,
- GHashTable *options,
- NMIP4Config *ip4_config,
- guint32 *gwaddr)
-{
- const char *str;
- char **octets, **o;
- gboolean have_routes = FALSE;
- NMIP4Route *route = NULL;
-
- /* dhclient doesn't have actual support for rfc3442 classless static routes
- * upstream. Thus, people resort to defining the option in dhclient.conf
- * and using arbitrary formats like so:
- *
- * option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;
- *
- * See https://lists.isc.org/pipermail/dhcp-users/2008-December/007629.html
- */
-
- str = g_hash_table_lookup (options, "new_rfc3442_classless_static_routes");
- /* Microsoft version; same as rfc3442 but with a different option # (249) */
- if (!str)
- str = g_hash_table_lookup (options, "new_ms_classless_static_routes");
-
- if (!str || !strlen (str))
- return FALSE;
-
- o = octets = g_strsplit (str, " ", 0);
- if (g_strv_length (octets) < 5) {
- nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes '%s'", str);
- goto out;
- }
-
- while (*o) {
- route = NULL;
- o = (char **) process_rfc3442_route ((const char **) o, &route);
- if (!route) {
- nm_log_warn (LOGD_DHCP4, "ignoring invalid classless static routes");
- break;
- }
-
- have_routes = TRUE;
- if (nm_ip4_route_get_prefix (route) == 0) {
- /* gateway passed as classless static route */
- *gwaddr = nm_ip4_route_get_next_hop (route);
- nm_ip4_route_unref (route);
- } else {
- char addr[INET_ADDRSTRLEN + 1];
- char nh[INET_ADDRSTRLEN + 1];
- struct in_addr tmp;
-
- /* normal route */
- nm_ip4_config_take_route (ip4_config, route);
-
- tmp.s_addr = nm_ip4_route_get_dest (route);
- inet_ntop (AF_INET, &tmp, addr, sizeof (addr));
- tmp.s_addr = nm_ip4_route_get_next_hop (route);
- inet_ntop (AF_INET, &tmp, nh, sizeof (nh));
- nm_log_info (LOGD_DHCP4, " classless static route %s/%d gw %s",
- addr, nm_ip4_route_get_prefix (route), nh);
- }
- }
-
-out:
- g_strfreev (octets);
- return have_routes;
-}
-
/***************************************************/
static void
@@ -773,6 +588,5 @@ nm_dhcp_dhclient_class_init (NMDHCPDhclientClass *dhclient_class)
client_class->ip4_start = real_ip4_start;
client_class->ip6_start = real_ip6_start;
client_class->stop = real_stop;
- client_class->ip4_process_classless_routes = real_ip4_process_classless_routes;
}
diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c
index 403431fcb4..4fb703c480 100644
--- a/src/dhcp-manager/nm-dhcp-dhcpcd.c
+++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c
@@ -88,14 +88,15 @@ dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED)
static GPid
real_ip4_start (NMDHCPClient *client,
NMSettingIP4Config *s_ip4,
- guint8 *dhcp_anycast_addr)
+ guint8 *dhcp_anycast_addr,
+ const char *hostname)
{
NMDHCPDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client);
GPtrArray *argv = NULL;
GPid pid = -1;
GError *error = NULL;
char *pid_contents = NULL, *binary_name, *cmd_str;
- const char *iface, *uuid, *hostname;
+ const char *iface, *uuid;
g_return_val_if_fail (priv->pid_file == NULL, -1);
@@ -127,10 +128,11 @@ real_ip4_start (NMDHCPClient *client,
g_ptr_array_add (argv, (gpointer) "-L"); /* Disable built-in IPv4LL since we use avahi-autoipd */
+ g_ptr_array_add (argv, (gpointer) "-G"); /* Let NM handle routing */
+
g_ptr_array_add (argv, (gpointer) "-c"); /* Set script file */
g_ptr_array_add (argv, (gpointer) ACTION_SCRIPT_PATH );
- hostname = nm_setting_ip4_config_get_dhcp_hostname (s_ip4);
if (hostname && strlen (hostname)) {
g_ptr_array_add (argv, (gpointer) "-h"); /* Send hostname to DHCP server */
g_ptr_array_add (argv, (gpointer) hostname );
@@ -160,6 +162,7 @@ static GPid
real_ip6_start (NMDHCPClient *client,
NMSettingIP6Config *s_ip6,
guint8 *dhcp_anycast_addr,
+ const char *hostname,
gboolean info_only)
{
nm_log_warn (LOGD_DHCP6, "the dhcpcd backend does not support IPv6.");
@@ -178,83 +181,6 @@ real_stop (NMDHCPClient *client)
remove (priv->pid_file);
}
-static gboolean
-real_ip4_process_classless_routes (NMDHCPClient *client,
- GHashTable *options,
- NMIP4Config *ip4_config,
- guint32 *gwaddr)
-{
- const char *str;
- char **routes, **r;
- gboolean have_routes = FALSE;
-
- /* Classless static routes over-ride any static routes and routers
- * provided. We should also check for MS classless static routes as
- * they implemented the draft RFC using their own code.
- */
- str = g_hash_table_lookup (options, "new_classless_static_routes");
- if (!str)
- str = g_hash_table_lookup (options, "new_ms_classless_static_routes");
-
- if (!str || !strlen (str))
- return FALSE;
-
- routes = g_strsplit (str, " ", 0);
- if (g_strv_length (routes) == 0)
- goto out;
-
- if ((g_strv_length (routes) % 2) != 0) {
- nm_log_warn (LOGD_DHCP4, " classless static routes provided, but invalid");
- goto out;
- }
-
- for (r = routes; *r; r += 2) {
- char *slash;
- NMIP4Route *route;
- int rt_cidr = 32;
- struct in_addr rt_addr;
- struct in_addr rt_route;
-
- slash = strchr(*r, '/');
- if (slash) {
- *slash = '\0';
- errno = 0;
- rt_cidr = strtol (slash + 1, NULL, 10);
- if ((errno == EINVAL) || (errno == ERANGE)) {
- nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route cidr: '%s'", slash + 1);
- continue;
- }
- }
- if (inet_pton (AF_INET, *r, &rt_addr) <= 0) {
- nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route address: '%s'", *r);
- continue;
- }
- if (inet_pton (AF_INET, *(r + 1), &rt_route) <= 0) {
- nm_log_warn (LOGD_DHCP4, "DHCP provided invalid classless static route gateway: '%s'", *(r + 1));
- continue;
- }
-
- have_routes = TRUE;
- if (rt_cidr == 0 && rt_addr.s_addr == 0) {
- /* FIXME: how to handle multiple routers? */
- *gwaddr = rt_addr.s_addr;
- } else {
- route = nm_ip4_route_new ();
- nm_ip4_route_set_dest (route, (guint32) rt_addr.s_addr);
- nm_ip4_route_set_prefix (route, rt_cidr);
- nm_ip4_route_set_next_hop (route, (guint32) rt_route.s_addr);
-
-
- nm_ip4_config_take_route (ip4_config, route);
- nm_log_info (LOGD_DHCP4, " classless static route %s/%d gw %s", *r, rt_cidr, *(r + 1));
- }
- }
-
-out:
- g_strfreev (routes);
- return have_routes;
-}
-
/***************************************************/
static void
@@ -289,6 +215,5 @@ nm_dhcp_dhcpcd_class_init (NMDHCPDhcpcdClass *dhcpcd_class)
client_class->ip4_start = real_ip4_start;
client_class->ip6_start = real_ip6_start;
client_class->stop = real_stop;
- client_class->ip4_process_classless_routes = real_ip4_process_classless_routes;
}
diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c
index 7b110fb232..a1e3e5e24d 100644
--- a/src/dhcp-manager/nm-dhcp-manager.c
+++ b/src/dhcp-manager/nm-dhcp-manager.c
@@ -265,8 +265,13 @@ get_client_type (const char *client, GError **error)
const char *dhclient_path = NULL;
const char *dhcpcd_path = NULL;
- dhclient_path = nm_dhcp_dhclient_get_path (DHCLIENT_PATH);
- dhcpcd_path = nm_dhcp_dhcpcd_get_path (DHCPCD_PATH);
+ /* If a client was disabled at build-time, its *_PATH define will be
+ * an empty string.
+ */
+ if (DHCLIENT_PATH && strlen (DHCLIENT_PATH))
+ dhclient_path = nm_dhcp_dhclient_get_path (DHCLIENT_PATH);
+ if (DHCPCD_PATH && strlen (DHCPCD_PATH))
+ dhcpcd_path = nm_dhcp_dhcpcd_get_path (DHCPCD_PATH);
if (!client) {
if (dhclient_path)
@@ -407,6 +412,7 @@ client_start (NMDHCPManager *self,
NMSettingIP6Config *s_ip6,
guint32 timeout,
guint8 *dhcp_anycast_addr,
+ const char *hostname,
gboolean info_only)
{
NMDHCPManagerPrivate *priv;
@@ -438,9 +444,9 @@ client_start (NMDHCPManager *self,
add_client (self, client);
if (ipv6)
- success = nm_dhcp_client_start_ip6 (client, s_ip6, dhcp_anycast_addr, info_only);
+ success = nm_dhcp_client_start_ip6 (client, s_ip6, dhcp_anycast_addr, hostname, info_only);
else
- success = nm_dhcp_client_start_ip4 (client, s_ip4, dhcp_anycast_addr);
+ success = nm_dhcp_client_start_ip4 (client, s_ip4, dhcp_anycast_addr, hostname);
if (!success) {
remove_client (self, client);
@@ -462,6 +468,7 @@ nm_dhcp_manager_start_ip4 (NMDHCPManager *self,
{
NMDHCPManagerPrivate *priv;
NMDHCPClient *client = NULL;
+ const char *hostname = NULL;
g_return_val_if_fail (self, NULL);
g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
@@ -476,27 +483,26 @@ nm_dhcp_manager_start_ip4 (NMDHCPManager *self,
g_return_val_if_fail (strcmp (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0, NULL);
}
- if ( nm_setting_ip4_config_get_dhcp_send_hostname (s_ip4)
- && (nm_setting_ip4_config_get_dhcp_hostname (s_ip4) == NULL)
- && priv->hostname_provider != NULL) {
-
- s_ip4 = NM_SETTING_IP4_CONFIG (nm_setting_duplicate (NM_SETTING (s_ip4)));
+ /* If we're asked to send the hostname to DHCP server, and the hostname
+ * isn't specified, and a hostname provider is registered: use that
+ */
+ if (nm_setting_ip4_config_get_dhcp_send_hostname (s_ip4)) {
+ hostname = nm_setting_ip4_config_get_dhcp_hostname (s_ip4);
- /* We're asked to send the hostname to DHCP server, the hostname
- * isn't specified, and a hostname provider is registered: use that
+ /* If we're supposed to send the hostname to the DHCP server but
+ * the user didn't specify one, use the persistent hostname.
*/
- g_object_set (G_OBJECT (s_ip4),
- NM_SETTING_IP4_CONFIG_DHCP_HOSTNAME,
- nm_hostname_provider_get_hostname (priv->hostname_provider),
- NULL);
- } else
- g_object_ref (s_ip4);
+ if (!hostname && priv->hostname_provider) {
+ hostname = nm_hostname_provider_get_hostname (priv->hostname_provider);
+ if ( hostname
+ && (!strcmp (hostname, "localhost.localdomain") ||
+ !strcmp (hostname, "localhost6.localdomain6")))
+ hostname = NULL;
+ }
+ }
}
- client = client_start (self, iface, uuid, FALSE, s_ip4, NULL, timeout, dhcp_anycast_addr, FALSE);
-
- if (s_ip4)
- g_object_unref (s_ip4);
+ client = client_start (self, iface, uuid, FALSE, s_ip4, NULL, timeout, dhcp_anycast_addr, hostname, FALSE);
return client;
}
@@ -511,7 +517,7 @@ nm_dhcp_manager_start_ip6 (NMDHCPManager *self,
guint8 *dhcp_anycast_addr,
gboolean info_only)
{
- return client_start (self, iface, uuid, TRUE, NULL, s_ip6, timeout, dhcp_anycast_addr, info_only);
+ return client_start (self, iface, uuid, TRUE, NULL, s_ip6, timeout, dhcp_anycast_addr, NULL, info_only);
}
static void
diff --git a/src/dhcp-manager/tests/Makefile.am b/src/dhcp-manager/tests/Makefile.am
new file mode 100644
index 0000000000..b075fd683a
--- /dev/null
+++ b/src/dhcp-manager/tests/Makefile.am
@@ -0,0 +1,28 @@
+INCLUDES = \
+ -I$(top_srcdir)/include \
+ -I${top_srcdir}/libnm-util \
+ -I$(top_srcdir)/src/dhcp-manager
+
+noinst_PROGRAMS = test-dhcp-dhclient
+
+####### policy /etc/hosts test #######
+
+test_dhcp_dhclient_SOURCES = \
+ test-dhcp-dhclient.c
+
+test_dhcp_dhclient_CPPFLAGS = \
+ $(GLIB_CFLAGS)
+
+test_dhcp_dhclient_LDADD = \
+ -ldl \
+ $(top_builddir)/src/dhcp-manager/libdhcp-dhclient.la \
+ $(top_builddir)/libnm-util/libnm-util.la \
+ $(GLIB_LIBS)
+
+if WITH_TESTS
+
+check-local: test-dhcp-dhclient
+ $(abs_builddir)/test-dhcp-dhclient
+
+endif
+
diff --git a/src/dhcp-manager/tests/test-dhcp-dhclient.c b/src/dhcp-manager/tests/test-dhcp-dhclient.c
new file mode 100644
index 0000000000..c1cd6f80c4
--- /dev/null
+++ b/src/dhcp-manager/tests/test-dhcp-dhclient.c
@@ -0,0 +1,249 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ */
+
+#include <glib.h>
+#include <string.h>
+
+#include "nm-dhcp-dhclient-utils.h"
+#include "nm-utils.h"
+
+#define DEBUG 0
+
+static void
+test_config (const char *orig,
+ const char *expected,
+ const char *hostname,
+ const char *dhcp_client_id,
+ const char *iface,
+ guint8 *anycast_addr)
+{
+ NMSettingIP4Config *s_ip4;
+ char *new;
+
+ s_ip4 = (NMSettingIP4Config *) nm_setting_ip4_config_new ();
+ g_object_set (s_ip4, NM_SETTING_IP4_CONFIG_DHCP_CLIENT_ID, dhcp_client_id, NULL);
+
+ new = nm_dhcp_dhclient_create_config (iface,
+ s_ip4,
+ anycast_addr,
+ hostname,
+ "/path/to/dhclient.conf",
+ orig);
+ g_assert (new != NULL);
+
+#if DEBUG
+ g_message ("\n- NEW ---------------------------------\n"
+ "%s"
+ "+ EXPECTED ++++++++++++++++++++++++++++++\n"
+ "%s"
+ "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
+ new, expected);
+#endif
+ g_assert (strlen (new) == strlen (expected));
+ g_assert (strcmp (new, expected) == 0);
+ g_free (new);
+}
+
+/*******************************************/
+
+static const char *orig_missing_expected = \
+ "# Created by NetworkManager\n"
+ "\n"
+ "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
+ "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
+ "option wpad code 252 = string;\n"
+ "\n"
+ "also request rfc3442-classless-static-routes;\n"
+ "also request ms-classless-static-routes;\n"
+ "also request wpad;\n"
+ "also request ntp-servers;\n"
+ "\n";
+
+static void
+test_orig_missing (void)
+{
+ test_config (NULL, orig_missing_expected,
+ NULL,
+ NULL,
+ "eth0",
+ NULL);
+}
+
+/*******************************************/
+
+static const char *override_client_id_orig = \
+ "send dhcp-client-identifier 00:30:04:20:7A:08;\n";
+
+static const char *override_client_id_expected = \
+ "# Created by NetworkManager\n"
+ "# Merged from /path/to/dhclient.conf\n"
+ "\n"
+ "send dhcp-client-identifier 11:22:33:44:55:66; # added by NetworkManager\n"
+ "\n"
+ "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
+ "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
+ "option wpad code 252 = string;\n"
+ "\n"
+ "also request rfc3442-classless-static-routes;\n"
+ "also request ms-classless-static-routes;\n"
+ "also request wpad;\n"
+ "also request ntp-servers;\n"
+ "\n";
+
+static void
+test_override_client_id (void)
+{
+ test_config (override_client_id_orig, override_client_id_expected,
+ NULL,
+ "11:22:33:44:55:66",
+ "eth0",
+ NULL);
+}
+
+/*******************************************/
+
+static const char *override_hostname_orig = \
+ "send host-name \"foobar\";\n";
+
+static const char *override_hostname_expected = \
+ "# Created by NetworkManager\n"
+ "# Merged from /path/to/dhclient.conf\n"
+ "\n"
+ "send host-name \"blahblah\"; # added by NetworkManager\n"
+ "\n"
+ "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
+ "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
+ "option wpad code 252 = string;\n"
+ "\n"
+ "also request rfc3442-classless-static-routes;\n"
+ "also request ms-classless-static-routes;\n"
+ "also request wpad;\n"
+ "also request ntp-servers;\n"
+ "\n";
+
+static void
+test_override_hostname (void)
+{
+ test_config (override_hostname_orig, override_hostname_expected,
+ "blahblah",
+ NULL,
+ "eth0",
+ NULL);
+}
+
+/*******************************************/
+
+static const char *existing_alsoreq_orig = \
+ "also request something;\n"
+ "also request another-thing;\n"
+ ;
+
+static const char *existing_alsoreq_expected = \
+ "# Created by NetworkManager\n"
+ "# Merged from /path/to/dhclient.conf\n"
+ "\n"
+ "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
+ "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
+ "option wpad code 252 = string;\n"
+ "\n"
+ "also request something;\n"
+ "also request another-thing;\n"
+ "also request rfc3442-classless-static-routes;\n"
+ "also request ms-classless-static-routes;\n"
+ "also request wpad;\n"
+ "also request ntp-servers;\n"
+ "\n";
+
+static void
+test_existing_alsoreq (void)
+{
+ test_config (existing_alsoreq_orig, existing_alsoreq_expected,
+ NULL,
+ NULL,
+ "eth0",
+ NULL);
+}
+
+/*******************************************/
+
+static const char *existing_multiline_alsoreq_orig = \
+ "also request something another-thing yet-another-thing\n"
+ " foobar baz blah;\n"
+ ;
+
+static const char *existing_multiline_alsoreq_expected = \
+ "# Created by NetworkManager\n"
+ "# Merged from /path/to/dhclient.conf\n"
+ "\n"
+ "option rfc3442-classless-static-routes code 121 = array of unsigned integer 8;\n"
+ "option ms-classless-static-routes code 249 = array of unsigned integer 8;\n"
+ "option wpad code 252 = string;\n"
+ "\n"
+ "also request something;\n"
+ "also request another-thing;\n"
+ "also request yet-another-thing;\n"
+ "also request foobar;\n"
+ "also request baz;\n"
+ "also request blah;\n"
+ "also request rfc3442-classless-static-routes;\n"
+ "also request ms-classless-static-routes;\n"
+ "also request wpad;\n"
+ "also request ntp-servers;\n"
+ "\n";
+
+static void
+test_existing_multiline_alsoreq (void)
+{
+ test_config (existing_multiline_alsoreq_orig, existing_multiline_alsoreq_expected,
+ NULL,
+ NULL,
+ "eth0",
+ NULL);
+}
+
+/*******************************************/
+
+#if GLIB_CHECK_VERSION(2,25,12)
+typedef GTestFixtureFunc TCFunc;
+#else
+typedef void (*TCFunc)(void);
+#endif
+
+#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (TCFunc) t, NULL)
+
+int main (int argc, char **argv)
+{
+ GTestSuite *suite;
+
+ g_test_init (&argc, &argv, NULL);
+
+ g_type_init ();
+
+ suite = g_test_get_root ();
+
+ g_test_suite_add (suite, TESTCASE (test_orig_missing, NULL));
+ g_test_suite_add (suite, TESTCASE (test_override_client_id, NULL));
+ g_test_suite_add (suite, TESTCASE (test_override_hostname, NULL));
+ g_test_suite_add (suite, TESTCASE (test_existing_alsoreq, NULL));
+ g_test_suite_add (suite, TESTCASE (test_existing_multiline_alsoreq, NULL));
+
+ return g_test_run ();
+}
+
diff --git a/src/dns-manager/Makefile.am b/src/dns-manager/Makefile.am
new file mode 100644
index 0000000000..7b5fc4f847
--- /dev/null
+++ b/src/dns-manager/Makefile.am
@@ -0,0 +1,30 @@
+INCLUDES = \
+ -I${top_srcdir}/src/logging \
+ -I${top_srcdir}/libnm-util \
+ -I${top_srcdir}/src \
+ -I${top_srcdir}/include
+
+noinst_LTLIBRARIES = libdns-manager.la
+
+libdns_manager_la_SOURCES = \
+ nm-dns-manager.h \
+ nm-dns-manager.c \
+ nm-dns-plugin.h \
+ nm-dns-plugin.c \
+ nm-dns-dnsmasq.h \
+ nm-dns-dnsmasq.c \
+ nm-dns-bind.h \
+ nm-dns-bind.c \
+ nm-dns-utils.h \
+ nm-dns-utils.c
+
+libdns_manager_la_CPPFLAGS = \
+ $(DBUS_CFLAGS) \
+ $(GLIB_CFLAGS) \
+ -DLOCALSTATEDIR=\"$(localstatedir)\"
+
+libdns_manager_la_LIBADD = \
+ $(top_builddir)/src/logging/libnm-logging.la \
+ $(DBUS_LIBS) \
+ $(GLIB_LIBS)
+
diff --git a/src/dns-manager/nm-dns-bind.c b/src/dns-manager/nm-dns-bind.c
new file mode 100644
index 0000000000..9e3fc1739e
--- /dev/null
+++ b/src/dns-manager/nm-dns-bind.c
@@ -0,0 +1,528 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Dan Williams <dcbw@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "nm-dns-bind.h"
+#include "nm-logging.h"
+#include "nm-ip4-config.h"
+#include "nm-ip6-config.h"
+
+G_DEFINE_TYPE (NMDnsBind, nm_dns_bind, NM_TYPE_DNS_PLUGIN)
+
+#define NM_DNS_BIND_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DNS_BIND, NMDnsBindPrivate))
+
+#define PIDFILE LOCALSTATEDIR "/run/nm-dns-named.pid"
+#define CONFFILE LOCALSTATEDIR "/run/nm-dns-named.conf"
+
+typedef struct {
+ GPid pid;
+} NMDnsBindPrivate;
+
+/*******************************************/
+
+static inline const char *
+find_bind (void)
+{
+ static const char *paths[] = {
+ "/usr/local/sbin/named",
+ "/usr/sbin/named",
+ "/sbin/named",
+ NULL
+ };
+ const char **binary = paths;
+
+ while (*binary != NULL) {
+ if (g_file_test (*binary, G_FILE_TEST_EXISTS))
+ return *binary;
+ binary++;
+ }
+ return NULL;
+}
+
+static gboolean
+start_bind (NMDnsBind *self)
+{
+ const char *argv[10];
+
+ argv[0] = find_bind ();
+ argv[1] = "-f"; /* don't daemonize; stay in foreground */
+ argv[2] = "-c";
+ argv[3] = CONFFILE;
+ argv[4] = NULL;
+
+ /* And finally spawn bind */
+ return nm_dns_plugin_child_spawn (NM_DNS_PLUGIN (self), argv, PIDFILE, "bin/named");
+}
+
+/*******************************************/
+
+static gboolean
+find_address (GPtrArray *array, const char *addr)
+{
+ int n;
+
+ for (n = 0; n < array->len; n++) {
+ if (g_strcmp0 ((const char*) g_ptr_array_index (array, n), addr) == 0)
+ return TRUE;
+ }
+ return FALSE;
+}
+
+static void
+add_ip4_nameservers (NMIP4Config *ip4, GPtrArray *array)
+{
+ int i;
+
+ for (i = 0; i < nm_ip4_config_get_num_nameservers (ip4); i++) {
+ char buf[INET_ADDRSTRLEN + 1];
+ struct in_addr addr;
+
+ memset (&buf[0], 0, sizeof (buf));
+ addr.s_addr = nm_ip4_config_get_nameserver (ip4, i);
+ if (inet_ntop (AF_INET, &addr, buf, sizeof (buf))) {
+ if (!find_address (array, buf))
+ g_ptr_array_add (array, g_strdup (buf));
+ }
+ }
+}
+
+static gboolean
+ip6_addr_to_string (const struct in6_addr *addr, char *buf, size_t buflen)
+{
+ /* inet_ntop is probably supposed to do this for us, but it doesn't */
+ if (IN6_IS_ADDR_V4MAPPED (addr))
+ return !!inet_ntop (AF_INET, &(addr->s6_addr32[3]), buf, buflen);
+
+ return !!inet_ntop (AF_INET6, addr, buf, buflen);
+}
+
+static void
+add_ip6_nameservers (NMIP6Config *ip6, GPtrArray *array)
+{
+ char buf[INET6_ADDRSTRLEN + 1];
+ int i;
+
+ for (i = 0; i < nm_ip6_config_get_num_nameservers (ip6); i++) {
+ memset (buf, 0, sizeof (buf));
+ if (ip6_addr_to_string (nm_ip6_config_get_nameserver (ip6, i), buf, sizeof (buf))) {
+ if (!find_address (array, buf))
+ g_ptr_array_add (array, g_strdup (buf));
+ }
+ }
+}
+
+typedef struct {
+ guint32 dhash;
+ char *domain;
+ GPtrArray *servers;
+} ZoneInfo;
+
+static ZoneInfo *
+zone_new (const char *domain)
+{
+ ZoneInfo *info;
+
+ g_return_val_if_fail (domain != NULL, NULL);
+
+ info = g_malloc0 (sizeof (ZoneInfo));
+ info->domain = g_strdup (domain);
+ info->dhash = g_str_hash (domain);
+ info->servers = g_ptr_array_sized_new (4);
+ return info;
+}
+
+static void
+zone_add_nameserver (ZoneInfo *info, const char *server)
+{
+ guint32 i;
+
+ g_return_if_fail (info != NULL);
+ g_return_if_fail (server != NULL);
+
+ for (i = 0; i < info->servers->len; i++) {
+ if (g_strcmp0 ((char *) g_ptr_array_index (info->servers, i), server) == 0)
+ return;
+ }
+ g_ptr_array_add (info->servers, g_strdup (server));
+}
+
+static void
+zone_free (ZoneInfo *info)
+{
+ g_return_if_fail (info != NULL);
+
+ g_free (info->domain);
+ g_ptr_array_foreach (info->servers, (GFunc) g_free, NULL);
+ g_ptr_array_free (info->servers, TRUE);
+ memset (info, 0, sizeof (ZoneInfo));
+ g_free (info);
+}
+
+static ZoneInfo *
+find_zone (GPtrArray *zones, const char *domain)
+{
+ guint32 dhash, i;
+
+ g_return_val_if_fail (domain != NULL, FALSE);
+
+ dhash = g_str_hash (domain);
+ for (i = 0; i < zones->len; i++) {
+ ZoneInfo *zone = g_ptr_array_index (zones, i);
+
+ if (zone->dhash == dhash)
+ return zone;
+ }
+ return NULL;
+}
+
+static void
+add_zone (GObject *ip, GPtrArray *zones)
+{
+ guint32 i, j, ns, nd, nn;
+ GPtrArray *to_add;
+ ZoneInfo *z;
+
+ if (NM_IS_IP4_CONFIG (ip)) {
+ ns = nm_ip4_config_get_num_searches (NM_IP4_CONFIG (ip));
+ nd = nm_ip4_config_get_num_domains (NM_IP4_CONFIG (ip));
+ nn = nm_ip4_config_get_num_nameservers (NM_IP4_CONFIG (ip));
+ } else if (NM_IS_IP6_CONFIG (ip)) {
+ ns = nm_ip6_config_get_num_searches (NM_IP6_CONFIG (ip));
+ nd = nm_ip6_config_get_num_domains (NM_IP6_CONFIG (ip));
+ nn = nm_ip6_config_get_num_nameservers (NM_IP6_CONFIG (ip));
+ } else
+ g_assert_not_reached ();
+
+ /* If we don't have any domains or searches, or we don't have any
+ * nameservers, we can't do split DNS for this config.
+ */
+ if ((!nd && !ns) || !nn)
+ return;
+
+ to_add = g_ptr_array_sized_new (MAX (ns, nd));
+
+ /* searches are preferred over domains */
+ for (i = 0; i < ns; i++) {
+ const char *domain = NULL;
+
+ if (NM_IS_IP4_CONFIG (ip))
+ domain = nm_ip4_config_get_search (NM_IP4_CONFIG (ip), i);
+ else if (NM_IS_IP6_CONFIG (ip))
+ domain = nm_ip6_config_get_search (NM_IP6_CONFIG (ip), i);
+
+ z = find_zone (zones, domain);
+ if (!z) {
+ z = zone_new (domain);
+ g_ptr_array_add (zones, z);
+ }
+ g_ptr_array_add (to_add, z);
+ }
+
+ if (ns == 0) {
+ /* If no searches, add any domains */
+ for (i = 0; i < nd; i++) {
+ const char *domain = NULL;
+
+ if (NM_IS_IP4_CONFIG (ip))
+ domain = nm_ip4_config_get_domain (NM_IP4_CONFIG (ip), i);
+ else if (NM_IS_IP6_CONFIG (ip))
+ domain = nm_ip6_config_get_domain (NM_IP6_CONFIG (ip), i);
+
+ z = find_zone (zones, domain);
+ if (!z) {
+ z = zone_new (domain);
+ g_ptr_array_add (zones, z);
+ }
+ g_ptr_array_add (to_add, z);
+ }
+ }
+
+ /* Now add the nameservers to every zone for this config */
+ for (i = 0; i < nn; i++) {
+ char buf[INET6_ADDRSTRLEN + 1];
+ struct in_addr addr4;
+ const struct in6_addr *addr6;
+
+ memset (&buf[0], 0, sizeof (buf));
+
+ if (NM_IS_IP4_CONFIG (ip)) {
+ addr4.s_addr = nm_ip4_config_get_nameserver (NM_IP4_CONFIG (ip), i);
+ if (!inet_ntop (AF_INET, &addr4, buf, sizeof (buf)))
+ continue;
+ } else if (NM_IS_IP6_CONFIG (ip)) {
+ addr6 = nm_ip6_config_get_nameserver (NM_IP6_CONFIG (ip), i);
+ if (!ip6_addr_to_string (addr6, buf, sizeof (buf)))
+ continue;
+ }
+
+ /* Add this nameserver to every zone from this IP config */
+ for (j = 0; j < to_add->len; j++) {
+ z = g_ptr_array_index (to_add, j);
+ zone_add_nameserver (z, buf);
+ }
+ }
+
+ g_ptr_array_free (to_add, TRUE);
+}
+
+static gboolean
+update (NMDnsPlugin *plugin,
+ const GSList *vpn_configs,
+ const GSList *dev_configs,
+ const GSList *other_configs,
+ const char *hostname)
+{
+ NMDnsBind *self = NM_DNS_BIND (plugin);
+ NMDnsBindPrivate *priv = NM_DNS_BIND_GET_PRIVATE (self);
+ GString *conf;
+ GPtrArray *globals, *zones;
+ GSList *iter;
+ GError *error = NULL;
+ int ignored, i, j;
+ gboolean success = FALSE;
+
+ /* Build up the new bind config file */
+ conf = g_string_sized_new (200);
+ globals = g_ptr_array_sized_new (6);
+
+ /* If any of the VPN configs *don't* have domains or searches, then we
+ * dont' have any split DNS configuration for them, and we add them
+ * first in the global nameserver lists. Otherwise we add them later as
+ * split DNS zones.
+ */
+ for (iter = (GSList *) vpn_configs; iter;iter = g_slist_next (iter)) {
+ if (NM_IS_IP4_CONFIG (iter->data)) {
+ NMIP4Config *ip4 = NM_IP4_CONFIG (iter->data);
+
+ if (!nm_ip4_config_get_num_domains (ip4) && !nm_ip4_config_get_num_searches (ip4))
+ add_ip4_nameservers (ip4, globals);
+ } else if (NM_IS_IP6_CONFIG (iter->data)) {
+ NMIP6Config *ip6 = NM_IP6_CONFIG (iter->data);
+
+ if (!nm_ip6_config_get_num_domains (ip6) && !nm_ip6_config_get_num_searches (ip6))
+ add_ip6_nameservers (ip6, globals);
+ }
+ }
+
+ /* Get a list of global upstream servers with dupe checking */
+ for (iter = (GSList *) dev_configs; iter;iter = g_slist_next (iter)) {
+ if (NM_IS_IP4_CONFIG (iter->data))
+ add_ip4_nameservers (NM_IP4_CONFIG (iter->data), globals);
+ else if (NM_IS_IP6_CONFIG (iter->data))
+ add_ip6_nameservers (NM_IP6_CONFIG (iter->data), globals);
+ }
+
+ /* And any other random configs with dupe checking */
+ for (iter = (GSList *) other_configs; iter;iter = g_slist_next (iter)) {
+ if (NM_IS_IP4_CONFIG (iter->data))
+ add_ip4_nameservers (NM_IP4_CONFIG (iter->data), globals);
+ else if (NM_IS_IP6_CONFIG (iter->data))
+ add_ip6_nameservers (NM_IP6_CONFIG (iter->data), globals);
+ }
+
+ g_string_append (conf,
+ "options {\n"
+ " directory \"" LOCALSTATEDIR "/named\";\n"
+ " forward only;\n"
+ " recursion yes;\n"
+ " listen-on-v6 { ::1; };\n"
+ " listen-on { 127.0.0.1; };\n"
+ " forwarders {\n");
+
+ for (i = 0; i < globals->len; i++) {
+ char *ns = g_ptr_array_index (globals, i);
+
+ g_string_append_printf (conf, " %s;\n", ns);
+ g_free (ns);
+ }
+ g_ptr_array_free (globals, TRUE);
+
+ g_string_append (conf,
+ " };\n"
+ "};\n\n");
+
+ /* Build up the list of any split DNS zones, avoiding duplicates */
+ zones = g_ptr_array_sized_new (4);
+ for (iter = (GSList *) vpn_configs; iter;iter = g_slist_next (iter)) {
+ if (NM_IS_IP4_CONFIG (iter->data))
+ add_zone (G_OBJECT (iter->data), zones);
+ else if (NM_IS_IP6_CONFIG (iter->data))
+ add_zone (G_OBJECT (iter->data), zones);
+ }
+
+ /* Add all the zones to the config */
+ for (i = 0; i < zones->len; i++) {
+ ZoneInfo *z = g_ptr_array_index (zones, i);
+
+ g_string_append_printf (conf,
+ "zone \"%s\" IN {\n"
+ " type forward;\n"
+ " forward only;\n"
+ " forwarders {\n",
+ z->domain);
+
+ /* Add each nameserver for this zone */
+ for (j = 0; j < z->servers->len; j++) {
+ g_string_append_printf (conf,
+ " %s;\n",
+ (const char *) g_ptr_array_index (z->servers, j));
+ }
+
+ g_string_append (conf,
+ " };\n"
+ "};\n\n");
+
+ zone_free (z);
+ }
+ g_ptr_array_free (zones, TRUE);
+
+ /* Write out the config file */
+ if (!g_file_set_contents (CONFFILE, conf->str, -1, &error)) {
+ nm_log_warn (LOGD_DNS, "Failed to write named config file %s: (%d) %s",
+ CONFFILE,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ goto out;
+ }
+ ignored = chmod (CONFFILE, 0600);
+
+ nm_log_dbg (LOGD_DNS, "BIND local caching DNS configuration:");
+ nm_log_dbg (LOGD_DNS, "%s", conf->str);
+
+ if (priv->pid) {
+ /* Send it SIGHUP to reload the new configuration */
+ if (kill (priv->pid, SIGHUP) == 0)
+ success = TRUE;
+ else {
+ /* Sigh... some error. Kill it and restart */
+ nm_dns_plugin_child_kill (NM_DNS_PLUGIN (self));
+ priv->pid = 0;
+ }
+ }
+
+ if (!success) {
+ /* Spawn it */
+ priv->pid = start_bind (self);
+ if (priv->pid)
+ success = TRUE;
+ }
+
+out:
+ g_string_free (conf, TRUE);
+ return success;
+}
+
+/****************************************************************/
+
+static void
+child_quit (NMDnsPlugin *plugin, gint status)
+{
+ NMDnsBind *self = NM_DNS_BIND (plugin);
+ gboolean failed = TRUE;
+ int err;
+
+ if (WIFEXITED (status)) {
+ err = WEXITSTATUS (status);
+ if (err) {
+ nm_log_warn (LOGD_DNS, "named exited with error %d", err);
+ } else
+ failed = FALSE;
+ } else if (WIFSTOPPED (status)) {
+ nm_log_warn (LOGD_DNS, "named stopped unexpectedly with signal %d", WSTOPSIG (status));
+ } else if (WIFSIGNALED (status)) {
+ nm_log_warn (LOGD_DNS, "named died with signal %d", WTERMSIG (status));
+ } else {
+ nm_log_warn (LOGD_DNS, "named died from an unknown cause");
+ }
+ unlink (CONFFILE);
+
+ if (failed)
+ g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED);
+}
+
+/****************************************************************/
+
+static gboolean
+init (NMDnsPlugin *plugin)
+{
+ return TRUE;
+}
+
+static gboolean
+is_caching (NMDnsPlugin *plugin)
+{
+ return TRUE;
+}
+
+static const char *
+get_name (NMDnsPlugin *plugin)
+{
+ return "bind";
+}
+
+/****************************************************************/
+
+NMDnsBind *
+nm_dns_bind_new (void)
+{
+ return (NMDnsBind *) g_object_new (NM_TYPE_DNS_BIND, NULL);
+}
+
+static void
+nm_dns_bind_init (NMDnsBind *self)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+ unlink (CONFFILE);
+
+ G_OBJECT_CLASS (nm_dns_bind_parent_class)->dispose (object);
+}
+
+static void
+nm_dns_bind_class_init (NMDnsBindClass *dns_class)
+{
+ NMDnsPluginClass *plugin_class = NM_DNS_PLUGIN_CLASS (dns_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (dns_class);
+
+ g_type_class_add_private (dns_class, sizeof (NMDnsBindPrivate));
+
+ object_class->dispose = dispose;
+
+ plugin_class->init = init;
+ plugin_class->child_quit = child_quit;
+ plugin_class->is_caching = is_caching;
+ plugin_class->update = update;
+ plugin_class->get_name = get_name;
+}
+
diff --git a/src/dns-manager/nm-dns-bind.h b/src/dns-manager/nm-dns-bind.h
new file mode 100644
index 0000000000..7127265f18
--- /dev/null
+++ b/src/dns-manager/nm-dns-bind.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_DNS_BIND_H
+#define NM_DNS_BIND_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "nm-dns-plugin.h"
+
+#define NM_TYPE_DNS_BIND (nm_dns_bind_get_type ())
+#define NM_DNS_BIND(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DNS_BIND, NMDnsBind))
+#define NM_DNS_BIND_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DNS_BIND, NMDnsBindClass))
+#define NM_IS_DNS_BIND(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DNS_BIND))
+#define NM_IS_DNS_BIND_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DNS_BIND))
+#define NM_DNS_BIND_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DNS_BIND, NMDnsBindClass))
+
+typedef struct {
+ NMDnsPlugin parent;
+} NMDnsBind;
+
+typedef struct {
+ NMDnsPluginClass parent;
+} NMDnsBindClass;
+
+GType nm_dns_bind_get_type (void);
+
+NMDnsBind *nm_dns_bind_new (void);
+
+#endif /* NM_DNS_BIND_H */
+
diff --git a/src/dns-manager/nm-dns-dnsmasq.c b/src/dns-manager/nm-dns-dnsmasq.c
new file mode 100644
index 0000000000..9cc019780b
--- /dev/null
+++ b/src/dns-manager/nm-dns-dnsmasq.c
@@ -0,0 +1,385 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/*
+ * Copyright (C) 2010 Dan Williams <dcbw@redhat.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <arpa/inet.h>
+#include <sys/stat.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+
+#include "nm-dns-dnsmasq.h"
+#include "nm-logging.h"
+#include "nm-ip4-config.h"
+#include "nm-ip6-config.h"
+#include "nm-dns-utils.h"
+
+G_DEFINE_TYPE (NMDnsDnsmasq, nm_dns_dnsmasq, NM_TYPE_DNS_PLUGIN)
+
+#define NM_DNS_DNSMASQ_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasqPrivate))
+
+#define PIDFILE LOCALSTATEDIR "/run/nm-dns-dnsmasq.pid"
+#define CONFFILE LOCALSTATEDIR "/run/nm-dns-dnsmasq.conf"
+
+typedef struct {
+ guint32 foo;
+} NMDnsDnsmasqPrivate;
+
+/*******************************************/
+
+static inline const char *
+find_dnsmasq (void)
+{
+ static const char *paths[] = {
+ "/usr/local/sbin/dnsmasq",
+ "/usr/sbin/dnsmasq",
+ "/sbin/dnsmasq",
+ NULL
+ };
+ const char **binary = paths;
+
+ while (*binary != NULL) {
+ if (g_file_test (*binary, G_FILE_TEST_EXISTS))
+ return *binary;
+ binary++;
+ }
+ return NULL;
+}
+
+static gboolean
+add_ip4_config (GString *str, NMIP4Config *ip4, gboolean split)
+{
+ char buf[INET_ADDRSTRLEN + 1];
+ struct in_addr addr;
+ int n, i;
+ gboolean added = FALSE;
+
+ if (split) {
+ char **domains, **iter;
+
+ /* FIXME: it appears that dnsmasq can only handle one nameserver
+ * per domain (and the manpage says this too) so only use the first
+ * nameserver here.
+ */
+ addr.s_addr = nm_ip4_config_get_nameserver (ip4, 0);
+ memset (&buf[0], 0, sizeof (buf));
+ if (!inet_ntop (AF_INET, &addr, buf, sizeof (buf)))
+ return FALSE;
+
+ /* searches are preferred over domains */
+ n = nm_ip4_config_get_num_searches (ip4);
+ for (i = 0; i < n; i++) {
+ g_string_append_printf (str, "server=/%s/%s\n",
+ nm_ip4_config_get_search (ip4, i),
+ buf);
+ added = TRUE;
+ }
+
+ if (n == 0) {
+ /* If not searches, use any domains */
+ n = nm_ip4_config_get_num_domains (ip4);
+ for (i = 0; i < n; i++) {
+ g_string_append_printf (str, "server=/%s/%s\n",
+ nm_ip4_config_get_domain (ip4, i),
+ buf);
+ added = TRUE;
+ }
+ }
+
+ /* Ensure reverse-DNS works by directing queries for in-addr.arpa
+ * domains to the split domain's nameserver.
+ */
+ domains = nm_dns_utils_get_ip4_rdns_domains (ip4);
+ if (domains) {
+ for (iter = domains; iter && *iter; iter++)
+ g_string_append_printf (str, "server=/%s/%s\n", *iter, buf);
+ g_strfreev (domains);
+ added = TRUE;
+ }
+ }
+
+ /* If no searches or domains, just add the namservers */
+ if (!added) {
+ n = nm_ip4_config_get_num_nameservers (ip4);
+ for (i = 0; i < n; i++) {
+ memset (&buf[0], 0, sizeof (buf));
+ addr.s_addr = nm_ip4_config_get_nameserver (ip4, i);
+ if (inet_ntop (AF_INET, &addr, buf, sizeof (buf)))
+ g_string_append_printf (str, "server=%s\n", buf);
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+ip6_addr_to_string (const struct in6_addr *addr, char *buf, size_t buflen)
+{
+ memset (buf, 0, buflen);
+
+ /* inet_ntop is probably supposed to do this for us, but it doesn't */
+ if (IN6_IS_ADDR_V4MAPPED (addr))
+ return !!inet_ntop (AF_INET, &(addr->s6_addr32[3]), buf, buflen);
+
+ return !!inet_ntop (AF_INET6, addr, buf, buflen);
+}
+
+static gboolean
+add_ip6_config (GString *str, NMIP6Config *ip6, gboolean split)
+{
+ char buf[INET6_ADDRSTRLEN + 1];
+ const struct in6_addr *addr;
+ int n, i;
+ gboolean added = FALSE;
+
+ if (split) {
+ /* FIXME: it appears that dnsmasq can only handle one nameserver
+ * per domain (at the manpage seems to indicate that) so only use
+ * the first nameserver here.
+ */
+ addr = nm_ip6_config_get_nameserver (ip6, 0);
+ if (!ip6_addr_to_string (addr, &buf[0], sizeof (buf)))
+ return FALSE;
+
+ /* searches are preferred over domains */
+ n = nm_ip6_config_get_num_searches (ip6);
+ for (i = 0; i < n; i++) {
+ g_string_append_printf (str, "server=/%s/%s\n",
+ nm_ip6_config_get_search (ip6, i),
+ buf);
+ added = TRUE;
+ }
+
+ if (n == 0) {
+ /* If not searches, use any domains */
+ n = nm_ip6_config_get_num_domains (ip6);
+ for (i = 0; i < n; i++) {
+ g_string_append_printf (str, "server=/%s/%s\n",
+ nm_ip6_config_get_domain (ip6, i),
+ buf);
+ added = TRUE;
+ }
+ }
+ }
+
+ /* If no searches or domains, just add the namservers */
+ if (!added) {
+ n = nm_ip6_config_get_num_nameservers (ip6);
+ for (i = 0; i < n; i++) {
+ addr = nm_ip6_config_get_nameserver (ip6, i);
+ if (ip6_addr_to_string (addr, &buf[0], sizeof (buf)))
+ g_string_append_printf (str, "server=%s\n", buf);
+ }
+ }
+
+ return TRUE;
+}
+
+static gboolean
+update (NMDnsPlugin *plugin,
+ const GSList *vpn_configs,
+ const GSList *dev_configs,
+ const GSList *other_configs,
+ const char *hostname)
+{
+ NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin);
+ GString *conf;
+ GSList *iter;
+ const char *argv[10];
+ GError *error = NULL;
+ int ignored;
+ GPid pid = 0;
+
+ /* Kill the old dnsmasq; there doesn't appear to be a way to get dnsmasq
+ * to reread the config file using SIGHUP or similar. This is a small race
+ * here when restarting dnsmasq when DNS requests could go to the upstream
+ * servers instead of to dnsmasq.
+ */
+ nm_dns_plugin_child_kill (plugin);
+
+ /* Build up the new dnsmasq config file */
+ conf = g_string_sized_new (150);
+
+ /* Use split DNS for VPN configs */
+ for (iter = (GSList *) vpn_configs; iter; iter = g_slist_next (iter)) {
+ if (NM_IS_IP4_CONFIG (iter->data))
+ add_ip4_config (conf, NM_IP4_CONFIG (iter->data), TRUE);
+ else if (NM_IS_IP6_CONFIG (iter->data))
+ add_ip6_config (conf, NM_IP6_CONFIG (iter->data), TRUE);
+ }
+
+ /* Now add interface configs without split DNS */
+ for (iter = (GSList *) dev_configs; iter; iter = g_slist_next (iter)) {
+ if (NM_IS_IP4_CONFIG (iter->data))
+ add_ip4_config (conf, NM_IP4_CONFIG (iter->data), FALSE);
+ else if (NM_IS_IP6_CONFIG (iter->data))
+ add_ip6_config (conf, NM_IP6_CONFIG (iter->data), FALSE);
+ }
+
+ /* And any other random configs */
+ for (iter = (GSList *) other_configs; iter; iter = g_slist_next (iter)) {
+ if (NM_IS_IP4_CONFIG (iter->data))
+ add_ip4_config (conf, NM_IP4_CONFIG (iter->data), FALSE);
+ else if (NM_IS_IP6_CONFIG (iter->data))
+ add_ip6_config (conf, NM_IP6_CONFIG (iter->data), FALSE);
+ }
+
+ /* Write out the config file */
+ if (!g_file_set_contents (CONFFILE, conf->str, -1, &error)) {
+ nm_log_warn (LOGD_DNS, "Failed to write dnsmasq config file %s: (%d) %s",
+ CONFFILE,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ goto out;
+ }
+ ignored = chmod (CONFFILE, 0600);
+
+ nm_log_dbg (LOGD_DNS, "dnsmasq local caching DNS configuration:");
+ nm_log_dbg (LOGD_DNS, "%s", conf->str);
+
+ argv[0] = find_dnsmasq ();
+ argv[1] = "--no-resolv"; /* Use only commandline */
+ argv[2] = "--keep-in-foreground";
+ argv[3] = "--strict-order";
+ argv[4] = "--bind-interfaces";
+ argv[5] = "--pid-file=" PIDFILE;
+ argv[6] = "--listen-address=127.0.0.1"; /* Should work for both 4 and 6 */
+ argv[7] = "--conf-file=" CONFFILE;
+ argv[8] = NULL;
+
+ /* And finally spawn dnsmasq */
+ pid = nm_dns_plugin_child_spawn (NM_DNS_PLUGIN (self), argv, PIDFILE, "bin/dnsmasq");
+
+out:
+ g_string_free (conf, TRUE);
+ return pid ? TRUE : FALSE;
+}
+
+/****************************************************************/
+
+static const char *
+dm_exit_code_to_msg (int status)
+{
+ if (status == 1)
+ return "Configuration problem";
+ else if (status == 2)
+ return "Network access problem (address in use; permissions; etc)";
+ else if (status == 3)
+ return "Filesystem problem (missing file/directory; permissions; etc)";
+ else if (status == 4)
+ return "Memory allocation failure";
+ else if (status == 5)
+ return "Other problem";
+ else if (status >= 11)
+ return "Lease-script 'init' process failure";
+ return "Unknown error";
+}
+
+static void
+child_quit (NMDnsPlugin *plugin, gint status)
+{
+ NMDnsDnsmasq *self = NM_DNS_DNSMASQ (plugin);
+ gboolean failed = TRUE;
+ int err;
+
+ if (WIFEXITED (status)) {
+ err = WEXITSTATUS (status);
+ if (err) {
+ nm_log_warn (LOGD_DNS, "dnsmasq exited with error: %s (%d)",
+ dm_exit_code_to_msg (err),
+ err);
+ } else
+ failed = FALSE;
+ } else if (WIFSTOPPED (status)) {
+ nm_log_warn (LOGD_DNS, "dnsmasq stopped unexpectedly with signal %d", WSTOPSIG (status));
+ } else if (WIFSIGNALED (status)) {
+ nm_log_warn (LOGD_DNS, "dnsmasq died with signal %d", WTERMSIG (status));
+ } else {
+ nm_log_warn (LOGD_DNS, "dnsmasq died from an unknown cause");
+ }
+ unlink (CONFFILE);
+
+ if (failed)
+ g_signal_emit_by_name (self, NM_DNS_PLUGIN_FAILED);
+}
+
+/****************************************************************/
+
+static gboolean
+init (NMDnsPlugin *plugin)
+{
+ return TRUE;
+}
+
+static gboolean
+is_caching (NMDnsPlugin *plugin)
+{
+ return TRUE;
+}
+
+static const char *
+get_name (NMDnsPlugin *plugin)
+{
+ return "dnsmasq";
+}
+
+/****************************************************************/
+
+NMDnsDnsmasq *
+nm_dns_dnsmasq_new (void)
+{
+ return (NMDnsDnsmasq *) g_object_new (NM_TYPE_DNS_DNSMASQ, NULL);
+}
+
+static void
+nm_dns_dnsmasq_init (NMDnsDnsmasq *self)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+ unlink (CONFFILE);
+
+ G_OBJECT_CLASS (nm_dns_dnsmasq_parent_class)->dispose (object);
+}
+
+static void
+nm_dns_dnsmasq_class_init (NMDnsDnsmasqClass *dns_class)
+{
+ NMDnsPluginClass *plugin_class = NM_DNS_PLUGIN_CLASS (dns_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (dns_class);
+
+ g_type_class_add_private (dns_class, sizeof (NMDnsDnsmasqPrivate));
+
+ object_class->dispose = dispose;
+
+ plugin_class->init = init;
+ plugin_class->child_quit = child_quit;
+ plugin_class->is_caching = is_caching;
+ plugin_class->update = update;
+ plugin_class->get_name = get_name;
+}
+
diff --git a/src/dns-manager/nm-dns-dnsmasq.h b/src/dns-manager/nm-dns-dnsmasq.h
new file mode 100644
index 0000000000..6491b271ae
--- /dev/null
+++ b/src/dns-manager/nm-dns-dnsmasq.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_DNS_DNSMASQ_H
+#define NM_DNS_DNSMASQ_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#include "nm-dns-plugin.h"
+
+#define NM_TYPE_DNS_DNSMASQ (nm_dns_dnsmasq_get_type ())
+#define NM_DNS_DNSMASQ(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasq))
+#define NM_DNS_DNSMASQ_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasqClass))
+#define NM_IS_DNS_DNSMASQ(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DNS_DNSMASQ))
+#define NM_IS_DNS_DNSMASQ_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DNS_DNSMASQ))
+#define NM_DNS_DNSMASQ_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DNS_DNSMASQ, NMDnsDnsmasqClass))
+
+typedef struct {
+ NMDnsPlugin parent;
+} NMDnsDnsmasq;
+
+typedef struct {
+ NMDnsPluginClass parent;
+} NMDnsDnsmasqClass;
+
+GType nm_dns_dnsmasq_get_type (void);
+
+NMDnsDnsmasq *nm_dns_dnsmasq_new (void);
+
+#endif /* NM_DNS_DNSMASQ_H */
+
diff --git a/src/named-manager/nm-named-manager.c b/src/dns-manager/nm-dns-manager.c
index fc3b6e2c17..b0cdcc267d 100644
--- a/src/named-manager/nm-named-manager.c
+++ b/src/dns-manager/nm-dns-manager.c
@@ -36,13 +36,17 @@
#include <glib/gi18n.h>
-#include "nm-named-manager.h"
+#include "nm-dns-manager.h"
#include "nm-ip4-config.h"
#include "nm-ip6-config.h"
#include "nm-logging.h"
#include "nm-system.h"
#include "NetworkManagerUtils.h"
+#include "nm-dns-plugin.h"
+#include "nm-dns-dnsmasq.h"
+#include "nm-dns-bind.h"
+
#ifdef HAVE_SELINUX
#include <selinux/selinux.h>
#endif
@@ -51,53 +55,44 @@
#define RESOLV_CONF "/etc/resolv.conf"
#endif
-#define ADDR_BUF_LEN 50
-
-G_DEFINE_TYPE(NMNamedManager, nm_named_manager, G_TYPE_OBJECT)
-
-#define NM_NAMED_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
- NM_TYPE_NAMED_MANAGER, \
- NMNamedManagerPrivate))
+G_DEFINE_TYPE(NMDnsManager, nm_dns_manager, G_TYPE_OBJECT)
+#define NM_DNS_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
+ NM_TYPE_DNS_MANAGER, \
+ NMDnsManagerPrivate))
-struct NMNamedManagerPrivate {
+struct NMDnsManagerPrivate {
NMIP4Config *ip4_vpn_config;
NMIP4Config *ip4_device_config;
NMIP6Config *ip6_vpn_config;
NMIP6Config *ip6_device_config;
GSList *configs;
-};
-
-
-NMNamedManager *
-nm_named_manager_get (void)
-{
- static NMNamedManager * singleton = NULL;
-
- if (!singleton)
- singleton = NM_NAMED_MANAGER (g_object_new (NM_TYPE_NAMED_MANAGER, NULL));
- else
- g_object_ref (singleton);
+ char *hostname;
- g_assert (singleton);
- return singleton;
-}
+ /* poor man's hash; we assume that the IP4 config object won't change
+ * after it's given to us, which is (at this time) a fair assumption. So
+ * we track the order of the currently applied IP configs and if they
+ * haven't changed we don't need to rewrite resolv.conf.
+ */
+ #define HLEN 6
+ gpointer hash[HLEN];
+ GSList *plugins;
-GQuark
-nm_named_manager_error_quark (void)
-{
- static GQuark quark = 0;
- if (!quark)
- quark = g_quark_from_static_string ("nm_named_manager_error");
+ /* This is a hack because SUSE's netconfig always wants changes
+ * associated with a network interface, but sometimes a change isn't
+ * associated with a network interface (like hostnames).
+ */
+ char *last_iface;
+};
- return quark;
-}
typedef struct {
GPtrArray *nameservers;
const char *domain;
GPtrArray *searches;
+ const char *nis_domain;
+ GPtrArray *nis_servers;
} NMResolvConfData;
static void
@@ -148,6 +143,23 @@ merge_one_ip4_config (NMResolvConfData *rc, NMIP4Config *src)
num = nm_ip4_config_get_num_searches (src);
for (i = 0; i < num; i++)
add_string_item (rc->searches, nm_ip4_config_get_search (src, i));
+
+ /* NIS stuff */
+ num = nm_ip4_config_get_num_nis_servers (src);
+ for (i = 0; i < num; i++) {
+ struct in_addr addr;
+ char buf[INET_ADDRSTRLEN];
+
+ addr.s_addr = nm_ip4_config_get_nis_server (src, i);
+ if (inet_ntop (AF_INET, &addr, buf, INET_ADDRSTRLEN) > 0)
+ add_string_item (rc->nis_servers, buf);
+ }
+
+ if (nm_ip4_config_get_nis_domain (src)) {
+ /* FIXME: handle multiple domains */
+ if (!rc->nis_domain)
+ rc->nis_domain = nm_ip4_config_get_nis_domain (src);
+ }
}
static void
@@ -239,10 +251,12 @@ static gboolean
dispatch_netconfig (const char *domain,
char **searches,
char **nameservers,
+ const char *nis_domain,
+ char **nis_servers,
const char *iface,
GError **error)
{
- char *str;
+ char *str, *tmp;
GPid pid;
gint fd;
int ret;
@@ -256,7 +270,7 @@ dispatch_netconfig (const char *domain,
// resolv.conf. Assuming netconfig works in the obvious way, then
// there are various failure modes, such as, eg, bringing up a VPN on
// eth0, then bringing up wlan0, then bringing down the VPN. Because
- // NMNamedManager would have claimed that the VPN DNS server was also
+ // NMDnsManager would have claimed that the VPN DNS server was also
// part of the wlan0 config, it will remain in resolv.conf after the
// VPN goes down, even though it is presumably no longer reachable
// at that point.
@@ -266,8 +280,6 @@ dispatch_netconfig (const char *domain,
str = g_strjoinv (" ", searches);
if (domain) {
- char *tmp;
-
tmp = g_strconcat (domain, " ", str, NULL);
g_free (str);
str = tmp;
@@ -283,6 +295,15 @@ dispatch_netconfig (const char *domain,
g_free (str);
}
+ if (nis_domain)
+ write_to_netconfig (fd, "NISDOMAIN", nis_domain);
+
+ if (nis_servers) {
+ str = g_strjoinv (" ", nis_servers);
+ write_to_netconfig (fd, "NISSERVERS", str);
+ g_free (str);
+ }
+
close (fd);
/* Wait until the process exits */
@@ -292,6 +313,10 @@ dispatch_netconfig (const char *domain,
ret = waitpid (pid, NULL, 0);
if (ret < 0 && errno == EINTR)
goto again;
+ else if (ret < 0 && errno == ECHILD) {
+ /* When the netconfig exist, the errno is ECHILD, it should return TRUE */
+ return TRUE;
+ }
return ret > 0;
}
@@ -309,13 +334,14 @@ write_resolv_conf (FILE *f, const char *domain,
char *nameservers_str = NULL;
int i;
gboolean retval = FALSE;
+ GString *str;
if (fprintf (f, "%s","# Generated by NetworkManager\n") < 0) {
g_set_error (error,
- NM_NAMED_MANAGER_ERROR,
- NM_NAMED_MANAGER_ERROR_SYSTEM,
- "Could not write " RESOLV_CONF ": %s\n",
- g_strerror (errno));
+ NM_DNS_MANAGER_ERROR,
+ NM_DNS_MANAGER_ERROR_SYSTEM,
+ "Could not write " RESOLV_CONF ": %s\n",
+ g_strerror (errno));
return FALSE;
}
@@ -330,12 +356,10 @@ write_resolv_conf (FILE *f, const char *domain,
g_free (tmp_str);
}
- if (nameservers) {
- GString *str;
- int num;
+ str = g_string_new ("");
- str = g_string_new ("");
- num = g_strv_length (nameservers);
+ if (nameservers) {
+ int num = g_strv_length (nameservers);
for (i = 0; i < num; i++) {
if (i == 3) {
@@ -350,14 +374,14 @@ write_resolv_conf (FILE *f, const char *domain,
g_string_append (str, nameservers[i]);
g_string_append_c (str, '\n');
}
-
- nameservers_str = g_string_free (str, FALSE);
}
+ nameservers_str = g_string_free (str, FALSE);
+
if (fprintf (f, "%s%s%s",
domain_str ? domain_str : "",
searches_str ? searches_str : "",
- nameservers_str ? nameservers_str : "") != -1)
+ strlen (nameservers_str) ? nameservers_str : "") != -1)
retval = TRUE;
g_free (domain_str);
@@ -387,11 +411,11 @@ dispatch_resolvconf (const char *domain,
nm_log_info (LOGD_DNS, "(%s): writing resolv.conf to %s", iface, RESOLVCONF_PATH);
if ((f = popen (cmd, "w")) == NULL)
g_set_error (error,
- NM_NAMED_MANAGER_ERROR,
- NM_NAMED_MANAGER_ERROR_SYSTEM,
- "Could not write to %s: %s\n",
- RESOLVCONF_PATH,
- g_strerror (errno));
+ NM_DNS_MANAGER_ERROR,
+ NM_DNS_MANAGER_ERROR_SYSTEM,
+ "Could not write to %s: %s\n",
+ RESOLVCONF_PATH,
+ g_strerror (errno));
else {
retval = write_resolv_conf (f, domain, searches, nameservers, error);
retval &= (pclose (f) == 0);
@@ -445,8 +469,8 @@ update_resolv_conf (const char *domain,
old_errno = errno;
if ((f = fopen (RESOLV_CONF, "w")) == NULL) {
g_set_error (error,
- NM_NAMED_MANAGER_ERROR,
- NM_NAMED_MANAGER_ERROR_SYSTEM,
+ NM_DNS_MANAGER_ERROR,
+ NM_DNS_MANAGER_ERROR_SYSTEM,
"Could not open %s: %s\nCould not open %s: %s\n",
tmp_resolv_conf_realpath,
g_strerror (old_errno),
@@ -468,8 +492,8 @@ update_resolv_conf (const char *domain,
* since its error is more important.
*/
g_set_error (error,
- NM_NAMED_MANAGER_ERROR,
- NM_NAMED_MANAGER_ERROR_SYSTEM,
+ NM_DNS_MANAGER_ERROR,
+ NM_DNS_MANAGER_ERROR_SYSTEM,
"Could not close %s: %s\n",
tmp_resolv_conf_realpath,
g_strerror (errno));
@@ -482,8 +506,8 @@ update_resolv_conf (const char *domain,
if (*error == NULL && do_rename) {
if (rename (tmp_resolv_conf_realpath, resolv_conf_realpath) < 0) {
g_set_error (error,
- NM_NAMED_MANAGER_ERROR,
- NM_NAMED_MANAGER_ERROR_SYSTEM,
+ NM_DNS_MANAGER_ERROR,
+ NM_DNS_MANAGER_ERROR_SYSTEM,
"Could not replace " RESOLV_CONF ": %s\n",
g_strerror (errno));
}
@@ -495,26 +519,72 @@ out:
return *error ? FALSE : TRUE;
}
+static void
+compute_hash (NMDnsManager *self, gpointer *hash)
+{
+ NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
+ gpointer check[HLEN];
+ GSList *iter;
+ int i = 0;
+
+ memset (check, 0, sizeof (check));
+
+ if (priv->ip4_vpn_config)
+ check[i++] = priv->ip4_vpn_config;
+ if (priv->ip4_device_config)
+ check[i++] = priv->ip4_device_config;
+
+ if (priv->ip6_vpn_config)
+ check[i++] = priv->ip6_vpn_config;
+ if (priv->ip6_device_config)
+ check[i++] = priv->ip6_device_config;
+
+ /* Add two more "other" configs if any exist */
+ for (iter = priv->configs; iter && i < HLEN; iter = g_slist_next (iter)) {
+ if ( (iter->data != priv->ip4_vpn_config)
+ && (iter->data != priv->ip4_device_config)
+ && (iter->data != priv->ip6_vpn_config)
+ && (iter->data != priv->ip6_device_config))
+ check[i++] = iter->data;
+ }
+ memcpy (hash, check, sizeof (check));
+}
+
static gboolean
-rewrite_resolv_conf (NMNamedManager *mgr, const char *iface, GError **error)
+update_dns (NMDnsManager *self,
+ const char *iface,
+ gboolean no_caching,
+ GError **error)
{
- NMNamedManagerPrivate *priv;
+ NMDnsManagerPrivate *priv;
NMResolvConfData rc;
- GSList *iter;
+ GSList *iter, *vpn_configs = NULL, *dev_configs = NULL, *other_configs = NULL;
const char *domain = NULL;
+ const char *nis_domain = NULL;
char **searches = NULL;
char **nameservers = NULL;
+ char **nis_servers = NULL;
int num, i, len;
- gboolean success = FALSE;
+ gboolean success = FALSE, caching = FALSE;
g_return_val_if_fail (error != NULL, FALSE);
g_return_val_if_fail (*error == NULL, FALSE);
- priv = NM_NAMED_MANAGER_GET_PRIVATE (mgr);
+ priv = NM_DNS_MANAGER_GET_PRIVATE (self);
+
+ if (iface && (iface != priv->last_iface)) {
+ g_free (priv->last_iface);
+ priv->last_iface = g_strdup (iface);
+ }
+
+ /* Update hash with config we're applying */
+ compute_hash (self, priv->hash);
rc.nameservers = g_ptr_array_new ();
rc.domain = NULL;
rc.searches = g_ptr_array_new ();
+ rc.nis_domain = NULL;
+ rc.nis_servers = g_ptr_array_new ();
if (priv->ip4_vpn_config)
merge_one_ip4_config (&rc, priv->ip4_vpn_config);
@@ -545,6 +615,20 @@ rewrite_resolv_conf (NMNamedManager *mgr, const char *iface, GError **error)
g_assert_not_reached ();
}
+ /* Add the current domain name (from the hostname) to the searches list;
+ * see rh #600407. The bug report is that when the hostname is set to
+ * something like 'dcbw.foobar.com' (ie an FQDN) that pinging 'dcbw' doesn't
+ * work because the resolver doesn't have anything to append to 'dcbw' when
+ * looking it up.
+ */
+ if (priv->hostname) {
+ const char *hostsearch = strchr (priv->hostname, '.');
+
+ /* +1 to get rid of the dot */
+ if (hostsearch && strlen (hostsearch + 1))
+ add_string_item (rc.searches, hostsearch + 1);
+ }
+
domain = rc.domain;
/* Per 'man resolv.conf', the search list is limited to 6 domains
@@ -569,13 +653,89 @@ rewrite_resolv_conf (NMNamedManager *mgr, const char *iface, GError **error)
} else
g_ptr_array_free (rc.nameservers, TRUE);
+ if (rc.nis_servers->len) {
+ g_ptr_array_add (rc.nis_servers, NULL);
+ nis_servers = (char **) g_ptr_array_free (rc.nis_servers, FALSE);
+ } else
+ g_ptr_array_free (rc.nis_servers, TRUE);
+
+ nis_domain = rc.nis_domain;
+
+ /* Build up config lists for plugins; we use the raw configs here, not the
+ * merged information that we write to resolv.conf so that the plugins can
+ * still use the domain information in each config to provide split DNS if
+ * they want to.
+ */
+ if (priv->ip4_vpn_config)
+ vpn_configs = g_slist_append (vpn_configs, priv->ip4_vpn_config);
+ if (priv->ip6_vpn_config)
+ vpn_configs = g_slist_append (vpn_configs, priv->ip6_vpn_config);
+ if (priv->ip4_device_config)
+ dev_configs = g_slist_append (dev_configs, priv->ip4_device_config);
+ if (priv->ip6_device_config)
+ dev_configs = g_slist_append (dev_configs, priv->ip6_device_config);
+
+ for (iter = priv->configs; iter; iter = g_slist_next (iter)) {
+ if ( (iter->data != priv->ip4_vpn_config)
+ && (iter->data != priv->ip4_device_config)
+ && (iter->data != priv->ip6_vpn_config)
+ && (iter->data != priv->ip6_device_config))
+ other_configs = g_slist_append (other_configs, iter->data);
+ }
+
+ /* Let any plugins do their thing first */
+ for (iter = priv->plugins; iter; iter = g_slist_next (iter)) {
+ NMDnsPlugin *plugin = NM_DNS_PLUGIN (iter->data);
+ const char *plugin_name = nm_dns_plugin_get_name (plugin);
+
+ if (nm_dns_plugin_is_caching (plugin)) {
+ if (no_caching) {
+ nm_log_dbg (LOGD_DNS, "DNS: plugin %s ignored (caching disabled)",
+ plugin_name);
+ continue;
+ }
+ caching = TRUE;
+ }
+
+ nm_log_dbg (LOGD_DNS, "DNS: updating plugin %s", plugin_name);
+ if (!nm_dns_plugin_update (plugin,
+ vpn_configs,
+ dev_configs,
+ other_configs,
+ priv->hostname)) {
+ nm_log_warn (LOGD_DNS, "DNS: plugin %s update failed", plugin_name);
+
+ /* If the plugin failed to update, we shouldn't write out a local
+ * caching DNS configuration to resolv.conf.
+ */
+ caching = FALSE;
+ }
+ }
+ g_slist_free (vpn_configs);
+ g_slist_free (dev_configs);
+ g_slist_free (other_configs);
+
+ /* If caching was successful, we only send 127.0.0.1 to /etc/resolv.conf
+ * to ensure that the glibc resolver doesn't try to round-robin nameservers,
+ * but only uses the local caching nameserver.
+ */
+ if (caching) {
+ if (nameservers)
+ g_strfreev (nameservers);
+ nameservers = g_new0 (char*, 2);
+ nameservers[0] = g_strdup ("127.0.0.1");
+ }
+
#ifdef RESOLVCONF_PATH
success = dispatch_resolvconf (domain, searches, nameservers, iface, error);
#endif
#ifdef TARGET_SUSE
- if (success == FALSE)
- success = dispatch_netconfig (domain, searches, nameservers, iface, error);
+ if (success == FALSE) {
+ success = dispatch_netconfig (domain, searches, nameservers,
+ nis_domain, nis_servers,
+ iface, error);
+ }
#endif
if (success == FALSE)
@@ -588,30 +748,69 @@ rewrite_resolv_conf (NMNamedManager *mgr, const char *iface, GError **error)
g_strfreev (searches);
if (nameservers)
g_strfreev (nameservers);
+ if (nis_servers)
+ g_strfreev (nis_servers);
return success;
}
+static void
+plugin_failed (NMDnsPlugin *plugin, gpointer user_data)
+{
+ NMDnsManager *self = NM_DNS_MANAGER (user_data);
+ NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
+ GError *error = NULL;
+
+ /* Errors with non-caching plugins aren't fatal */
+ if (!nm_dns_plugin_is_caching (plugin))
+ return;
+
+ /* Disable caching until the next DNS update */
+ if (!update_dns (self, priv->last_iface, TRUE, &error)) {
+ nm_log_warn (LOGD_DNS, "could not commit DNS changes: (%d) %s",
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ }
+}
+
+static gboolean
+config_changed (NMDnsManager *self)
+{
+ NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
+ gpointer check[HLEN];
+
+ /* We only store HLEN configs; so if there are actually more than that,
+ * we have to assume that the config has changed.
+ */
+ if (g_slist_length (priv->configs) > HLEN)
+ return TRUE;
+
+ /* Otherwise return TRUE if the configuration has changed */
+ compute_hash (self, check);
+ return memcmp (check, priv->hash, sizeof (check)) ? TRUE : FALSE;
+}
+
gboolean
-nm_named_manager_add_ip4_config (NMNamedManager *mgr,
- const char *iface,
- NMIP4Config *config,
- NMNamedIPConfigType cfg_type)
+nm_dns_manager_add_ip4_config (NMDnsManager *mgr,
+ const char *iface,
+ NMIP4Config *config,
+ NMDnsIPConfigType cfg_type)
{
- NMNamedManagerPrivate *priv;
+ NMDnsManagerPrivate *priv;
GError *error = NULL;
g_return_val_if_fail (mgr != NULL, FALSE);
g_return_val_if_fail (iface != NULL, FALSE);
g_return_val_if_fail (config != NULL, FALSE);
- priv = NM_NAMED_MANAGER_GET_PRIVATE (mgr);
+ priv = NM_DNS_MANAGER_GET_PRIVATE (mgr);
switch (cfg_type) {
- case NM_NAMED_IP_CONFIG_TYPE_VPN:
+ case NM_DNS_IP_CONFIG_TYPE_VPN:
priv->ip4_vpn_config = config;
break;
- case NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE:
+ case NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE:
priv->ip4_device_config = config;
break;
default:
@@ -622,27 +821,32 @@ nm_named_manager_add_ip4_config (NMNamedManager *mgr,
if (!g_slist_find (priv->configs, config))
priv->configs = g_slist_append (priv->configs, g_object_ref (config));
- if (!rewrite_resolv_conf (mgr, iface, &error)) {
- nm_log_warn (LOGD_DNS, "could not commit DNS changes: '%s'", error ? error->message : "(none)");
- g_error_free (error);
+ if (!config_changed (mgr))
+ return TRUE;
+
+ if (!update_dns (mgr, iface, FALSE, &error)) {
+ nm_log_warn (LOGD_DNS, "could not commit DNS changes: (%d) %s",
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
}
return TRUE;
}
gboolean
-nm_named_manager_remove_ip4_config (NMNamedManager *mgr,
- const char *iface,
- NMIP4Config *config)
+nm_dns_manager_remove_ip4_config (NMDnsManager *mgr,
+ const char *iface,
+ NMIP4Config *config)
{
- NMNamedManagerPrivate *priv;
+ NMDnsManagerPrivate *priv;
GError *error = NULL;
g_return_val_if_fail (mgr != NULL, FALSE);
g_return_val_if_fail (iface != NULL, FALSE);
g_return_val_if_fail (config != NULL, FALSE);
- priv = NM_NAMED_MANAGER_GET_PRIVATE (mgr);
+ priv = NM_DNS_MANAGER_GET_PRIVATE (mgr);
/* Can't remove it if it wasn't in the list to begin with */
if (!g_slist_find (priv->configs, config))
@@ -657,37 +861,41 @@ nm_named_manager_remove_ip4_config (NMNamedManager *mgr,
g_object_unref (config);
- if (!rewrite_resolv_conf (mgr, iface, &error)) {
- nm_log_warn (LOGD_DNS, "could not commit DNS changes: '%s'", error ? error->message : "(none)");
- if (error)
- g_error_free (error);
+ if (config_changed (mgr))
+ return TRUE;
+
+ if (!update_dns (mgr, iface, FALSE, &error)) {
+ nm_log_warn (LOGD_DNS, "could not commit DNS changes: (%d) %s",
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
}
return TRUE;
}
gboolean
-nm_named_manager_add_ip6_config (NMNamedManager *mgr,
- const char *iface,
- NMIP6Config *config,
- NMNamedIPConfigType cfg_type)
+nm_dns_manager_add_ip6_config (NMDnsManager *mgr,
+ const char *iface,
+ NMIP6Config *config,
+ NMDnsIPConfigType cfg_type)
{
- NMNamedManagerPrivate *priv;
+ NMDnsManagerPrivate *priv;
GError *error = NULL;
g_return_val_if_fail (mgr != NULL, FALSE);
g_return_val_if_fail (iface != NULL, FALSE);
g_return_val_if_fail (config != NULL, FALSE);
- priv = NM_NAMED_MANAGER_GET_PRIVATE (mgr);
+ priv = NM_DNS_MANAGER_GET_PRIVATE (mgr);
switch (cfg_type) {
- case NM_NAMED_IP_CONFIG_TYPE_VPN:
+ case NM_DNS_IP_CONFIG_TYPE_VPN:
/* FIXME: not quite yet... */
- g_return_val_if_fail (cfg_type != NM_NAMED_IP_CONFIG_TYPE_VPN, FALSE);
+ g_return_val_if_fail (cfg_type != NM_DNS_IP_CONFIG_TYPE_VPN, FALSE);
priv->ip6_vpn_config = config;
break;
- case NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE:
+ case NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE:
priv->ip6_device_config = config;
break;
default:
@@ -698,27 +906,32 @@ nm_named_manager_add_ip6_config (NMNamedManager *mgr,
if (!g_slist_find (priv->configs, config))
priv->configs = g_slist_append (priv->configs, g_object_ref (config));
- if (!rewrite_resolv_conf (mgr, iface, &error)) {
- nm_log_warn (LOGD_DNS, "could not commit DNS changes: '%s'", error ? error->message : "(none)");
- g_error_free (error);
+ if (config_changed (mgr))
+ return TRUE;
+
+ if (!update_dns (mgr, iface, FALSE, &error)) {
+ nm_log_warn (LOGD_DNS, "could not commit DNS changes: (%d) %s",
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
}
return TRUE;
}
gboolean
-nm_named_manager_remove_ip6_config (NMNamedManager *mgr,
- const char *iface,
- NMIP6Config *config)
+nm_dns_manager_remove_ip6_config (NMDnsManager *mgr,
+ const char *iface,
+ NMIP6Config *config)
{
- NMNamedManagerPrivate *priv;
+ NMDnsManagerPrivate *priv;
GError *error = NULL;
g_return_val_if_fail (mgr != NULL, FALSE);
g_return_val_if_fail (iface != NULL, FALSE);
g_return_val_if_fail (config != NULL, FALSE);
- priv = NM_NAMED_MANAGER_GET_PRIVATE (mgr);
+ priv = NM_DNS_MANAGER_GET_PRIVATE (mgr);
/* Can't remove it if it wasn't in the list to begin with */
if (!g_slist_find (priv->configs, config))
@@ -733,39 +946,156 @@ nm_named_manager_remove_ip6_config (NMNamedManager *mgr,
g_object_unref (config);
- if (!rewrite_resolv_conf (mgr, iface, &error)) {
- nm_log_warn (LOGD_DNS, "could not commit DNS changes: '%s'", error ? error->message : "(none)");
- if (error)
- g_error_free (error);
+ if (config_changed (mgr))
+ return TRUE;
+
+ if (!update_dns (mgr, iface, FALSE, &error)) {
+ nm_log_warn (LOGD_DNS, "could not commit DNS changes: (%d) %s",
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
}
return TRUE;
}
+void
+nm_dns_manager_set_hostname (NMDnsManager *mgr,
+ const char *hostname)
+{
+ NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (mgr);
+ GError *error = NULL;
+ const char *filtered = NULL;
+
+ /* Certain hostnames we don't want to include in resolv.conf 'searches' */
+ if ( hostname
+ && strcmp (hostname, "localhost.localdomain")
+ && strcmp (hostname, "localhost6.localdomain6")
+ && !strstr (hostname, ".in-addr.arpa")
+ && strchr (hostname, '.')) {
+ filtered = hostname;
+ }
+
+ if ( (!priv->hostname && !filtered)
+ || (priv->hostname && filtered && !strcmp (priv->hostname, filtered)))
+ return;
+
+ g_free (priv->hostname);
+ priv->hostname = g_strdup (filtered);
+
+ /* Passing the last interface here is completely bogus, but SUSE's netconfig
+ * wants one. But hostname changes are system-wide and *not* tied to a
+ * specific interface, so netconfig can't really handle this. Fake it.
+ */
+ if (!update_dns (mgr, priv->last_iface, FALSE, &error)) {
+ nm_log_warn (LOGD_DNS, "could not commit DNS changes: (%d) %s",
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ }
+}
+
+static void
+load_plugins (NMDnsManager *self, const char **plugins)
+{
+ NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (self);
+ NMDnsPlugin *plugin;
+ const char **iter;
+ gboolean have_caching = FALSE;
+
+ if (plugins && *plugins) {
+ /* Create each configured plugin */
+ for (iter = plugins; iter && *iter; iter++) {
+ if (!strcasecmp (*iter, "dnsmasq"))
+ plugin = NM_DNS_PLUGIN (nm_dns_dnsmasq_new ());
+ else if (!strcasecmp (*iter, "bind")) {
+ plugin = NM_DNS_PLUGIN (nm_dns_bind_new ());
+ nm_log_warn (LOGD_DNS, "The BIND plugin is experimental!");
+ } else {
+ nm_log_warn (LOGD_DNS, "Unknown DNS plugin '%s'", *iter);\
+ continue;
+ }
+ g_assert (plugin);
+
+ /* Only one caching DNS plugin is allowed */
+ if (nm_dns_plugin_is_caching (plugin)) {
+ if (have_caching) {
+ nm_log_warn (LOGD_DNS,
+ "Ignoring plugin %s; only one caching DNS "
+ "plugin is allowed.",
+ *iter);
+ g_object_unref (plugin);
+ continue;
+ }
+ have_caching = TRUE;
+ }
+
+ nm_log_info (LOGD_DNS, "DNS: loaded plugin %s", nm_dns_plugin_get_name (plugin));
+ priv->plugins = g_slist_append (priv->plugins, plugin);
+ g_signal_connect (plugin, NM_DNS_PLUGIN_FAILED,
+ G_CALLBACK (plugin_failed),
+ self);
+ }
+ } else {
+ /* Create default plugins */
+ }
+}
+
+/******************************************************************/
+
+NMDnsManager *
+nm_dns_manager_get (const char **plugins)
+{
+ static NMDnsManager * singleton = NULL;
+
+ if (!singleton) {
+ singleton = NM_DNS_MANAGER (g_object_new (NM_TYPE_DNS_MANAGER, NULL));
+ g_assert (singleton);
+ load_plugins (singleton, plugins);
+ } else
+ g_object_ref (singleton);
+
+ return singleton;
+}
+
+GQuark
+nm_dns_manager_error_quark (void)
+{
+ static GQuark quark = 0;
+ if (!quark)
+ quark = g_quark_from_static_string ("nm_dns_manager_error");
+
+ return quark;
+}
static void
-nm_named_manager_init (NMNamedManager *mgr)
+nm_dns_manager_init (NMDnsManager *mgr)
{
}
static void
-nm_named_manager_finalize (GObject *object)
+nm_dns_manager_finalize (GObject *object)
{
- NMNamedManagerPrivate *priv = NM_NAMED_MANAGER_GET_PRIVATE (object);
+ NMDnsManagerPrivate *priv = NM_DNS_MANAGER_GET_PRIVATE (object);
g_slist_foreach (priv->configs, (GFunc) g_object_unref, NULL);
g_slist_free (priv->configs);
+ g_free (priv->hostname);
+ g_free (priv->last_iface);
+
+ g_slist_foreach (priv->plugins, (GFunc) g_object_unref, NULL);
+ g_slist_free (priv->plugins);
- G_OBJECT_CLASS (nm_named_manager_parent_class)->finalize (object);
+ G_OBJECT_CLASS (nm_dns_manager_parent_class)->finalize (object);
}
static void
-nm_named_manager_class_init (NMNamedManagerClass *klass)
+nm_dns_manager_class_init (NMDnsManagerClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
- object_class->finalize = nm_named_manager_finalize;
+ object_class->finalize = nm_dns_manager_finalize;
- g_type_class_add_private (object_class, sizeof (NMNamedManagerPrivate));
+ g_type_class_add_private (object_class, sizeof (NMDnsManagerPrivate));
}
diff --git a/src/dns-manager/nm-dns-manager.h b/src/dns-manager/nm-dns-manager.h
new file mode 100644
index 0000000000..eb1c73a7c2
--- /dev/null
+++ b/src/dns-manager/nm-dns-manager.h
@@ -0,0 +1,95 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2004 - 2005 Colin Walters <walters@redhat.com>
+ * Copyright (C) 2004 - 2010 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
+ * and others
+ */
+
+#ifndef NM_DNS_MANAGER_H
+#define NM_DNS_MANAGER_H
+
+#include "config.h"
+#include <glib-object.h>
+#include <dbus/dbus.h>
+#include "nm-ip4-config.h"
+#include "nm-ip6-config.h"
+
+typedef enum {
+ NM_DNS_MANAGER_ERROR_SYSTEM,
+ NM_DNS_MANAGER_ERROR_INVALID_NAMESERVER,
+ NM_DNS_MANAGER_ERROR_INVALID_HOST,
+ NM_DNS_MANAGER_ERROR_INVALID_ID
+} NMDnsManagerError;
+
+typedef enum {
+ NM_DNS_IP_CONFIG_TYPE_DEFAULT = 0,
+ NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE,
+ NM_DNS_IP_CONFIG_TYPE_VPN
+} NMDnsIPConfigType;
+
+#define NM_DNS_MANAGER_ERROR nm_dns_manager_error_quark ()
+GQuark nm_dns_manager_error_quark (void);
+
+G_BEGIN_DECLS
+
+#define NM_TYPE_DNS_MANAGER (nm_dns_manager_get_type ())
+#define NM_DNS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_DNS_MANAGER, NMDnsManager))
+#define NM_DNS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_DNS_MANAGER, NMDnsManagerClass))
+#define NM_IS_DNS_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NM_TYPE_DNS_MANAGER))
+#define NM_IS_DNS_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NM_TYPE_DNS_MANAGER))
+#define NM_DNS_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NM_TYPE_DNS_MANAGER, NMDnsManagerClass))
+
+typedef struct NMDnsManagerPrivate NMDnsManagerPrivate;
+
+typedef struct {
+ GObject parent;
+} NMDnsManager;
+
+typedef struct {
+ GObjectClass parent;
+} NMDnsManagerClass;
+
+GType nm_dns_manager_get_type (void);
+
+NMDnsManager * nm_dns_manager_get (const char **plugins);
+
+gboolean nm_dns_manager_add_ip4_config (NMDnsManager *mgr,
+ const char *iface,
+ NMIP4Config *config,
+ NMDnsIPConfigType cfg_type);
+
+gboolean nm_dns_manager_remove_ip4_config (NMDnsManager *mgr,
+ const char *iface,
+ NMIP4Config *config);
+
+gboolean nm_dns_manager_add_ip6_config (NMDnsManager *mgr,
+ const char *iface,
+ NMIP6Config *config,
+ NMDnsIPConfigType cfg_type);
+
+gboolean nm_dns_manager_remove_ip6_config (NMDnsManager *mgr,
+ const char *iface,
+ NMIP6Config *config);
+
+void nm_dns_manager_set_hostname (NMDnsManager *mgr,
+ const char *hostname);
+
+G_END_DECLS
+
+#endif /* NM_DNS_MANAGER_H */
diff --git a/src/dns-manager/nm-dns-plugin.c b/src/dns-manager/nm-dns-plugin.c
new file mode 100644
index 0000000000..f7d65a529b
--- /dev/null
+++ b/src/dns-manager/nm-dns-plugin.c
@@ -0,0 +1,319 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ */
+
+#include <config.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <glib.h>
+
+#include "nm-dns-plugin.h"
+#include "nm-logging.h"
+
+typedef struct {
+ gboolean disposed;
+
+ GPid pid;
+ guint32 watch_id;
+ char *progname;
+ char *pidfile;
+} NMDnsPluginPrivate;
+
+#define NM_DNS_PLUGIN_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DNS_PLUGIN, NMDnsPluginPrivate))
+
+G_DEFINE_TYPE_EXTENDED (NMDnsPlugin, nm_dns_plugin, G_TYPE_OBJECT, G_TYPE_FLAG_ABSTRACT, {})
+
+enum {
+ FAILED,
+ CHILD_QUIT,
+ LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/********************************************/
+
+gboolean
+nm_dns_plugin_update (NMDnsPlugin *self,
+ const GSList *vpn_configs,
+ const GSList *dev_configs,
+ const GSList *other_configs,
+ const char *hostname)
+{
+ g_return_val_if_fail (NM_DNS_PLUGIN_GET_CLASS (self)->update != NULL, FALSE);
+
+ return NM_DNS_PLUGIN_GET_CLASS (self)->update (self,
+ vpn_configs,
+ dev_configs,
+ other_configs,
+ hostname);
+}
+
+static gboolean
+is_caching (NMDnsPlugin *self)
+{
+ return FALSE;
+}
+
+gboolean
+nm_dns_plugin_is_caching (NMDnsPlugin *self)
+{
+ return NM_DNS_PLUGIN_GET_CLASS (self)->is_caching (self);
+}
+
+const char *
+nm_dns_plugin_get_name (NMDnsPlugin *self)
+{
+ g_assert (NM_DNS_PLUGIN_GET_CLASS (self)->get_name);
+ return NM_DNS_PLUGIN_GET_CLASS (self)->get_name (self);
+}
+
+/********************************************/
+
+static void
+kill_existing (const char *progname, const char *pidfile, const char *kill_match)
+{
+ char *contents = NULL;
+ glong pid;
+ char *proc_path = NULL;
+ char *cmdline_contents = NULL;
+
+ if (!g_file_get_contents (pidfile, &contents, NULL, NULL))
+ return;
+
+ pid = strtol (contents, NULL, 10);
+ if (pid < 1 || pid > INT_MAX)
+ goto out;
+
+ proc_path = g_strdup_printf ("/proc/%ld/cmdline", pid);
+ if (!g_file_get_contents (proc_path, &cmdline_contents, NULL, NULL))
+ goto out;
+
+ if (strstr (cmdline_contents, kill_match)) {
+ if (kill (pid, 0) == 0) {
+ nm_log_dbg (LOGD_DNS, "Killing stale %s child process %ld", progname, pid);
+ kill (pid, SIGKILL);
+ }
+ unlink (pidfile);
+ }
+
+out:
+ g_free (cmdline_contents);
+ g_free (proc_path);
+ g_free (contents);
+}
+
+static void
+watch_cb (GPid pid, gint status, gpointer user_data)
+{
+ NMDnsPlugin *self = NM_DNS_PLUGIN (user_data);
+ NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
+
+ priv->pid = 0;
+ g_free (priv->progname);
+ priv->progname = NULL;
+
+ g_signal_emit (self, signals[CHILD_QUIT], 0, status);
+}
+
+static void
+child_setup (gpointer user_data G_GNUC_UNUSED)
+{
+ /* We are in the child process at this point */
+ pid_t pid = getpid ();
+ setpgid (pid, pid);
+}
+
+GPid
+nm_dns_plugin_child_spawn (NMDnsPlugin *self,
+ const char **argv,
+ const char *pidfile,
+ const char *kill_match)
+{
+ NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
+ GError *error = NULL;
+ char *cmdline;
+
+ g_return_val_if_fail (argv != NULL, 0);
+ g_return_val_if_fail (argv[0] != NULL, 0);
+
+ g_warn_if_fail (priv->progname == NULL);
+ g_free (priv->progname);
+ priv->progname = g_path_get_basename (argv[0]);
+
+ if (pidfile) {
+ g_return_val_if_fail (kill_match != NULL, 0);
+ kill_existing (priv->progname, pidfile, kill_match);
+
+ g_free (priv->pidfile);
+ priv->pidfile = g_strdup (pidfile);
+ }
+
+ nm_log_info (LOGD_DNS, "DNS: starting %s...", priv->progname);
+ cmdline = g_strjoinv (" ", (char **) argv);
+ nm_log_dbg (LOGD_DNS, "DNS: command line: %s", cmdline);
+ g_free (cmdline);
+
+ priv->pid = 0;
+ if (g_spawn_async (NULL, (char **) argv, NULL,
+ G_SPAWN_DO_NOT_REAP_CHILD,
+ child_setup,
+ NULL, &priv->pid,
+ &error)) {
+ nm_log_dbg (LOGD_DNS, "%s started with pid %d", priv->progname, priv->pid);
+ priv->watch_id = g_child_watch_add (priv->pid, (GChildWatchFunc) watch_cb, self);
+ } else {
+ nm_log_warn (LOGD_DNS, "Failed to spawn %s: (%d) %s",
+ priv->progname, error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ }
+
+ return priv->pid;
+}
+
+typedef struct {
+ int pid;
+ char *progname;
+} KillInfo;
+
+static gboolean
+ensure_killed (gpointer data)
+{
+ KillInfo *info = data;
+
+ if (kill (info->pid, 0) == 0)
+ kill (info->pid, SIGKILL);
+
+ /* ensure the child is reaped */
+ nm_log_dbg (LOGD_DNS, "waiting for %s pid %d to exit", info->progname, info->pid);
+ waitpid (info->pid, NULL, 0);
+ nm_log_dbg (LOGD_DNS, "dnsmasq pid %d cleaned up", info->progname, info->pid);
+
+ g_free (info->progname);
+ g_free (info);
+ return FALSE;
+}
+
+gboolean nm_dns_plugin_child_kill (NMDnsPlugin *self)
+{
+ NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
+
+ if (priv->watch_id) {
+ g_source_remove (priv->watch_id);
+ priv->watch_id = 0;
+ }
+
+ if (priv->pid) {
+ KillInfo *info;
+
+ if (kill (priv->pid, SIGTERM) == 0) {
+ info = g_malloc0 (sizeof (KillInfo));
+ info->pid = priv->pid;
+ info->progname = g_strdup (priv->progname);
+ g_timeout_add_seconds (2, ensure_killed, info);
+ } else {
+ kill (priv->pid, SIGKILL);
+
+ /* ensure the child is reaped */
+ nm_log_dbg (LOGD_DNS, "waiting for %s pid %d to exit", priv->progname, priv->pid);
+ waitpid (priv->pid, NULL, 0);
+ nm_log_dbg (LOGD_DNS, "%s pid %d cleaned up", priv->progname, priv->pid);
+ }
+ priv->pid = 0;
+ g_free (priv->progname);
+ priv->progname = NULL;
+ }
+
+ if (priv->pidfile) {
+ unlink (priv->pidfile);
+ g_free (priv->pidfile);
+ priv->pidfile = NULL;
+ }
+
+ return TRUE;
+}
+
+/********************************************/
+
+static void
+nm_dns_plugin_init (NMDnsPlugin *self)
+{
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDnsPlugin *self = NM_DNS_PLUGIN (object);
+ NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
+
+ if (!priv->disposed) {
+ priv->disposed = TRUE;
+
+ nm_dns_plugin_child_kill (self);
+ }
+
+ G_OBJECT_CLASS (nm_dns_plugin_parent_class)->dispose (object);
+}
+
+static void
+finalize (GObject *object)
+{
+ NMDnsPlugin *self = NM_DNS_PLUGIN (object);
+ NMDnsPluginPrivate *priv = NM_DNS_PLUGIN_GET_PRIVATE (self);
+
+ g_free (priv->progname);
+ g_free (priv->pidfile);
+
+ G_OBJECT_CLASS (nm_dns_plugin_parent_class)->finalize (object);
+}
+
+static void
+nm_dns_plugin_class_init (NMDnsPluginClass *plugin_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (plugin_class);
+
+ g_type_class_add_private (plugin_class, sizeof (NMDnsPluginPrivate));
+
+ /* virtual methods */
+ object_class->dispose = dispose;
+ object_class->finalize = finalize;
+ plugin_class->is_caching = is_caching;
+
+ /* signals */
+ signals[FAILED] =
+ g_signal_new (NM_DNS_PLUGIN_FAILED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMDnsPluginClass, failed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[CHILD_QUIT] =
+ g_signal_new (NM_DNS_PLUGIN_CHILD_QUIT,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ G_STRUCT_OFFSET (NMDnsPluginClass, child_quit),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__INT,
+ G_TYPE_NONE, 1, G_TYPE_INT);
+}
+
diff --git a/src/dns-manager/nm-dns-plugin.h b/src/dns-manager/nm-dns-plugin.h
new file mode 100644
index 0000000000..d4298b869d
--- /dev/null
+++ b/src/dns-manager/nm-dns-plugin.h
@@ -0,0 +1,112 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_DNS_PLUGIN_H
+#define NM_DNS_PLUGIN_H
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define NM_TYPE_DNS_PLUGIN (nm_dns_plugin_get_type ())
+#define NM_DNS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DNS_PLUGIN, NMDnsPlugin))
+#define NM_DNS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_DNS_PLUGIN, NMDnsPluginClass))
+#define NM_IS_DNS_PLUGIN(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DNS_PLUGIN))
+#define NM_IS_DNS_PLUGIN_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_DNS_PLUGIN))
+#define NM_DNS_PLUGIN_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DNS_PLUGIN, NMDnsPluginClass))
+
+#define NM_DNS_PLUGIN_FAILED "failed"
+#define NM_DNS_PLUGIN_CHILD_QUIT "child-quit"
+
+typedef struct {
+ GObject parent;
+} NMDnsPlugin;
+
+typedef struct {
+ GObjectClass parent;
+
+ /* Methods */
+ gboolean (*init) (NMDnsPlugin *self);
+
+ /* Called when DNS information is changed. 'vpn_configs' is a list of
+ * NMIP4Config or NMIP6Config objects from VPN connections, while
+ * 'dev_configs' is a list of NMPI4Config or NMIP6Config objects from
+ * active devices. 'other_configs' represent other IP configuration that
+ * may be in-use. Configs of the same IP version are sorted in priority
+ * order.
+ */
+ gboolean (*update) (NMDnsPlugin *self,
+ const GSList *vpn_configs,
+ const GSList *dev_configs,
+ const GSList *other_configs,
+ const char *hostname);
+
+ /* Subclasses should override and return TRUE if they start a local
+ * caching nameserver that listens on localhost and would block any
+ * other local caching nameserver from operating.
+ */
+ gboolean (*is_caching) (NMDnsPlugin *self);
+
+ /* Subclasses should override this and return their plugin name */
+ const char *(*get_name) (NMDnsPlugin *self);
+
+ /* Signals */
+
+ /* Emitted by the plugin and consumed by NMDnsManager when
+ * some error happens with the nameserver subprocess. Causes NM to fall
+ * back to writing out a non-local-caching resolv.conf until the next
+ * DNS update.
+ */
+ void (*failed) (NMDnsPlugin *self);
+
+ /* Emitted by the plugin base class when the nameserver subprocess
+ * quits. This signal is consumed by the plugin subclasses and not
+ * by NMDnsManager. If the subclass decides the exit status (as returned
+ * by waitpid(2)) is fatal it should then emit the 'failed' signal.
+ */
+ void (*child_quit) (NMDnsPlugin *self, gint status);
+} NMDnsPluginClass;
+
+GType nm_dns_plugin_get_type (void);
+
+gboolean nm_dns_plugin_is_caching (NMDnsPlugin *self);
+
+const char *nm_dns_plugin_get_name (NMDnsPlugin *self);
+
+gboolean nm_dns_plugin_update (NMDnsPlugin *self,
+ const GSList *vpn_configs,
+ const GSList *dev_configs,
+ const GSList *other_configs,
+ const char *hostname);
+
+/* For subclasses/plugins */
+
+/* Spawn a child process and watch for it to quit. 'argv' is the NULL-terminated
+ * argument vector to spawn the child with, where argv[0] is the full path to
+ * the child's executable. If 'pidfile' is given the process owning the PID
+ * contained in 'pidfile' will be killed if its command line matches 'kill_match'
+ * and the pidfile will be deleted.
+ */
+GPid nm_dns_plugin_child_spawn (NMDnsPlugin *self,
+ const char **argv,
+ const char *pidfile,
+ const char *kill_match);
+
+gboolean nm_dns_plugin_child_kill (NMDnsPlugin *self);
+
+#endif /* NM_DNS_PLUGIN_H */
+
diff --git a/src/dns-manager/nm-dns-utils.c b/src/dns-manager/nm-dns-utils.c
new file mode 100644
index 0000000000..615adfd151
--- /dev/null
+++ b/src/dns-manager/nm-dns-utils.c
@@ -0,0 +1,99 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ */
+
+#include <arpa/inet.h>
+#include <string.h>
+
+#include "nm-dns-utils.h"
+#include "nm-utils.h"
+
+static void
+add_ip4_to_rdns_array (guint32 ip, GPtrArray *domains) /* network byte order */
+{
+ guint32 defprefix;
+ guchar *p;
+ char *str = NULL;
+ int i;
+
+ defprefix = nm_utils_ip4_get_default_prefix (ip);
+
+ /* Convert to host byte order, mask the host bits, and convert back */
+ ip = ntohl (ip);
+ ip &= 0xFFFFFFFF << (32 - defprefix);
+ ip = htonl (ip);
+ p = (guchar *) &ip;
+
+ if (defprefix == 8)
+ str = g_strdup_printf ("%u.in-addr.arpa", p[0] & 0xFF);
+ else if (defprefix == 16)
+ str = g_strdup_printf ("%u.%u.in-addr.arpa", p[1] & 0xFF, p[0] & 0xFF);
+ else if (defprefix == 24)
+ str = g_strdup_printf ("%u.%u.%u.in-addr.arpa", p[2] & 0xFF, p[1] & 0xFF, p[0] & 0xFF);
+
+ g_return_if_fail (str != NULL);
+
+ /* Suppress duplicates */
+ for (i = 0; i < domains->len; i++) {
+ if (strcmp (str, g_ptr_array_index (domains, i)) == 0)
+ break;
+ }
+
+ if (i == domains->len)
+ g_ptr_array_add (domains, str);
+ else
+ g_free (str);
+}
+
+char **
+nm_dns_utils_get_ip4_rdns_domains (NMIP4Config *ip4)
+{
+ GPtrArray *domains = NULL;
+ int i;
+
+ g_return_val_if_fail (ip4 != NULL, NULL);
+
+ domains = g_ptr_array_sized_new (5);
+
+ /* To calculate the reverse DNS domains for this IP4 config, we take
+ * all the IP addresses and routes in the config, calculate the network
+ * portion, and convert that to classful, and use the network bits for
+ * the final domain. FIXME: better handle classless routing, which might
+ * require us to add multiple domains for each actual network prefix to
+ * cover all the separate networks in that block.
+ */
+
+ for (i = 0; i < nm_ip4_config_get_num_addresses (ip4); i++) {
+ NMIP4Address *addr = nm_ip4_config_get_address (ip4, i);
+
+ add_ip4_to_rdns_array (nm_ip4_address_get_address (addr), domains);
+ }
+
+ for (i = 0; i < nm_ip4_config_get_num_routes (ip4); i++) {
+ NMIP4Route *route = nm_ip4_config_get_route (ip4, i);
+
+ add_ip4_to_rdns_array (nm_ip4_route_get_dest (route), domains);
+ }
+
+ /* Terminating NULL so we can use g_strfreev() to free it */
+ g_ptr_array_add (domains, NULL);
+
+ /* Free the array and return NULL if the only element was the ending NULL */
+ return (char **) g_ptr_array_free (domains, (domains->len == 1));
+}
+
diff --git a/src/dns-manager/nm-dns-utils.h b/src/dns-manager/nm-dns-utils.h
new file mode 100644
index 0000000000..daa6711cce
--- /dev/null
+++ b/src/dns-manager/nm-dns-utils.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ *
+ */
+
+#ifndef NM_DNS_UTILS_H
+#define NM_DNS_UTILS_H
+
+#include "nm-ip4-config.h"
+
+char **nm_dns_utils_get_ip4_rdns_domains (NMIP4Config *ip4);
+
+#endif /* NM_DNS_UTILS_H */
+
diff --git a/src/dnsmasq-manager/nm-dnsmasq-manager.c b/src/dnsmasq-manager/nm-dnsmasq-manager.c
index ea529c77eb..701c078268 100644
--- a/src/dnsmasq-manager/nm-dnsmasq-manager.c
+++ b/src/dnsmasq-manager/nm-dnsmasq-manager.c
@@ -18,6 +18,7 @@
* Copyright (C) 2008 - 2010 Red Hat, Inc.
*/
+#include <config.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
@@ -251,8 +252,9 @@ create_dm_cmd_line (const char *iface,
GString *s;
NMIP4Address *tmp;
struct in_addr addr;
- char buf[INET_ADDRSTRLEN + 1];
+ char buf[INET_ADDRSTRLEN + 15];
char localaddr[INET_ADDRSTRLEN + 1];
+ int i;
dm_binary = nm_find_dnsmasq ();
if (!dm_binary) {
@@ -273,6 +275,21 @@ create_dm_cmd_line (const char *iface,
nm_cmd_line_add_string (cmd, "--log-queries");
}
+ /* dnsmasq may read from it's default config file location, which if that
+ * location is a valid config file, it will combine with the options here
+ * and cause undesirable side-effects. Like sending bogus IP addresses
+ * as the gateway or whatever. So give dnsmasq a bogus config file
+ * location to avoid screwing up the configuration we're passing to it.
+ */
+ memset (buf, 0, sizeof (buf));
+ strcpy (buf, "/tmp/");
+ for (i = 5; i < 15; i++)
+ buf[i] = (char) (g_random_int_range ((guint32) 'a', (guint32) 'z') & 0xFF);
+ strcat (buf, ".conf");
+
+ nm_cmd_line_add_string (cmd, "--conf-file");
+ nm_cmd_line_add_string (cmd, buf);
+
nm_cmd_line_add_string (cmd, "--no-hosts");
nm_cmd_line_add_string (cmd, "--keep-in-foreground");
nm_cmd_line_add_string (cmd, "--bind-interfaces");
@@ -369,7 +386,7 @@ kill_existing_for_iface (const char *iface, const char *pidfile)
goto out;
if (strstr (cmdline_contents, "bin/dnsmasq")) {
- if (kill (pid, 0)) {
+ if (kill (pid, 0) == 0) {
nm_log_dbg (LOGD_SHARING, "Killing stale dnsmasq process %ld", pid);
kill (pid, SIGKILL);
}
diff --git a/src/ip6-manager/Makefile.am b/src/ip6-manager/Makefile.am
index c2f5591429..b56b197e75 100644
--- a/src/ip6-manager/Makefile.am
+++ b/src/ip6-manager/Makefile.am
@@ -4,8 +4,7 @@ INCLUDES = \
-I${top_builddir}/marshallers \
-I${top_srcdir}/libnm-util \
-I${top_srcdir}/src/logging \
- -I${top_srcdir}/src \
- -I${top_srcdir}/src/named-manager
+ -I${top_srcdir}/src
noinst_LTLIBRARIES = libip6-manager.la
diff --git a/src/ip6-manager/nm-ip6-manager.c b/src/ip6-manager/nm-ip6-manager.c
index f6f6127ce7..ea93f02c02 100644
--- a/src/ip6-manager/nm-ip6-manager.c
+++ b/src/ip6-manager/nm-ip6-manager.c
@@ -76,6 +76,11 @@ typedef struct {
time_t expires;
} NMIP6RDNSS;
+typedef struct {
+ char domain[256];
+ time_t expires;
+} NMIP6DNSSL;
+
/******************************************************************/
typedef struct {
@@ -97,6 +102,9 @@ typedef struct {
GArray *rdnss_servers;
guint rdnss_timeout_id;
+ GArray *dnssl_domains;
+ guint dnssl_timeout_id;
+
guint ip6flags_poll_id;
guint32 ra_flags;
@@ -122,6 +130,10 @@ nm_ip6_device_destroy (NMIP6Device *device)
g_array_free (device->rdnss_servers, TRUE);
if (device->rdnss_timeout_id)
g_source_remove (device->rdnss_timeout_id);
+ if (device->dnssl_domains)
+ g_array_free (device->dnssl_domains, TRUE);
+ if (device->dnssl_timeout_id)
+ g_source_remove (device->dnssl_timeout_id);
if (device->ip6flags_poll_id)
g_source_remove (device->ip6flags_poll_id);
@@ -155,6 +167,8 @@ nm_ip6_device_new (NMIP6Manager *manager, int ifindex)
device->rdnss_servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS));
+ device->dnssl_domains = g_array_new (FALSE, FALSE, sizeof (NMIP6DNSSL));
+
g_hash_table_replace (priv->devices, GINT_TO_POINTER (device->ifindex), device);
/* and the original value of IPv6 enable/disable */
@@ -285,7 +299,7 @@ set_rdnss_timeout (NMIP6Device *device)
nm_log_dbg (LOGD_IP6, "(%s): removing expired RA-provided nameserver %s",
device->iface, buf);
}
- g_array_remove_index_fast (device->rdnss_servers, i--);
+ g_array_remove_index (device->rdnss_servers, i--);
continue;
}
@@ -300,6 +314,61 @@ set_rdnss_timeout (NMIP6Device *device)
}
}
+static void set_dnssl_timeout (NMIP6Device *device);
+
+static gboolean
+dnssl_expired (gpointer user_data)
+{
+ NMIP6Device *device = user_data;
+ CallbackInfo info = { device, IP6_DHCP_OPT_NONE };
+
+ nm_log_dbg (LOGD_IP6, "(%s): IPv6 DNSSL information expired", device->iface);
+
+ set_dnssl_timeout (device);
+ emit_config_changed (&info);
+ return FALSE;
+}
+
+static void
+set_dnssl_timeout (NMIP6Device *device)
+{
+ time_t expires = 0, now = time (NULL);
+ NMIP6DNSSL *dnssl;
+ int i;
+
+ if (device->dnssl_timeout_id) {
+ g_source_remove (device->dnssl_timeout_id);
+ device->dnssl_timeout_id = 0;
+ }
+
+ /* Find the soonest expiration time. */
+ for (i = 0; i < device->dnssl_domains->len; i++) {
+ dnssl = &g_array_index (device->dnssl_domains, NMIP6DNSSL, i);
+ if (dnssl->expires == 0)
+ continue;
+
+ /* If the entry has already expired, remove it; the "+ 1" is
+ * because g_timeout_add_seconds() might fudge the timing a
+ * bit.
+ */
+ if (dnssl->expires <= now + 1) {
+ nm_log_dbg (LOGD_IP6, "(%s): removing expired RA-provided domain %s",
+ device->iface, dnssl->domain);
+ g_array_remove_index (device->dnssl_domains, i--);
+ continue;
+ }
+
+ if (!expires || dnssl->expires < expires)
+ expires = dnssl->expires;
+ }
+
+ if (expires) {
+ device->dnssl_timeout_id = g_timeout_add_seconds (expires - now,
+ dnssl_expired,
+ device);
+ }
+}
+
static CallbackInfo *
callback_info_new (NMIP6Device *device, guint dhcp_opts, gboolean success)
{
@@ -569,13 +638,258 @@ process_prefix (NMIP6Manager *manager, struct nl_msg *msg)
*/
#define ND_OPT_RDNSS 25
+#define ND_OPT_DNSSL 31
+
struct nd_opt_rdnss {
uint8_t nd_opt_rdnss_type;
uint8_t nd_opt_rdnss_len;
uint16_t nd_opt_rdnss_reserved1;
uint32_t nd_opt_rdnss_lifetime;
/* followed by one or more IPv6 addresses */
-};
+} __attribute__ ((packed));
+
+struct nd_opt_dnssl {
+ uint8_t nd_opt_dnssl_type;
+ uint8_t nd_opt_dnssl_len;
+ uint16_t nd_opt_dnssl_reserved1;
+ uint32_t nd_opt_dnssl_lifetime;
+ /* followed by one or more suffixes */
+} __attribute__ ((packed));
+
+static gboolean
+process_nduseropt_rdnss (NMIP6Device *device, struct nd_opt_hdr *opt)
+{
+ size_t opt_len;
+ struct nd_opt_rdnss *rdnss_opt;
+ time_t now = time (NULL);
+ struct in6_addr *addr;
+ GArray *new_servers;
+ NMIP6RDNSS server, *cur_server;
+ gboolean changed = FALSE;
+ guint i;
+
+ opt_len = opt->nd_opt_len;
+
+ if (opt_len < 3 || (opt_len & 1) == 0)
+ return FALSE;
+
+ rdnss_opt = (struct nd_opt_rdnss *) opt;
+
+ new_servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS));
+
+ /* Pad the DNS server expiry somewhat to give a bit of slack in cases
+ * where one RA gets lost or something (which can happen on unreliable
+ * links like WiFi where certain types of frames are not retransmitted).
+ * Note that 0 has special meaning and is therefore not adjusted.
+ */
+ server.expires = ntohl (rdnss_opt->nd_opt_rdnss_lifetime);
+ if (server.expires > 0)
+ server.expires += now + 10;
+
+ for (addr = (struct in6_addr *) (rdnss_opt + 1); opt_len >= 2; addr++, opt_len -= 2) {
+ char buf[INET6_ADDRSTRLEN + 1];
+
+ if (!inet_ntop (AF_INET6, addr, buf, sizeof (buf)))
+ strcpy(buf, "[invalid]");
+
+ for (i = 0; i < device->rdnss_servers->len; i++) {
+ cur_server = &(g_array_index (device->rdnss_servers, NMIP6RDNSS, i));
+
+ if (!IN6_ARE_ADDR_EQUAL (addr, &cur_server->addr))
+ continue;
+
+ cur_server->expires = server.expires;
+
+ if (server.expires > 0) {
+ nm_log_dbg (LOGD_IP6, "(%s): refreshing RA-provided nameserver %s (expires in %d seconds)",
+ device->iface, buf,
+ server.expires - now);
+ break;
+ }
+
+ nm_log_dbg (LOGD_IP6, "(%s): removing RA-provided nameserver %s on router request",
+ device->iface, buf);
+
+ g_array_remove_index (device->rdnss_servers, i);
+ changed = TRUE;
+ break;
+ }
+
+ if (server.expires == 0)
+ continue;
+ if (i < device->rdnss_servers->len)
+ continue;
+
+ nm_log_dbg (LOGD_IP6, "(%s): found RA-provided nameserver %s (expires in %d seconds)",
+ device->iface, buf, server.expires - now);
+
+ server.addr = *addr;
+ g_array_append_val (new_servers, server);
+ }
+
+ /* New servers must be added in the order they are listed in the
+ * RA option and before any existing servers.
+ *
+ * Note: This is the place to remove servers if we want to cap the
+ * number of resolvers. The RFC states that the one to expire
+ * first of the existing servers should be removed.
+ */
+ if (new_servers->len) {
+ g_array_prepend_vals (device->rdnss_servers,
+ new_servers->data, new_servers->len);
+ changed = TRUE;
+ }
+
+ g_array_free (new_servers, TRUE);
+
+ /* Timeouts may have changed even if IPs didn't */
+ set_rdnss_timeout (device);
+
+ return changed;
+}
+
+static const char *
+parse_dnssl_domain (const unsigned char *buffer, size_t maxlen)
+{
+ static char domain[256];
+ size_t label_len;
+
+ domain[0] = '\0';
+
+ while (maxlen > 0) {
+ label_len = *buffer;
+ buffer++;
+ maxlen--;
+
+ if (label_len == 0)
+ return domain;
+
+ if (label_len > maxlen)
+ return NULL;
+ if ((sizeof (domain) - strlen (domain)) < (label_len + 2))
+ return NULL;
+
+ if (domain[0] != '\0')
+ strcat (domain, ".");
+ strncat (domain, (const char *)buffer, label_len);
+ buffer += label_len;
+ maxlen -= label_len;
+ }
+
+ return NULL;
+}
+
+static gboolean
+process_nduseropt_dnssl (NMIP6Device *device, struct nd_opt_hdr *opt)
+{
+ size_t opt_len;
+ struct nd_opt_dnssl *dnssl_opt;
+ unsigned char *opt_ptr;
+ time_t now = time (NULL);
+ GArray *new_domains;
+ NMIP6DNSSL domain, *cur_domain;
+ gboolean changed;
+ guint i;
+
+ opt_len = opt->nd_opt_len;
+
+ if (opt_len < 2)
+ return FALSE;
+
+ dnssl_opt = (struct nd_opt_dnssl *) opt;
+
+ opt_ptr = (unsigned char *)(dnssl_opt + 1);
+ opt_len = (opt_len - 1) * 8; /* prefer bytes for later handling */
+
+ new_domains = g_array_new (FALSE, FALSE, sizeof (NMIP6DNSSL));
+
+ changed = FALSE;
+
+ /* Pad the DNS server expiry somewhat to give a bit of slack in cases
+ * where one RA gets lost or something (which can happen on unreliable
+ * links like wifi where certain types of frames are not retransmitted).
+ * Note that 0 has special meaning and is therefore not adjusted.
+ */
+ domain.expires = ntohl (dnssl_opt->nd_opt_dnssl_lifetime);
+ if (domain.expires > 0)
+ domain.expires += now + 10;
+
+ while (opt_len) {
+ const char *domain_str;
+
+ domain_str = parse_dnssl_domain (opt_ptr, opt_len);
+ if (domain_str == NULL) {
+ nm_log_dbg (LOGD_IP6, "(%s): invalid DNSSL option, parsing aborted",
+ device->iface);
+ break;
+ }
+
+ /* The DNSSL encoding of domains happen to occupy the same size
+ * as the length of the resulting string, including terminating
+ * null. */
+ opt_ptr += strlen (domain_str) + 1;
+ opt_len -= strlen (domain_str) + 1;
+
+ /* Ignore empty domains. They're probably just padding... */
+ if (domain_str[0] == '\0')
+ continue;
+
+ for (i = 0; i < device->dnssl_domains->len; i++) {
+ cur_domain = &(g_array_index (device->dnssl_domains, NMIP6DNSSL, i));
+
+ if (strcmp (domain_str, cur_domain->domain) != 0)
+ continue;
+
+ cur_domain->expires = domain.expires;
+
+ if (domain.expires > 0) {
+ nm_log_dbg (LOGD_IP6, "(%s): refreshing RA-provided domain %s (expires in %d seconds)",
+ device->iface, domain_str,
+ domain.expires - now);
+ break;
+ }
+
+ nm_log_dbg (LOGD_IP6, "(%s): removing RA-provided domain %s on router request",
+ device->iface, domain_str);
+
+ g_array_remove_index (device->dnssl_domains, i);
+ changed = TRUE;
+ break;
+ }
+
+ if (domain.expires == 0)
+ continue;
+ if (i < device->dnssl_domains->len)
+ continue;
+
+ nm_log_dbg (LOGD_IP6, "(%s): found RA-provided domain %s (expires in %d seconds)",
+ device->iface, domain_str, domain.expires - now);
+
+ g_assert (strlen (domain_str) < sizeof (domain.domain));
+ strcpy (domain.domain, domain_str);
+ g_array_append_val (new_domains, domain);
+ }
+
+ /* New domains must be added in the order they are listed in the
+ * RA option and before any existing domains.
+ *
+ * Note: This is the place to remove domains if we want to cap the
+ * number of domains. The RFC states that the one to expire
+ * first of the existing domains should be removed.
+ */
+ if (new_domains->len) {
+ g_array_prepend_vals (device->dnssl_domains,
+ new_domains->data, new_domains->len);
+ changed = TRUE;
+ }
+
+ g_array_free (new_domains, TRUE);
+
+ /* Timeouts may have changed even if domains didn't */
+ set_dnssl_timeout (device);
+
+ return changed;
+}
static NMIP6Device *
process_nduseropt (NMIP6Manager *manager, struct nl_msg *msg)
@@ -583,13 +897,8 @@ process_nduseropt (NMIP6Manager *manager, struct nl_msg *msg)
NMIP6Device *device;
struct nduseroptmsg *ndmsg;
struct nd_opt_hdr *opt;
- guint opts_len, i;
- time_t now = time (NULL);
- struct nd_opt_rdnss *rdnss_opt;
- struct in6_addr *addr;
- GArray *servers;
- NMIP6RDNSS server, *sa, *sb;
- gboolean changed;
+ guint opts_len;
+ gboolean changed = FALSE;
nm_log_dbg (LOGD_IP6, "processing netlink nduseropt message");
@@ -608,8 +917,6 @@ process_nduseropt (NMIP6Manager *manager, struct nl_msg *msg)
return NULL;
}
- servers = g_array_new (FALSE, FALSE, sizeof (NMIP6RDNSS));
-
opt = (struct nd_opt_hdr *) (ndmsg + 1);
opts_len = ndmsg->nduseropt_opts_len;
@@ -619,66 +926,19 @@ process_nduseropt (NMIP6Manager *manager, struct nl_msg *msg)
if (nd_opt_len == 0 || opts_len < (nd_opt_len << 3))
break;
- if (opt->nd_opt_type != ND_OPT_RDNSS)
- goto next;
-
- if (nd_opt_len < 3 || (nd_opt_len & 1) == 0)
- goto next;
-
- rdnss_opt = (struct nd_opt_rdnss *) opt;
-
- /* Pad the DNS server expiry somewhat to give a bit of slack in cases
- * where one RA gets lost or something (which can happen on unreliable
- * links like wifi where certain types of frames are not retransmitted).
- */
- server.expires = now + ntohl (rdnss_opt->nd_opt_rdnss_lifetime) + 10;
-
- for (addr = (struct in6_addr *) (rdnss_opt + 1); nd_opt_len >= 2; addr++, nd_opt_len -= 2) {
- char buf[INET6_ADDRSTRLEN + 1];
-
- if (inet_ntop (AF_INET6, addr, buf, sizeof (buf))) {
- nm_log_dbg (LOGD_IP6, "(%s): found RA-provided nameserver %s (expires in %d seconds)",
- device->iface, buf,
- ntohl (rdnss_opt->nd_opt_rdnss_lifetime));
- }
-
- server.addr = *addr;
- g_array_append_val (servers, server);
+ switch (opt->nd_opt_type) {
+ case ND_OPT_RDNSS:
+ changed = process_nduseropt_rdnss (device, opt);
+ break;
+ case ND_OPT_DNSSL:
+ changed = process_nduseropt_dnssl (device, opt);
+ break;
}
- next:
opts_len -= opt->nd_opt_len << 3;
opt = (struct nd_opt_hdr *) ((uint8_t *) opt + (opt->nd_opt_len << 3));
}
- /* See if anything (other than expiration time) changed */
- if (servers->len != device->rdnss_servers->len)
- changed = TRUE;
- else {
- for (i = 0; i < servers->len; i++) {
- sa = &(g_array_index (servers, NMIP6RDNSS, i));
- sb = &(g_array_index (device->rdnss_servers, NMIP6RDNSS, i));
- if (IN6_ARE_ADDR_EQUAL (&sa->addr, &sb->addr) == FALSE) {
- changed = TRUE;
- break;
- }
- }
- changed = FALSE;
- }
-
- if (changed) {
- nm_log_dbg (LOGD_IP6, "(%s): RA-provided nameservers changed", device->iface);
- }
-
- /* Always copy in new servers (even if unchanged) to get their updated
- * expiration times.
- */
- g_array_free (device->rdnss_servers, TRUE);
- device->rdnss_servers = servers;
-
- /* Timeouts may have changed even if IPs didn't */
- set_rdnss_timeout (device);
-
if (changed)
return device;
else
@@ -1014,6 +1274,14 @@ nm_ip6_manager_get_ip6_config (NMIP6Manager *manager, int ifindex)
nm_ip6_config_add_nameserver (config, &rdnss[i].addr);
}
+ /* Add DNS domains */
+ if (device->dnssl_domains) {
+ NMIP6DNSSL *dnssl = (NMIP6DNSSL *)(device->dnssl_domains->data);
+
+ for (i = 0; i < device->dnssl_domains->len; i++)
+ nm_ip6_config_add_domain (config, dnssl[i].domain);
+ }
+
return config;
}
diff --git a/src/logging/nm-logging.c b/src/logging/nm-logging.c
index 9d1905da08..5b5622fd70 100644
--- a/src/logging/nm-logging.c
+++ b/src/logging/nm-logging.c
@@ -209,6 +209,12 @@ nm_logging_domains_to_string (void)
return g_string_free (str, FALSE);
}
+gboolean
+nm_logging_level_enabled (guint32 level)
+{
+ return !!(log_level & level);
+}
+
void _nm_log (const char *loc,
const char *func,
guint32 domain,
diff --git a/src/logging/nm-logging.h b/src/logging/nm-logging.h
index 2eedf5ff14..e4abcf75ca 100644
--- a/src/logging/nm-logging.h
+++ b/src/logging/nm-logging.h
@@ -90,6 +90,7 @@ void _nm_log (const char *loc, const char *func,
const char *nm_logging_level_to_string (void);
char *nm_logging_domains_to_string (void);
+gboolean nm_logging_level_enabled (guint32 level);
/* Undefine the nm-utils.h logging stuff to ensure errors */
#undef nm_print_backtrace
diff --git a/src/main.c b/src/main.c
index ff7805636a..1e3db217b1 100644
--- a/src/main.c
+++ b/src/main.c
@@ -19,10 +19,7 @@
* Copyright (C) 2005 - 2008 Novell, Inc.
*/
-#ifdef HAVE_CONFIG_H
-# include <config.h>
-#endif
-
+#include <config.h>
#include <glib.h>
#include <dbus/dbus.h>
#include <dbus/dbus-glib-lowlevel.h>
@@ -44,7 +41,7 @@
#include "nm-manager.h"
#include "nm-policy.h"
#include "nm-system.h"
-#include "nm-named-manager.h"
+#include "nm-dns-manager.h"
#include "nm-dbus-manager.h"
#include "nm-supplicant-manager.h"
#include "nm-dhcp-manager.h"
@@ -52,6 +49,7 @@
#include "nm-netlink-monitor.h"
#include "nm-vpn-manager.h"
#include "nm-logging.h"
+#include "nm-policy-hosts.h"
#if !defined(NM_DIST_VERSION)
# define NM_DIST_VERSION VERSION
@@ -303,11 +301,13 @@ static gboolean
parse_config_file (const char *filename,
char **plugins,
char **dhcp_client,
+ char ***dns_plugins,
char **log_level,
char **log_domains,
GError **error)
{
GKeyFile *config;
+ gboolean success = FALSE;
config = g_key_file_new ();
if (!config) {
@@ -318,19 +318,23 @@ parse_config_file (const char *filename,
g_key_file_set_list_separator (config, ',');
if (!g_key_file_load_from_file (config, filename, G_KEY_FILE_NONE, error))
- return FALSE;
+ goto out;
*plugins = g_key_file_get_value (config, "main", "plugins", error);
if (*error)
- return FALSE;
+ goto out;
*dhcp_client = g_key_file_get_value (config, "main", "dhcp", NULL);
+ *dns_plugins = g_key_file_get_string_list (config, "main", "dns", NULL, NULL);
*log_level = g_key_file_get_value (config, "logging", "level", NULL);
*log_domains = g_key_file_get_value (config, "logging", "domains", NULL);
+ success = TRUE;
+
+out:
g_key_file_free (config);
- return TRUE;
+ return success;
}
static gboolean
@@ -456,11 +460,12 @@ main (int argc, char *argv[])
char *pidfile = NULL, *state_file = NULL, *dhcp = NULL;
char *config = NULL, *plugins = NULL, *conf_plugins = NULL;
char *log_level = NULL, *log_domains = NULL;
+ char **dns = NULL;
gboolean wifi_enabled = TRUE, net_enabled = TRUE, wwan_enabled = TRUE, wimax_enabled = TRUE;
gboolean success;
NMPolicy *policy = NULL;
NMVPNManager *vpn_manager = NULL;
- NMNamedManager *named_mgr = NULL;
+ NMDnsManager *dns_mgr = NULL;
NMDBusManager *dbus_mgr = NULL;
NMSupplicantManager *sup_mgr = NULL;
NMDHCPManager *dhcp_mgr = NULL;
@@ -529,7 +534,7 @@ main (int argc, char *argv[])
/* Parse the config file */
if (config) {
- if (!parse_config_file (config, &conf_plugins, &dhcp, &cfg_log_level, &cfg_log_domains, &error)) {
+ if (!parse_config_file (config, &conf_plugins, &dhcp, &dns, &cfg_log_level, &cfg_log_domains, &error)) {
fprintf (stderr, "Config file %s invalid: (%d) %s\n",
config,
error ? error->code : -1,
@@ -539,10 +544,17 @@ main (int argc, char *argv[])
} else {
gboolean parsed = FALSE;
- /* Try NetworkManager.conf first */
- if (g_file_test (NM_DEFAULT_SYSTEM_CONF_FILE, G_FILE_TEST_EXISTS)) {
- config = g_strdup (NM_DEFAULT_SYSTEM_CONF_FILE);
- parsed = parse_config_file (config, &conf_plugins, &dhcp, &cfg_log_level, &cfg_log_domains, &error);
+ /* Even though we prefer NetworkManager.conf, we need to check the
+ * old nm-system-settings.conf first to preserve compat with older
+ * setups. In package managed systems dropping a NetworkManager.conf
+ * onto the system would make NM use it instead of nm-system-settings.conf,
+ * changing behavior during an upgrade. We don't want that.
+ */
+
+ /* Try deprecated nm-system-settings.conf first */
+ if (g_file_test (NM_OLD_SYSTEM_CONF_FILE, G_FILE_TEST_EXISTS)) {
+ config = g_strdup (NM_OLD_SYSTEM_CONF_FILE);
+ parsed = parse_config_file (config, &conf_plugins, &dhcp, &dns, &cfg_log_level, &cfg_log_domains, &error);
if (!parsed) {
fprintf (stderr, "Default config file %s invalid: (%d) %s\n",
config,
@@ -551,14 +563,14 @@ main (int argc, char *argv[])
g_free (config);
config = NULL;
g_clear_error (&error);
- /* Not a hard failure */
}
}
- /* Try old nm-system-settings.conf next */
- if (!parsed) {
- config = g_strdup (NM_OLD_SYSTEM_CONF_FILE);
- if (!parse_config_file (config, &conf_plugins, &dhcp, &cfg_log_level, &cfg_log_domains, &error)) {
+ /* Try the preferred NetworkManager.conf last */
+ if (!parsed && g_file_test (NM_DEFAULT_SYSTEM_CONF_FILE, G_FILE_TEST_EXISTS)) {
+ config = g_strdup (NM_DEFAULT_SYSTEM_CONF_FILE);
+ parsed = parse_config_file (config, &conf_plugins, &dhcp, &dns, &cfg_log_level, &cfg_log_domains, &error);
+ if (!parsed) {
fprintf (stderr, "Default config file %s invalid: (%d) %s\n",
config,
error ? error->code : -1,
@@ -566,11 +578,9 @@ main (int argc, char *argv[])
g_free (config);
config = NULL;
g_clear_error (&error);
- /* Not a hard failure */
}
}
}
-
/* Logging setup */
if (!nm_logging_setup (log_level ? log_level : cfg_log_level,
log_domains ? log_domains : cfg_log_domains,
@@ -633,6 +643,17 @@ main (int argc, char *argv[])
g_thread_init (NULL);
dbus_g_thread_init ();
+#ifndef HAVE_DBUS_GLIB_DISABLE_LEGACY_PROP_ACCESS
+#error HAVE_DBUS_GLIB_DISABLE_LEGACY_PROP_ACCESS not defined
+#endif
+
+#if HAVE_DBUS_GLIB_DISABLE_LEGACY_PROP_ACCESS
+ /* Ensure that non-exported properties don't leak out, and that the
+ * introspection 'access' permissions are respected.
+ */
+ dbus_glib_global_set_disable_legacy_property_access ();
+#endif
+
setup_signals ();
nm_logging_start (become_daemon);
@@ -640,6 +661,9 @@ main (int argc, char *argv[])
nm_log_info (LOGD_CORE, "NetworkManager (version " NM_DIST_VERSION ") is starting...");
success = FALSE;
+ if (config)
+ nm_log_info (LOGD_CORE, "Read config file %s", config);
+
main_loop = g_main_loop_new (NULL, FALSE);
/* Create watch functions that monitor cards for link status. */
@@ -658,9 +682,9 @@ main (int argc, char *argv[])
goto done;
}
- named_mgr = nm_named_manager_get ();
- if (!named_mgr) {
- nm_log_err (LOGD_CORE, "failed to start the named manager.");
+ dns_mgr = nm_dns_manager_get ((const char **) dns);
+ if (!dns_mgr) {
+ nm_log_err (LOGD_CORE, "failed to start the DNS manager.");
goto done;
}
@@ -706,6 +730,9 @@ main (int argc, char *argv[])
goto done;
}
+ /* Clean leftover "# Added by NetworkManager" entries from /etc/hosts */
+ nm_policy_hosts_clean_etc_hosts ();
+
nm_manager_start (manager);
/* Bring up the loopback interface. */
@@ -729,8 +756,8 @@ done:
if (vpn_manager)
g_object_unref (vpn_manager);
- if (named_mgr)
- g_object_unref (named_mgr);
+ if (dns_mgr)
+ g_object_unref (dns_mgr);
if (dhcp_mgr)
g_object_unref (dhcp_mgr);
@@ -752,6 +779,7 @@ done:
g_free (config);
g_free (plugins);
g_free (dhcp);
+ g_strfreev (dns);
g_free (log_level);
g_free (log_domains);
g_free (cfg_log_level);
diff --git a/src/modem-manager/Makefile.am b/src/modem-manager/Makefile.am
index a0f65c1bae..22ed809fe8 100644
--- a/src/modem-manager/Makefile.am
+++ b/src/modem-manager/Makefile.am
@@ -27,7 +27,7 @@ libmodem_manager_la_LIBADD = \
$(DBUS_LIBS)
nm-serial-device-glue.h: $(top_srcdir)/introspection/nm-device-serial.xml
- dbus-binding-tool --prefix=nm_serial_device --mode=glib-server --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_serial_device --mode=glib-server --output=$@ $<
BUILT_SOURCES = \
nm-serial-device-glue.h
diff --git a/src/modem-manager/nm-modem.c b/src/modem-manager/nm-modem.c
index e07b818bbb..b3f7eaa1cb 100644
--- a/src/modem-manager/nm-modem.c
+++ b/src/modem-manager/nm-modem.c
@@ -876,6 +876,12 @@ modem_properties_changed (DBusGProxy *proxy,
priv->mm_enabled = g_value_get_boolean (value);
g_object_notify (G_OBJECT (self), NM_MODEM_ENABLED);
}
+
+ value = g_hash_table_lookup (props, "IpMethod");
+ if (value && G_VALUE_HOLDS_UINT (value)) {
+ priv->ip_method = g_value_get_uint (value);
+ g_object_notify (G_OBJECT (self), NM_MODEM_IP_METHOD);
+ }
}
/*****************************************************************************/
diff --git a/src/named-manager/Makefile.am b/src/named-manager/Makefile.am
deleted file mode 100644
index a33f7d50a9..0000000000
--- a/src/named-manager/Makefile.am
+++ /dev/null
@@ -1,21 +0,0 @@
-INCLUDES = \
- -I${top_srcdir}/src/logging \
- -I${top_srcdir}/libnm-util \
- -I${top_srcdir}/src \
- -I${top_srcdir}/include
-
-noinst_LTLIBRARIES = libnamed-manager.la
-
-libnamed_manager_la_SOURCES = nm-named-manager.h nm-named-manager.c
-
-libnamed_manager_la_CPPFLAGS = \
- $(DBUS_CFLAGS) \
- $(GLIB_CFLAGS) \
- -DNM_PKGDATADIR=\"$(pkgdatadir)\" \
- -DNM_LOCALSTATEDIR=\"$(localstatedir)\"
-
-libnamed_manager_la_LIBADD = \
- $(top_builddir)/src/logging/libnm-logging.la \
- $(DBUS_LIBS) \
- $(GLIB_LIBS)
-
diff --git a/src/named-manager/nm-named-manager.h b/src/named-manager/nm-named-manager.h
deleted file mode 100644
index 47c8e7115d..0000000000
--- a/src/named-manager/nm-named-manager.h
+++ /dev/null
@@ -1,92 +0,0 @@
-/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
-/* NetworkManager -- Network link manager
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License along
- * with this program; if not, write to the Free Software Foundation, Inc.,
- * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- *
- * Copyright (C) 2004 - 2005 Colin Walters <walters@redhat.com>
- * Copyright (C) 2004 - 2008 Red Hat, Inc.
- * Copyright (C) 2005 - 2008 Novell, Inc.
- * and others
- */
-
-#ifndef __NM_NAMED_MANAGER_H__
-#define __NM_NAMED_MANAGER_H__
-
-#include "config.h"
-#include <glib-object.h>
-#include <dbus/dbus.h>
-#include "nm-ip4-config.h"
-#include "nm-ip6-config.h"
-
-typedef enum {
- NM_NAMED_MANAGER_ERROR_SYSTEM,
- NM_NAMED_MANAGER_ERROR_INVALID_NAMESERVER,
- NM_NAMED_MANAGER_ERROR_INVALID_HOST,
- NM_NAMED_MANAGER_ERROR_INVALID_ID
-} NMNamedManagerError;
-
-typedef enum {
- NM_NAMED_IP_CONFIG_TYPE_DEFAULT = 0,
- NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE,
- NM_NAMED_IP_CONFIG_TYPE_VPN
-} NMNamedIPConfigType;
-
-#define NM_NAMED_MANAGER_ERROR nm_named_manager_error_quark ()
-GQuark nm_named_manager_error_quark (void);
-
-G_BEGIN_DECLS
-
-#define NM_TYPE_NAMED_MANAGER (nm_named_manager_get_type ())
-#define NM_NAMED_MANAGER(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), NM_TYPE_NAMED_MANAGER, NMNamedManager))
-#define NM_NAMED_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), NM_TYPE_NAMED_MANAGER, NMNamedManagerClass))
-#define NM_IS_NAMED_MANAGER(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), NM_TYPE_NAMED_MANAGER))
-#define NM_IS_NAMED_MANAGER_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), NM_TYPE_NAMED_MANAGER))
-#define NM_NAMED_MANAGER_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), NM_TYPE_NAMED_MANAGER, NMNamedManagerClass))
-
-typedef struct NMNamedManagerPrivate NMNamedManagerPrivate;
-
-typedef struct {
- GObject parent;
-} NMNamedManager;
-
-typedef struct {
- GObjectClass parent;
-} NMNamedManagerClass;
-
-GType nm_named_manager_get_type (void);
-
-NMNamedManager * nm_named_manager_get (void);
-
-gboolean nm_named_manager_add_ip4_config (NMNamedManager *mgr,
- const char *iface,
- NMIP4Config *config,
- NMNamedIPConfigType cfg_type);
-
-gboolean nm_named_manager_remove_ip4_config (NMNamedManager *mgr,
- const char *iface,
- NMIP4Config *config);
-
-gboolean nm_named_manager_add_ip6_config (NMNamedManager *mgr,
- const char *iface,
- NMIP6Config *config,
- NMNamedIPConfigType cfg_type);
-
-gboolean nm_named_manager_remove_ip6_config (NMNamedManager *mgr,
- const char *iface,
- NMIP6Config *config);
-
-G_END_DECLS
-
-#endif /* __NM_NAMED_MANAGER_H__ */
diff --git a/src/nm-device-ethernet.c b/src/nm-device-ethernet.c
index be6d4e2f3c..00e8a2167a 100644
--- a/src/nm-device-ethernet.c
+++ b/src/nm-device-ethernet.c
@@ -34,6 +34,9 @@
#include <linux/if.h>
#include <errno.h>
+#define G_UDEV_API_IS_SUBJECT_TO_CHANGE
+#include <gudev/gudev.h>
+
#include <netlink/route/addr.h>
#include "nm-glib-compat.h"
@@ -75,37 +78,26 @@ typedef enum
#define NM_ETHERNET_ERROR (nm_ethernet_error_quark ())
#define NM_TYPE_ETHERNET_ERROR (nm_ethernet_error_get_type ())
-typedef struct SupplicantStateTask {
- NMDeviceEthernet *self;
- guint32 new_state;
- guint32 old_state;
- gboolean mgr_task;
- guint source_id;
-} SupplicantStateTask;
-
typedef struct Supplicant {
NMSupplicantManager *mgr;
NMSupplicantInterface *iface;
/* signal handler ids */
- guint mgr_state_id;
guint iface_error_id;
guint iface_state_id;
- guint iface_con_state_id;
/* Timeouts and idles */
guint iface_con_error_cb_id;
guint con_timeout_id;
-
- GSList *iface_tasks;
- GSList *mgr_tasks;
} Supplicant;
typedef struct {
- gboolean disposed;
+ gboolean disposed;
- struct ether_addr hw_addr;
- gboolean carrier;
+ guint8 hw_addr[ETH_ALEN]; /* Currently set MAC address */
+ guint8 perm_hw_addr[ETH_ALEN]; /* Permanent MAC address */
+ guint8 initial_hw_addr[ETH_ALEN]; /* Initial MAC address (as seen when NM starts) */
+ gboolean carrier;
NMNetlinkMonitor * monitor;
gulong link_connected_id;
@@ -115,6 +107,12 @@ typedef struct {
Supplicant supplicant;
guint supplicant_timeout_id;
+ /* s390 */
+ char * subchan1;
+ char * subchan2;
+ char * subchan3;
+ char * subchannels; /* Composite used for checking unmanaged specs */
+
/* PPPoE */
NMPPPManager *ppp_manager;
NMIP4Config *pending_ip4_config;
@@ -131,6 +129,7 @@ static guint signals[LAST_SIGNAL] = { 0 };
enum {
PROP_0,
PROP_HW_ADDRESS,
+ PROP_PERM_HW_ADDRESS,
PROP_SPEED,
PROP_CARRIER,
@@ -289,6 +288,109 @@ carrier_off (NMNetlinkMonitor *monitor,
}
}
+static void
+_update_s390_subchannels (NMDeviceEthernet *self)
+{
+ NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+ const char *iface;
+ GUdevClient *client;
+ GUdevDevice *dev;
+ GUdevDevice *parent = NULL;
+ const char *parent_path, *item, *driver;
+ const char *subsystems[] = { "net", NULL };
+ GDir *dir;
+ GError *error = NULL;
+
+ iface = nm_device_get_iface (NM_DEVICE (self));
+
+ client = g_udev_client_new (subsystems);
+ if (!client) {
+ nm_log_warn (LOGD_DEVICE | LOGD_HW, "(%s): failed to initialize GUdev client", iface);
+ return;
+ }
+
+ dev = g_udev_client_query_by_subsystem_and_name (client, "net", iface);
+ if (!dev) {
+ nm_log_warn (LOGD_DEVICE | LOGD_HW, "(%s): failed to find device with udev", iface);
+ goto out;
+ }
+
+ /* Try for the "ccwgroup" parent */
+ parent = g_udev_device_get_parent_with_subsystem (dev, "ccwgroup", NULL);
+ if (!parent) {
+ /* FIXME: whatever 'lcs' devices' subsystem is here... */
+ if (!parent) {
+ /* Not an s390 device */
+ goto out;
+ }
+ }
+
+ parent_path = g_udev_device_get_sysfs_path (parent);
+ dir = g_dir_open (parent_path, 0, &error);
+ if (!dir) {
+ nm_log_warn (LOGD_DEVICE | LOGD_HW, "(%s): failed to open directory '%s': %s",
+ iface, parent_path,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ goto out;
+ }
+
+ /* FIXME: we probably care about ordering here to ensure that we map
+ * cdev0 -> subchan1, cdev1 -> subchan2, etc.
+ */
+ while ((item = g_dir_read_name (dir))) {
+ char buf[50];
+ char *cdev_path;
+
+ if (strncmp (item, "cdev", 4))
+ continue; /* Not a subchannel link */
+
+ cdev_path = g_strdup_printf ("%s/%s", parent_path, item);
+
+ memset (buf, 0, sizeof (buf));
+ errno = 0;
+ if (readlink (cdev_path, &buf[0], sizeof (buf) - 1) >= 0) {
+ if (!priv->subchan1)
+ priv->subchan1 = g_path_get_basename (buf);
+ else if (!priv->subchan2)
+ priv->subchan2 = g_path_get_basename (buf);
+ else if (!priv->subchan3)
+ priv->subchan3 = g_path_get_basename (buf);
+ } else {
+ nm_log_warn (LOGD_DEVICE | LOGD_HW,
+ "(%s): failed to read cdev link '%s': %s",
+ iface, cdev_path, errno);
+ }
+ g_free (cdev_path);
+ };
+
+ g_dir_close (dir);
+
+ if (priv->subchan3) {
+ priv->subchannels = g_strdup_printf ("%s,%s,%s",
+ priv->subchan1,
+ priv->subchan2,
+ priv->subchan3);
+ } else if (priv->subchan2) {
+ priv->subchannels = g_strdup_printf ("%s,%s",
+ priv->subchan1,
+ priv->subchan2);
+ } else
+ priv->subchannels = g_strdup (priv->subchan1);
+
+ driver = nm_device_get_driver (NM_DEVICE (self));
+ nm_log_info (LOGD_DEVICE | LOGD_HW,
+ "(%s): found s390 '%s' subchannels [%s]",
+ iface, driver ? driver : "(unknown driver)", priv->subchannels);
+
+out:
+ if (parent)
+ g_object_unref (parent);
+ if (dev)
+ g_object_unref (dev);
+ g_object_unref (client);
+}
+
static GObject*
constructor (GType type,
guint n_construct_params,
@@ -312,6 +414,9 @@ constructor (GType type,
nm_device_get_iface (NM_DEVICE (self)),
nm_device_get_ifindex (NM_DEVICE (self)));
+ /* s390 stuff */
+ _update_s390_subchannels (NM_DEVICE_ETHERNET (self));
+
caps = nm_device_get_capabilities (self);
if (caps & NM_DEVICE_CAP_CARRIER_DETECT) {
GError *error = NULL;
@@ -449,10 +554,13 @@ nm_device_ethernet_new (const char *udi,
void
nm_device_ethernet_get_address (NMDeviceEthernet *self, struct ether_addr *addr)
{
+ NMDeviceEthernetPrivate *priv;
+
g_return_if_fail (self != NULL);
g_return_if_fail (addr != NULL);
- memcpy (addr, &(NM_DEVICE_ETHERNET_GET_PRIVATE (self)->hw_addr), sizeof (struct ether_addr));
+ priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+ memcpy (addr, &priv->hw_addr, sizeof (priv->hw_addr));
}
/* Returns speed in Mb/s */
@@ -496,10 +604,65 @@ out:
}
static void
+_update_hw_addr (NMDeviceEthernet *self, const guint8 *addr)
+{
+ NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+
+ g_return_if_fail (addr != NULL);
+
+ if (memcmp (&priv->hw_addr, addr, ETH_ALEN)) {
+ memcpy (&priv->hw_addr, addr, ETH_ALEN);
+ g_object_notify (G_OBJECT (self), NM_DEVICE_ETHERNET_HW_ADDRESS);
+ }
+}
+
+static gboolean
+_set_hw_addr (NMDeviceEthernet *self, const guint8 *addr, const char *detail)
+{
+ NMDevice *dev = NM_DEVICE (self);
+ NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+ const char *iface;
+ char *mac_str = NULL;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (addr != NULL, FALSE);
+
+ iface = nm_device_get_iface (dev);
+
+ mac_str = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+
+ /* Do nothing if current MAC is same */
+ if (!memcmp (&priv->hw_addr, addr, ETH_ALEN)) {
+ nm_log_dbg (LOGD_DEVICE | LOGD_ETHER, "(%s): no MAC address change needed",
+ iface, detail, mac_str);
+ g_free (mac_str);
+ return TRUE;
+ }
+
+ /* Can't change MAC address while device is up */
+ real_hw_take_down (dev);
+
+ success = nm_system_device_set_mac (iface, (struct ether_addr *) addr);
+ if (success) {
+ /* MAC address succesfully changed; update the current MAC to match */
+ _update_hw_addr (self, addr);
+ nm_log_info (LOGD_DEVICE | LOGD_ETHER, "(%s): %s MAC address to %s",
+ iface, detail, mac_str);
+ } else {
+ nm_log_warn (LOGD_DEVICE | LOGD_ETHER, "(%s): failed to %s MAC address to %s",
+ iface, detail, mac_str);
+ }
+ real_hw_bring_up (dev, NULL);
+ g_free (mac_str);
+
+ return success;
+}
+
+static void
real_update_hw_address (NMDevice *dev)
{
NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
- NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
struct ifreq req;
int fd;
@@ -511,27 +674,91 @@ real_update_hw_address (NMDevice *dev)
memset (&req, 0, sizeof (struct ifreq));
strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ);
+
+ errno = 0;
if (ioctl (fd, SIOCGIFHWADDR, &req) < 0) {
nm_log_err (LOGD_HW | LOGD_ETHER,
- "(%s) error getting hardware address: %d",
+ "(%s) failed to read hardware address (error %d)",
nm_device_get_iface (dev), errno);
- goto out;
+ } else
+ _update_hw_addr (self, (const guint8 *) &req.ifr_hwaddr.sa_data);
+
+ close (fd);
+}
+
+static void
+real_update_permanent_hw_address (NMDevice *dev)
+{
+ NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
+ NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+ struct ifreq req;
+ struct ethtool_perm_addr *epaddr = NULL;
+ int fd, ret;
+
+ fd = socket (PF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ nm_log_warn (LOGD_HW, "couldn't open control socket.");
+ return;
}
- if (memcmp (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr))) {
- memcpy (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr));
- g_object_notify (G_OBJECT (dev), NM_DEVICE_ETHERNET_HW_ADDRESS);
+ /* Get permanent MAC address */
+ memset (&req, 0, sizeof (struct ifreq));
+ strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ);
+
+ epaddr = g_malloc0 (sizeof (struct ethtool_perm_addr) + ETH_ALEN);
+ epaddr->cmd = ETHTOOL_GPERMADDR;
+ epaddr->size = ETH_ALEN;
+ req.ifr_data = (void *) epaddr;
+
+ errno = 0;
+ ret = ioctl (fd, SIOCETHTOOL, &req);
+ if ((ret < 0) || !nm_ethernet_address_is_valid ((struct ether_addr *) epaddr->data)) {
+ nm_log_err (LOGD_HW | LOGD_ETHER, "(%s): unable to read permanent MAC address (error %d)",
+ nm_device_get_iface (dev), errno);
+ /* Fall back to current address */
+ memcpy (epaddr->data, &priv->hw_addr, ETH_ALEN);
+ }
+
+ if (memcmp (&priv->perm_hw_addr, epaddr->data, ETH_ALEN)) {
+ memcpy (&priv->perm_hw_addr, epaddr->data, ETH_ALEN);
+ g_object_notify (G_OBJECT (dev), NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS);
}
-out:
close (fd);
}
+static void
+real_update_initial_hw_address (NMDevice *dev)
+{
+ NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
+ NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+ char *mac_str = NULL;
+ guint8 *addr = priv->initial_hw_addr;
+ guint8 zero[ETH_ALEN] = {0,0,0,0,0,0};
+
+ /* This sets initial MAC address from current MAC address. It should only
+ * be called from NMDevice constructor() to really get the initial address.
+ */
+ if (!memcmp (&priv->hw_addr, &zero, ETH_ALEN))
+ real_update_hw_address (dev);
+
+ if (memcmp (&priv->initial_hw_addr, &priv->hw_addr, ETH_ALEN))
+ memcpy (&priv->initial_hw_addr, &priv->hw_addr, ETH_ALEN);
+
+ mac_str = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+
+ nm_log_dbg (LOGD_DEVICE | LOGD_ETHER, "(%s): read initial MAC address %s",
+ nm_device_get_iface (dev), mac_str);
+
+ g_free (mac_str);
+}
+
static guint32
real_get_generic_capabilities (NMDevice *dev)
{
- NMDeviceEthernet * self = NM_DEVICE_ETHERNET (dev);
- guint32 caps = NM_DEVICE_CAP_NONE;
+ NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
+ guint32 caps = NM_DEVICE_CAP_NONE;
/* cipsec devices are also explicitly unsupported at this time */
if (strstr (nm_device_get_iface (dev), "cipsec"))
@@ -573,6 +800,39 @@ real_is_available (NMDevice *dev)
return TRUE;
}
+static gboolean
+match_subchans (NMDeviceEthernet *self, NMSettingWired *s_wired, gboolean *try_mac)
+{
+ NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+ const GPtrArray *subchans;
+ int i;
+
+ *try_mac = TRUE;
+
+ subchans = nm_setting_wired_get_s390_subchannels (s_wired);
+ if (!subchans)
+ return TRUE;
+
+ /* connection requires subchannels but the device has none */
+ if (!priv->subchannels)
+ return FALSE;
+
+ /* Make sure each subchannel in the connection is a subchannel of this device */
+ for (i = 0; i < subchans->len; i++) {
+ const char *candidate = g_ptr_array_index (subchans, i);
+
+ if ( (priv->subchan1 && !strcmp (priv->subchan1, candidate))
+ || (priv->subchan2 && !strcmp (priv->subchan2, candidate))
+ || (priv->subchan3 && !strcmp (priv->subchan3, candidate)))
+ continue;
+
+ return FALSE; /* a subchannel was not found */
+ }
+
+ *try_mac = FALSE;
+ return TRUE;
+}
+
static NMConnection *
real_get_best_auto_connection (NMDevice *dev,
GSList *connections,
@@ -608,9 +868,13 @@ real_get_best_auto_connection (NMDevice *dev,
if (s_wired) {
const GByteArray *mac;
+ gboolean try_mac = TRUE;
+
+ if (!match_subchans (self, s_wired, &try_mac))
+ continue;
mac = nm_setting_wired_get_mac_address (s_wired);
- if (mac && memcmp (mac->data, priv->hw_addr.ether_addr_octet, ETH_ALEN))
+ if (try_mac && mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN))
continue;
}
@@ -720,30 +984,6 @@ remove_supplicant_timeouts (NMDeviceEthernet *self)
}
static void
-finish_supplicant_task (SupplicantStateTask *task, gboolean remove_source)
-{
- NMDeviceEthernet *self = task->self;
- NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
-
- /* idle/timeout handlers should pass FALSE for remove_source, since they
- * will tell glib to remove their source from the mainloop by returning
- * FALSE when they exit. When called from this NMDevice's dispose handler,
- * remove_source should be TRUE to cancel all outstanding idle/timeout
- * handlers asynchronously.
- */
- if (task->source_id && remove_source)
- g_source_remove (task->source_id);
-
- if (task->mgr_task)
- priv->supplicant.mgr_tasks = g_slist_remove (priv->supplicant.mgr_tasks, task);
- else
- priv->supplicant.iface_tasks = g_slist_remove (priv->supplicant.iface_tasks, task);
-
- memset (task, 0, sizeof (SupplicantStateTask));
- g_slice_free (SupplicantStateTask, task);
-}
-
-static void
remove_supplicant_interface_error_handler (NMDeviceEthernet *self)
{
NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
@@ -767,28 +1007,14 @@ supplicant_interface_release (NMDeviceEthernet *self)
remove_supplicant_timeouts (self);
remove_supplicant_interface_error_handler (self);
- /* Clean up all pending supplicant interface state idle tasks */
- while (priv->supplicant.iface_tasks)
- finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.iface_tasks->data, TRUE);
-
- if (priv->supplicant.iface_con_state_id) {
- g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_con_state_id);
- priv->supplicant.iface_con_state_id = 0;
- }
-
if (priv->supplicant.iface_state_id > 0) {
g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_state_id);
priv->supplicant.iface_state_id = 0;
}
- if (priv->supplicant.mgr_state_id) {
- g_signal_handler_disconnect (priv->supplicant.mgr, priv->supplicant.mgr_state_id);
- priv->supplicant.mgr_state_id = 0;
- }
-
if (priv->supplicant.iface) {
nm_supplicant_interface_disconnect (priv->supplicant.iface);
- nm_supplicant_manager_release_iface (priv->supplicant.mgr, priv->supplicant.iface);
+ nm_supplicant_manager_iface_release (priv->supplicant.mgr, priv->supplicant.iface);
priv->supplicant.iface = NULL;
}
}
@@ -850,77 +1076,6 @@ time_out:
return FALSE;
}
-static gboolean
-schedule_state_handler (NMDeviceEthernet *self,
- GSourceFunc handler,
- guint32 new_state,
- guint32 old_state,
- gboolean mgr_task)
-{
- NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
- SupplicantStateTask *task;
-
- if (new_state == old_state)
- return TRUE;
-
- task = g_slice_new0 (SupplicantStateTask);
- if (!task) {
- nm_log_err (LOGD_DEVICE, "Not enough memory to process supplicant manager state change.");
- return FALSE;
- }
-
- task->self = self;
- task->new_state = new_state;
- task->old_state = old_state;
- task->mgr_task = mgr_task;
-
- task->source_id = g_idle_add (handler, task);
- if (mgr_task)
- priv->supplicant.mgr_tasks = g_slist_append (priv->supplicant.mgr_tasks, task);
- else
- priv->supplicant.iface_tasks = g_slist_append (priv->supplicant.iface_tasks, task);
- return TRUE;
-}
-
-static gboolean
-supplicant_mgr_state_cb_handler (gpointer user_data)
-{
- SupplicantStateTask *task = (SupplicantStateTask *) user_data;
- NMDevice *device = NM_DEVICE (task->self);
-
- /* If the supplicant went away, release the supplicant interface */
- if (task->new_state == NM_SUPPLICANT_MANAGER_STATE_DOWN) {
- supplicant_interface_release (task->self);
-
- if (nm_device_get_state (device) > NM_DEVICE_STATE_UNAVAILABLE) {
- nm_device_state_changed (device, NM_DEVICE_STATE_UNAVAILABLE,
- NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
- }
- }
-
- finish_supplicant_task (task, FALSE);
- return FALSE;
-}
-
-static void
-supplicant_mgr_state_cb (NMSupplicantInterface * iface,
- guint32 new_state,
- guint32 old_state,
- gpointer user_data)
-{
- nm_log_info (LOGD_DEVICE | LOGD_ETHER,
- "(%s): supplicant manager state: %s -> %s",
- nm_device_get_iface (NM_DEVICE (user_data)),
- nm_supplicant_manager_state_to_string (old_state),
- nm_supplicant_manager_state_to_string (new_state));
-
- schedule_state_handler (NM_DEVICE_ETHERNET (user_data),
- supplicant_mgr_state_cb_handler,
- new_state,
- old_state,
- TRUE);
-}
-
static NMSupplicantConfig *
build_supplicant_config (NMDeviceEthernet *self)
{
@@ -947,20 +1102,33 @@ build_supplicant_config (NMDeviceEthernet *self)
return config;
}
-static gboolean
-supplicant_iface_state_cb_handler (gpointer user_data)
+static void
+supplicant_iface_state_cb (NMSupplicantInterface *iface,
+ guint32 new_state,
+ guint32 old_state,
+ gpointer user_data)
{
- SupplicantStateTask *task = (SupplicantStateTask *) user_data;
- NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (task->self);
- NMDevice *device = NM_DEVICE (task->self);
+ NMDeviceEthernet *self = NM_DEVICE_ETHERNET (user_data);
+ NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
+ NMDevice *device = NM_DEVICE (self);
+ NMSupplicantConfig *config;
+ gboolean success = FALSE;
+ NMDeviceState devstate;
- if (task->new_state == NM_SUPPLICANT_INTERFACE_STATE_READY) {
- NMSupplicantConfig *config;
- const char *iface;
- gboolean success = FALSE;
+ if (new_state == old_state)
+ return;
- iface = nm_device_get_iface (device);
- config = build_supplicant_config (task->self);
+ nm_log_info (LOGD_DEVICE | LOGD_ETHER,
+ "(%s): supplicant interface state: %s -> %s",
+ nm_device_get_iface (device),
+ nm_supplicant_interface_state_to_string (old_state),
+ nm_supplicant_interface_state_to_string (new_state));
+
+ devstate = nm_device_get_state (device);
+
+ switch (new_state) {
+ case NM_SUPPLICANT_INTERFACE_STATE_READY:
+ config = build_supplicant_config (self);
if (config) {
success = nm_supplicant_interface_set_config (priv->supplicant.iface, config);
g_object_unref (config);
@@ -969,99 +1137,54 @@ supplicant_iface_state_cb_handler (gpointer user_data)
nm_log_err (LOGD_DEVICE | LOGD_ETHER,
"Activation (%s/wired): couldn't send security "
"configuration to the supplicant.",
- iface);
+ nm_device_get_iface (device));
}
} else {
nm_log_warn (LOGD_DEVICE | LOGD_ETHER,
"Activation (%s/wired): couldn't build security configuration.",
- iface);
+ nm_device_get_iface (device));
}
- if (!success)
- nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
- } else if (task->new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) {
- NMDeviceState state = nm_device_get_state (device);
-
- supplicant_interface_release (task->self);
-
- if (nm_device_is_activating (device) || state == NM_DEVICE_STATE_ACTIVATED)
- nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
- }
-
- finish_supplicant_task (task, FALSE);
- return FALSE;
-}
-
-static void
-supplicant_iface_state_cb (NMSupplicantInterface * iface,
- guint32 new_state,
- guint32 old_state,
- gpointer user_data)
-{
-
- nm_log_info (LOGD_DEVICE | LOGD_ETHER,
- "(%s): supplicant interface state: %s -> %s",
- nm_device_get_iface (NM_DEVICE (user_data)),
- nm_supplicant_interface_state_to_string (old_state),
- nm_supplicant_interface_state_to_string (new_state));
-
- schedule_state_handler (NM_DEVICE_ETHERNET (user_data),
- supplicant_iface_state_cb_handler,
- new_state,
- old_state,
- FALSE);
-}
-
-static gboolean
-supplicant_iface_connection_state_cb_handler (gpointer user_data)
-{
- SupplicantStateTask *task = (SupplicantStateTask *) user_data;
- NMDevice *dev = NM_DEVICE (task->self);
-
- if (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED) {
- remove_supplicant_interface_error_handler (task->self);
- remove_supplicant_timeouts (task->self);
+ if (!success) {
+ nm_device_state_changed (device,
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_SUPPLICANT_CONFIG_FAILED);
+ }
+ break;
+ case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED:
+ remove_supplicant_interface_error_handler (self);
+ remove_supplicant_timeouts (self);
/* If this is the initial association during device activation,
* schedule the next activation stage.
*/
- if (nm_device_get_state (dev) == NM_DEVICE_STATE_CONFIG) {
+ if (devstate == NM_DEVICE_STATE_CONFIG) {
nm_log_info (LOGD_DEVICE | LOGD_ETHER,
"Activation (%s/wired) Stage 2 of 5 (Device Configure) successful.",
- nm_device_get_iface (dev));
- nm_device_activate_schedule_stage3_ip_config_start (dev);
+ nm_device_get_iface (device));
+ nm_device_activate_schedule_stage3_ip_config_start (device);
}
- } else if (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED) {
- if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED || nm_device_is_activating (dev)) {
- NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (task->self);
-
+ break;
+ case NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED:
+ if ((devstate == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (device)) {
/* Start the link timeout so we allow some time for reauthentication */
if (!priv->supplicant_timeout_id)
- priv->supplicant_timeout_id = g_timeout_add_seconds (15, link_timeout_cb, dev);
+ priv->supplicant_timeout_id = g_timeout_add_seconds (15, link_timeout_cb, device);
}
- }
-
- finish_supplicant_task (task, FALSE);
- return FALSE;
-}
-
-static void
-supplicant_iface_connection_state_cb (NMSupplicantInterface * iface,
- guint32 new_state,
- guint32 old_state,
- gpointer user_data)
-{
- nm_log_info (LOGD_DEVICE | LOGD_ETHER,
- "(%s) supplicant connection state: %s -> %s",
- nm_device_get_iface (NM_DEVICE (user_data)),
- nm_supplicant_interface_connection_state_to_string (old_state),
- nm_supplicant_interface_connection_state_to_string (new_state));
+ break;
+ case NM_SUPPLICANT_INTERFACE_STATE_DOWN:
+ supplicant_interface_release (self);
+ remove_supplicant_timeouts (self);
- schedule_state_handler (NM_DEVICE_ETHERNET (user_data),
- supplicant_iface_connection_state_cb_handler,
- new_state,
- old_state,
- FALSE);
+ if ((devstate == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (device)) {
+ nm_device_state_changed (device,
+ NM_DEVICE_STATE_FAILED,
+ NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+ }
+ break;
+ default:
+ break;
+ }
}
static gboolean
@@ -1179,38 +1302,26 @@ supplicant_interface_init (NMDeviceEthernet *self)
iface = nm_device_get_iface (NM_DEVICE (self));
/* Create supplicant interface */
- priv->supplicant.iface = nm_supplicant_manager_get_iface (priv->supplicant.mgr, iface, FALSE);
+ priv->supplicant.iface = nm_supplicant_manager_iface_get (priv->supplicant.mgr, iface, FALSE);
if (!priv->supplicant.iface) {
nm_log_err (LOGD_DEVICE | LOGD_ETHER,
"Couldn't initialize supplicant interface for %s.",
iface);
supplicant_interface_release (self);
-
return FALSE;
}
/* Listen for it's state signals */
priv->supplicant.iface_state_id = g_signal_connect (priv->supplicant.iface,
- "state",
- G_CALLBACK (supplicant_iface_state_cb),
- self);
+ "state",
+ G_CALLBACK (supplicant_iface_state_cb),
+ self);
/* Hook up error signal handler to capture association errors */
priv->supplicant.iface_error_id = g_signal_connect (priv->supplicant.iface,
- "connection-error",
- G_CALLBACK (supplicant_iface_connection_error_cb),
- self);
-
- priv->supplicant.iface_con_state_id = g_signal_connect (priv->supplicant.iface,
- "connection-state",
- G_CALLBACK (supplicant_iface_connection_state_cb),
- self);
-
- /* Listen for supplicant manager state changes */
- priv->supplicant.mgr_state_id = g_signal_connect (priv->supplicant.mgr,
- "state",
- G_CALLBACK (supplicant_mgr_state_cb),
- self);
+ "connection-error",
+ G_CALLBACK (supplicant_iface_connection_error_cb),
+ self);
/* Set up a timeout on the connection attempt to fail it after 25 seconds */
priv->supplicant.con_timeout_id = g_timeout_add_seconds (25, supplicant_connection_timeout_cb, self);
@@ -1219,6 +1330,31 @@ supplicant_interface_init (NMDeviceEthernet *self)
}
static NMActStageReturn
+real_act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
+{
+ NMDeviceEthernet *self = NM_DEVICE_ETHERNET (dev);
+ NMActRequest *req;
+ NMSettingWired *s_wired;
+ const GByteArray *cloned_mac;
+ NMActStageReturn ret = NM_ACT_STAGE_RETURN_SUCCESS;
+
+ g_return_val_if_fail (reason != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+ req = nm_device_get_act_request (NM_DEVICE (self));
+ g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+ s_wired = NM_SETTING_WIRED (device_get_setting (dev, NM_TYPE_SETTING_WIRED));
+ g_assert (s_wired);
+
+ /* Set device MAC address if the connection wants to change it */
+ cloned_mac = nm_setting_wired_get_cloned_mac_address (s_wired);
+ if (cloned_mac && (cloned_mac->len == ETH_ALEN))
+ _set_hw_addr (self, (const guint8 *) cloned_mac->data, "set");
+
+ return ret;
+}
+
+static NMActStageReturn
nm_8021x_stage2_config (NMDeviceEthernet *self, NMDeviceStateReason *reason)
{
NMConnection *connection;
@@ -1446,9 +1582,8 @@ real_act_stage4_get_ip4_config (NMDevice *device,
static void
real_deactivate_quickly (NMDevice *device)
{
- NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
-
- nm_device_set_ip_iface (device, NULL);
+ NMDeviceEthernet *self = NM_DEVICE_ETHERNET (device);
+ NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
if (priv->pending_ip4_config) {
g_object_unref (priv->pending_ip4_config);
@@ -1460,7 +1595,10 @@ real_deactivate_quickly (NMDevice *device)
priv->ppp_manager = NULL;
}
- supplicant_interface_release (NM_DEVICE_ETHERNET (device));
+ supplicant_interface_release (self);
+
+ /* Reset MAC address back to initial address */
+ _set_hw_addr (self, priv->initial_hw_addr, "reset");
}
static gboolean
@@ -1474,6 +1612,8 @@ real_check_connection_compatible (NMDevice *device,
NMSettingWired *s_wired;
const char *connection_type;
gboolean is_pppoe = FALSE;
+ const GByteArray *mac;
+ gboolean try_mac = TRUE;
s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION));
g_assert (s_con);
@@ -1500,10 +1640,15 @@ real_check_connection_compatible (NMDevice *device,
}
if (s_wired) {
- const GByteArray *mac;
+ if (!match_subchans (self, s_wired, &try_mac)) {
+ g_set_error (error,
+ NM_ETHERNET_ERROR, NM_ETHERNET_ERROR_CONNECTION_INCOMPATIBLE,
+ "The connection's s390 subchannels did not match this device.");
+ return FALSE;
+ }
mac = nm_setting_wired_get_mac_address (s_wired);
- if (mac && memcmp (mac->data, &(priv->hw_addr.ether_addr_octet), ETH_ALEN)) {
+ if (try_mac && mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN)) {
g_set_error (error,
NM_ETHERNET_ERROR, NM_ETHERNET_ERROR_CONNECTION_INCOMPATIBLE,
"The connection's MAC address did not match this device.");
@@ -1519,37 +1664,39 @@ real_check_connection_compatible (NMDevice *device,
static gboolean
spec_match_list (NMDevice *device, const GSList *specs)
{
- struct ether_addr ether;
+ NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (device);
char *hwaddr;
gboolean matched;
- nm_device_ethernet_get_address (NM_DEVICE_ETHERNET (device), &ether);
- hwaddr = nm_ether_ntop (&ether);
+ hwaddr = nm_ether_ntop ((struct ether_addr *) &priv->perm_hw_addr);
matched = nm_match_spec_hwaddr (specs, hwaddr);
g_free (hwaddr);
+ if (!matched && priv->subchannels)
+ matched = nm_match_spec_s390_subchannels (specs, priv->subchannels);
+
return matched;
}
static gboolean
wired_match_config (NMDevice *self, NMConnection *connection)
{
+ NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
NMSettingWired *s_wired;
- struct ether_addr ether;
const GByteArray *s_ether;
+ gboolean try_mac = TRUE;
s_wired = (NMSettingWired *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRED);
if (!s_wired)
return FALSE;
+ if (!match_subchans (NM_DEVICE_ETHERNET (self), s_wired, &try_mac))
+ return FALSE;
+
/* MAC address check */
s_ether = nm_setting_wired_get_mac_address (s_wired);
- if (s_ether) {
- nm_device_ethernet_get_address (NM_DEVICE_ETHERNET (self), &ether);
-
- if (memcmp (s_ether->data, ether.ether_addr_octet, ETH_ALEN))
- return FALSE;
- }
+ if (try_mac && s_ether && memcmp (s_ether->data, priv->perm_hw_addr, ETH_ALEN))
+ return FALSE;
return TRUE;
}
@@ -1734,12 +1881,6 @@ dispose (GObject *object)
priv->disposed = TRUE;
- /* Clean up all pending supplicant tasks */
- while (priv->supplicant.iface_tasks)
- finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.iface_tasks->data, TRUE);
- while (priv->supplicant.mgr_tasks)
- finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.mgr_tasks->data, TRUE);
-
if (priv->link_connected_id) {
g_signal_handler_disconnect (priv->monitor, priv->link_connected_id);
priv->link_connected_id = 0;
@@ -1756,6 +1897,11 @@ dispose (GObject *object)
priv->monitor = NULL;
}
+ g_free (priv->subchan1);
+ g_free (priv->subchan2);
+ g_free (priv->subchan3);
+ g_free (priv->subchannels);
+
G_OBJECT_CLASS (nm_device_ethernet_parent_class)->dispose (object);
}
@@ -1765,12 +1911,13 @@ get_property (GObject *object, guint prop_id,
{
NMDeviceEthernet *self = NM_DEVICE_ETHERNET (object);
NMDeviceEthernetPrivate *priv = NM_DEVICE_ETHERNET_GET_PRIVATE (self);
- struct ether_addr hw_addr;
switch (prop_id) {
case PROP_HW_ADDRESS:
- nm_device_ethernet_get_address (self, &hw_addr);
- g_value_take_string (value, nm_ether_ntop (&hw_addr));
+ g_value_take_string (value, nm_ether_ntop ((struct ether_addr *) &priv->hw_addr));
+ break;
+ case PROP_PERM_HW_ADDRESS:
+ g_value_take_string (value, nm_ether_ntop ((struct ether_addr *) &priv->perm_hw_addr));
break;
case PROP_SPEED:
g_value_set_uint (value, nm_device_ethernet_get_speed (self));
@@ -1818,11 +1965,14 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
parent_class->take_down = real_take_down;
parent_class->can_interrupt_activation = real_can_interrupt_activation;
parent_class->update_hw_address = real_update_hw_address;
+ parent_class->update_permanent_hw_address = real_update_permanent_hw_address;
+ parent_class->update_initial_hw_address = real_update_initial_hw_address;
parent_class->get_best_auto_connection = real_get_best_auto_connection;
parent_class->is_available = real_is_available;
parent_class->connection_secrets_updated = real_connection_secrets_updated;
parent_class->check_connection_compatible = real_check_connection_compatible;
+ parent_class->act_stage1_prepare = real_act_stage1_prepare;
parent_class->act_stage2_config = real_act_stage2_config;
parent_class->act_stage3_ip4_config_start = real_act_stage3_ip4_config_start;
parent_class->act_stage4_get_ip4_config = real_act_stage4_get_ip4_config;
@@ -1834,8 +1984,16 @@ nm_device_ethernet_class_init (NMDeviceEthernetClass *klass)
g_object_class_install_property
(object_class, PROP_HW_ADDRESS,
g_param_spec_string (NM_DEVICE_ETHERNET_HW_ADDRESS,
- "MAC Address",
- "Hardware MAC address",
+ "Active MAC Address",
+ "Currently set hardware MAC address",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property
+ (object_class, PROP_PERM_HW_ADDRESS,
+ g_param_spec_string (NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS,
+ "Permanent MAC Address",
+ "Permanent hardware MAC address",
NULL,
G_PARAM_READABLE));
diff --git a/src/nm-device-ethernet.h b/src/nm-device-ethernet.h
index 7bb3db0086..b9e2afd402 100644
--- a/src/nm-device-ethernet.h
+++ b/src/nm-device-ethernet.h
@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2005 - 2008 Red Hat, Inc.
+ * Copyright (C) 2005 - 2010 Red Hat, Inc.
* Copyright (C) 2006 - 2008 Novell, Inc.
*/
@@ -37,6 +37,7 @@ G_BEGIN_DECLS
#define NM_DEVICE_ETHERNET_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DEVICE_ETHERNET, NMDeviceEthernetClass))
#define NM_DEVICE_ETHERNET_HW_ADDRESS "hw-address"
+#define NM_DEVICE_ETHERNET_PERMANENT_HW_ADDRESS "perm-hw-address"
#define NM_DEVICE_ETHERNET_SPEED "speed"
#define NM_DEVICE_ETHERNET_CARRIER "carrier"
diff --git a/src/nm-device-interface.c b/src/nm-device-interface.c
index 85f58d5563..eb0e009e20 100644
--- a/src/nm-device-interface.c
+++ b/src/nm-device-interface.c
@@ -19,6 +19,8 @@
* Copyright (C) 2007 - 2010 Red Hat, Inc.
*/
+#include <dbus/dbus-glib.h>
+
#include "nm-marshal.h"
#include "nm-setting-connection.h"
#include "nm-device-interface.h"
@@ -26,8 +28,8 @@
#include "nm-properties-changed-signal.h"
#include "nm-rfkill.h"
-static gboolean impl_device_disconnect (NMDeviceInterface *device,
- GError **error);
+static void impl_device_disconnect (NMDeviceInterface *device,
+ DBusGMethodInvocation *context);
#include "nm-device-interface-glue.h"
@@ -88,7 +90,15 @@ nm_device_interface_init (gpointer g_iface)
"Interface",
"Interface",
NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_interface_install_property
+ (g_iface,
+ g_param_spec_string (NM_DEVICE_INTERFACE_IP_IFACE,
+ "IP Interface",
+ "IP Interface",
+ NULL,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_interface_install_property
(g_iface,
@@ -170,6 +180,13 @@ nm_device_interface_init (gpointer g_iface)
G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_interface_install_property
+ (g_iface, g_param_spec_boolean (NM_DEVICE_INTERFACE_FIRMWARE_MISSING,
+ "FirmwareMissing",
+ "Firmware missing",
+ FALSE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_interface_install_property
(g_iface,
g_param_spec_string (NM_DEVICE_INTERFACE_TYPE_DESC,
"Type Description",
@@ -204,6 +221,13 @@ nm_device_interface_init (gpointer g_iface)
G_TYPE_NONE, 3,
G_TYPE_UINT, G_TYPE_UINT, G_TYPE_UINT);
+ g_signal_new (NM_DEVICE_INTERFACE_DISCONNECT_REQUEST,
+ iface_type,
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+
dbus_g_object_type_install_info (iface_type,
&dbus_glib_nm_device_interface_object_info);
@@ -327,11 +351,11 @@ nm_device_interface_disconnect (NMDeviceInterface *device,
return success;
}
-static gboolean
+static void
impl_device_disconnect (NMDeviceInterface *device,
- GError **error)
+ DBusGMethodInvocation *context)
{
- return nm_device_interface_disconnect (device, error);
+ g_signal_emit_by_name (device, NM_DEVICE_INTERFACE_DISCONNECT_REQUEST, context);
}
void
@@ -374,11 +398,13 @@ nm_device_interface_connection_match_config (NMDeviceInterface *device,
}
gboolean
-nm_device_interface_can_assume_connection (NMDeviceInterface *device)
+nm_device_interface_can_assume_connections (NMDeviceInterface *device)
{
g_return_val_if_fail (NM_IS_DEVICE_INTERFACE (device), FALSE);
- return !!NM_DEVICE_INTERFACE_GET_INTERFACE (device)->connection_match_config;
+ if (NM_DEVICE_INTERFACE_GET_INTERFACE (device)->can_assume_connections)
+ return NM_DEVICE_INTERFACE_GET_INTERFACE (device)->can_assume_connections (device);
+ return FALSE;
}
void
diff --git a/src/nm-device-interface.h b/src/nm-device-interface.h
index ec27f6e820..f39e8459db 100644
--- a/src/nm-device-interface.h
+++ b/src/nm-device-interface.h
@@ -16,7 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2007 - 2008 Novell, Inc.
- * Copyright (C) 2007 - 2008 Red Hat, Inc.
+ * Copyright (C) 2007 - 2010 Red Hat, Inc.
*/
#ifndef NM_DEVICE_INTERFACE_H
@@ -45,27 +45,32 @@ typedef enum
#define NM_DEVICE_INTERFACE_ERROR (nm_device_interface_error_quark ())
#define NM_TYPE_DEVICE_INTERFACE_ERROR (nm_device_interface_error_get_type ())
-#define NM_DEVICE_INTERFACE_UDI "udi"
-#define NM_DEVICE_INTERFACE_IFACE "interface"
-#define NM_DEVICE_INTERFACE_DRIVER "driver"
-#define NM_DEVICE_INTERFACE_CAPABILITIES "capabilities"
-#define NM_DEVICE_INTERFACE_IP4_ADDRESS "ip4-address"
-#define NM_DEVICE_INTERFACE_IP4_CONFIG "ip4-config"
-#define NM_DEVICE_INTERFACE_DHCP4_CONFIG "dhcp4-config"
-#define NM_DEVICE_INTERFACE_IP6_CONFIG "ip6-config"
-#define NM_DEVICE_INTERFACE_DHCP6_CONFIG "dhcp6-config"
-#define NM_DEVICE_INTERFACE_STATE "state"
-#define NM_DEVICE_INTERFACE_DEVICE_TYPE "device-type" /* ugh */
-#define NM_DEVICE_INTERFACE_MANAGED "managed"
-#define NM_DEVICE_INTERFACE_TYPE_DESC "type-desc" /* Internal only */
-#define NM_DEVICE_INTERFACE_RFKILL_TYPE "rfkill-type" /* Internal only */
-#define NM_DEVICE_INTERFACE_IFINDEX "ifindex" /* Internal only */
+#define NM_DEVICE_INTERFACE_DISCONNECT_REQUEST "disconnect-request"
+
+#define NM_DEVICE_INTERFACE_UDI "udi"
+#define NM_DEVICE_INTERFACE_IFACE "interface"
+#define NM_DEVICE_INTERFACE_IP_IFACE "ip-interface"
+#define NM_DEVICE_INTERFACE_DRIVER "driver"
+#define NM_DEVICE_INTERFACE_CAPABILITIES "capabilities"
+#define NM_DEVICE_INTERFACE_IP4_ADDRESS "ip4-address"
+#define NM_DEVICE_INTERFACE_IP4_CONFIG "ip4-config"
+#define NM_DEVICE_INTERFACE_DHCP4_CONFIG "dhcp4-config"
+#define NM_DEVICE_INTERFACE_IP6_CONFIG "ip6-config"
+#define NM_DEVICE_INTERFACE_DHCP6_CONFIG "dhcp6-config"
+#define NM_DEVICE_INTERFACE_STATE "state"
+#define NM_DEVICE_INTERFACE_DEVICE_TYPE "device-type" /* ugh */
+#define NM_DEVICE_INTERFACE_MANAGED "managed"
+#define NM_DEVICE_INTERFACE_FIRMWARE_MISSING "firmware-missing"
+#define NM_DEVICE_INTERFACE_TYPE_DESC "type-desc" /* Internal only */
+#define NM_DEVICE_INTERFACE_RFKILL_TYPE "rfkill-type" /* Internal only */
+#define NM_DEVICE_INTERFACE_IFINDEX "ifindex" /* Internal only */
typedef enum {
NM_DEVICE_INTERFACE_PROP_FIRST = 0x1000,
NM_DEVICE_INTERFACE_PROP_UDI = NM_DEVICE_INTERFACE_PROP_FIRST,
NM_DEVICE_INTERFACE_PROP_IFACE,
+ NM_DEVICE_INTERFACE_PROP_IP_IFACE,
NM_DEVICE_INTERFACE_PROP_DRIVER,
NM_DEVICE_INTERFACE_PROP_CAPABILITIES,
NM_DEVICE_INTERFACE_PROP_IP4_ADDRESS,
@@ -76,6 +81,7 @@ typedef enum {
NM_DEVICE_INTERFACE_PROP_STATE,
NM_DEVICE_INTERFACE_PROP_DEVICE_TYPE,
NM_DEVICE_INTERFACE_PROP_MANAGED,
+ NM_DEVICE_INTERFACE_PROP_FIRMWARE_MISSING,
NM_DEVICE_INTERFACE_PROP_TYPE_DESC,
NM_DEVICE_INTERFACE_PROP_RFKILL_TYPE,
NM_DEVICE_INTERFACE_PROP_IFINDEX,
@@ -103,6 +109,8 @@ struct _NMDeviceInterface {
NMConnection * (*connection_match_config) (NMDeviceInterface *device, const GSList *specs);
+ gboolean (*can_assume_connections) (NMDeviceInterface *device);
+
void (*set_enabled) (NMDeviceInterface *device, gboolean enabled);
gboolean (*get_enabled) (NMDeviceInterface *device);
@@ -139,7 +147,7 @@ gboolean nm_device_interface_spec_match_list (NMDeviceInterface *device,
NMConnection * nm_device_interface_connection_match_config (NMDeviceInterface *device,
const GSList *connections);
-gboolean nm_device_interface_can_assume_connection (NMDeviceInterface *device);
+gboolean nm_device_interface_can_assume_connections (NMDeviceInterface *device);
gboolean nm_device_interface_get_enabled (NMDeviceInterface *device);
diff --git a/src/nm-device-modem.c b/src/nm-device-modem.c
index 3ba26ddcdd..441f92cece 100644
--- a/src/nm-device-modem.c
+++ b/src/nm-device-modem.c
@@ -46,6 +46,7 @@ enum {
enum {
PPP_STATS,
+ ENABLE_CHANGED,
LAST_SIGNAL
};
static guint signals[LAST_SIGNAL] = { 0 };
@@ -158,6 +159,8 @@ modem_enabled_cb (NMModem *modem, GParamSpec *pspec, gpointer user_data)
NMDeviceModemPrivate *priv = NM_DEVICE_MODEM_GET_PRIVATE (self);
real_set_enabled (NM_DEVICE_INTERFACE (self), nm_modem_get_mm_enabled (priv->modem));
+
+ g_signal_emit (G_OBJECT (self), signals[ENABLE_CHANGED], 0);
}
/*****************************************************************************/
@@ -453,6 +456,15 @@ nm_device_modem_class_init (NMDeviceModemClass *mclass)
G_TYPE_NONE, 2,
G_TYPE_UINT, G_TYPE_UINT);
+ signals[ENABLE_CHANGED] =
+ g_signal_new (NM_DEVICE_MODEM_ENABLE_CHANGED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0,
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
dbus_g_object_type_install_info (G_TYPE_FROM_CLASS (mclass),
nm_modem_get_serial_dbus_info ());
}
diff --git a/src/nm-device-modem.h b/src/nm-device-modem.h
index 40e89d4b7c..c6ef4d2184 100644
--- a/src/nm-device-modem.h
+++ b/src/nm-device-modem.h
@@ -36,6 +36,8 @@
#define NM_DEVICE_MODEM_MODEM "modem"
+#define NM_DEVICE_MODEM_ENABLE_CHANGED "enable-changed"
+
typedef struct {
NMDevice parent;
} NMDeviceModem;
diff --git a/src/nm-device-private.h b/src/nm-device-private.h
index 371f17f10d..f4f968a949 100644
--- a/src/nm-device-private.h
+++ b/src/nm-device-private.h
@@ -42,4 +42,8 @@ void nm_device_handle_autoip4_event (NMDevice *self,
gboolean nm_device_ip_config_should_fail (NMDevice *self, gboolean ip6);
+gboolean nm_device_get_firmware_missing (NMDevice *self);
+
+void nm_device_set_firmware_missing (NMDevice *self, gboolean missing);
+
#endif /* NM_DEVICE_PRIVATE_H */
diff --git a/src/nm-device-wifi.c b/src/nm-device-wifi.c
index 1fa2937253..38631d7552 100644
--- a/src/nm-device-wifi.c
+++ b/src/nm-device-wifi.c
@@ -30,6 +30,9 @@
#include <sys/wait.h>
#include <signal.h>
#include <unistd.h>
+#include <linux/sockios.h>
+#include <linux/ethtool.h>
+#include <sys/ioctl.h>
#include "nm-glib-compat.h"
#include "nm-device.h"
@@ -78,6 +81,7 @@ G_DEFINE_TYPE_EXTENDED (NMDeviceWifi, nm_device_wifi, NM_TYPE_DEVICE, 0,
enum {
PROP_0,
PROP_HW_ADDRESS,
+ PROP_PERM_HW_ADDRESS,
PROP_MODE,
PROP_BITRATE,
PROP_ACTIVE_ACCESS_POINT,
@@ -109,40 +113,26 @@ typedef enum {
#define NM_WIFI_ERROR (nm_wifi_error_quark ())
#define NM_TYPE_WIFI_ERROR (nm_wifi_error_get_type ())
-typedef struct SupplicantStateTask {
- NMDeviceWifi *self;
- guint32 new_state;
- guint32 old_state;
- gboolean mgr_task;
- guint source_id;
-} SupplicantStateTask;
+#define SUP_SIG_ID_LEN 5
typedef struct Supplicant {
NMSupplicantManager *mgr;
NMSupplicantInterface *iface;
- /* signal handler ids */
- guint mgr_state_id;
+ guint sig_ids[SUP_SIG_ID_LEN];
guint iface_error_id;
- guint iface_state_id;
- guint iface_scanned_ap_id;
- guint iface_scan_request_result_id;
- guint iface_scan_results_id;
- guint iface_con_state_id;
- guint iface_notify_scanning_id;
/* Timeouts and idles */
guint iface_con_error_cb_id;
guint con_timeout_id;
-
- GSList *mgr_tasks;
- GSList *iface_tasks;
} Supplicant;
struct _NMDeviceWifiPrivate {
gboolean disposed;
- struct ether_addr hw_addr;
+ guint8 hw_addr[ETH_ALEN]; /* Currently set MAC address */
+ guint8 perm_hw_addr[ETH_ALEN]; /* Permanent MAC address */
+ guint8 initial_hw_addr[ETH_ALEN]; /* Initial MAC address (as seen when NM starts) */
/* Legacy rfkill for ipw2x00; will be fixed with 2.6.33 kernel */
char * ipw_rfkill_path;
@@ -185,40 +175,23 @@ static void schedule_scan (NMDeviceWifi *self, gboolean backoff);
static void cancel_pending_scan (NMDeviceWifi *self);
-static int wireless_qual_to_percent (const struct iw_quality *qual,
- const struct iw_quality *max_qual);
-
static void cleanup_association_attempt (NMDeviceWifi * self,
gboolean disconnect);
static void remove_supplicant_timeouts (NMDeviceWifi *self);
-static void supplicant_iface_state_cb (NMSupplicantInterface * iface,
+static void supplicant_iface_state_cb (NMSupplicantInterface *iface,
guint32 new_state,
guint32 old_state,
- NMDeviceWifi *self);
-
-static void supplicant_iface_connection_state_cb (NMSupplicantInterface * iface,
- guint32 new_state,
- guint32 old_state,
- NMDeviceWifi *self);
+ gpointer user_data);
-static void supplicant_iface_scanned_ap_cb (NMSupplicantInterface * iface,
- GHashTable *properties,
- NMDeviceWifi * self);
+static void supplicant_iface_new_bss_cb (NMSupplicantInterface * iface,
+ GHashTable *properties,
+ NMDeviceWifi * self);
-static void supplicant_iface_scan_request_result_cb (NMSupplicantInterface * iface,
- gboolean success,
- NMDeviceWifi * self);
-
-static void supplicant_iface_scan_results_cb (NMSupplicantInterface * iface,
- guint32 num_bssids,
- NMDeviceWifi * self);
-
-static void supplicant_mgr_state_cb (NMSupplicantInterface * iface,
- guint32 new_state,
- guint32 old_state,
- NMDeviceWifi *self);
+static void supplicant_iface_scan_done_cb (NMSupplicantInterface * iface,
+ gboolean success,
+ NMDeviceWifi * self);
static void supplicant_iface_notify_scanning_cb (NMSupplicantInterface * iface,
GParamSpec * pspec,
@@ -319,6 +292,107 @@ ipw_rfkill_state_work (gpointer user_data)
}
/*
+ * wireless_qual_to_percent
+ *
+ * Convert an iw_quality structure from SIOCGIWSTATS into a magical signal
+ * strength percentage.
+ *
+ */
+static int
+wireless_qual_to_percent (const struct iw_quality *qual,
+ const struct iw_quality *max_qual)
+{
+ int percent = -1;
+ int level_percent = -1;
+
+ g_return_val_if_fail (qual != NULL, -1);
+ g_return_val_if_fail (max_qual != NULL, -1);
+
+ nm_log_dbg (LOGD_WIFI,
+ "QL: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X ** MAX: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X",
+ (__s8) qual->qual, qual->qual, qual->qual,
+ (__s8) qual->level, qual->level, qual->level,
+ (__s8) qual->noise, qual->noise, qual->noise,
+ qual->updated,
+ (__s8) max_qual->qual, max_qual->qual, max_qual->qual,
+ (__s8) max_qual->level, max_qual->level, max_qual->level,
+ (__s8) max_qual->noise, max_qual->noise, max_qual->noise,
+ max_qual->updated);
+
+ /* Try using the card's idea of the signal quality first as long as it tells us what the max quality is.
+ * Drivers that fill in quality values MUST treat them as percentages, ie the "Link Quality" MUST be
+ * bounded by 0 and max_qual->qual, and MUST change in a linear fashion. Within those bounds, drivers
+ * are free to use whatever they want to calculate "Link Quality".
+ */
+ if ((max_qual->qual != 0) && !(max_qual->updated & IW_QUAL_QUAL_INVALID) && !(qual->updated & IW_QUAL_QUAL_INVALID))
+ percent = (int)(100 * ((double)qual->qual / (double)max_qual->qual));
+
+ /* If the driver doesn't specify a complete and valid quality, we have two options:
+ *
+ * 1) dBm: driver must specify max_qual->level = 0, and have valid values for
+ * qual->level and (qual->noise OR max_qual->noise)
+ * 2) raw RSSI: driver must specify max_qual->level > 0, and have valid values for
+ * qual->level and max_qual->level
+ *
+ * This is the WEXT spec. If this interpretation is wrong, I'll fix it. Otherwise,
+ * If drivers don't conform to it, they are wrong and need to be fixed.
+ */
+
+ if ( (max_qual->level == 0) && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level == 0 */
+ && !(qual->updated & IW_QUAL_LEVEL_INVALID) /* Must have valid qual->level */
+ && ( ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID)) /* Must have valid max_qual->noise */
+ || ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID))) /* OR valid qual->noise */
+ ) {
+ /* Absolute power values (dBm) */
+
+ /* Reasonable fallbacks for dumb drivers that don't specify either level. */
+ #define FALLBACK_NOISE_FLOOR_DBM -90
+ #define FALLBACK_SIGNAL_MAX_DBM -20
+ int max_level = FALLBACK_SIGNAL_MAX_DBM;
+ int noise = FALLBACK_NOISE_FLOOR_DBM;
+ int level = qual->level - 0x100;
+
+ level = CLAMP (level, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM);
+
+ if ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID))
+ noise = qual->noise - 0x100;
+ else if ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID))
+ noise = max_qual->noise - 0x100;
+ noise = CLAMP (noise, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM);
+
+ /* A sort of signal-to-noise ratio calculation */
+ level_percent = (int)(100 - 70 *(
+ ((double)max_level - (double)level) /
+ ((double)max_level - (double)noise)));
+ nm_log_dbg (LOGD_WIFI, "QL1: level_percent is %d. max_level %d, level %d, noise_floor %d.",
+ level_percent, max_level, level, noise);
+ } else if ( (max_qual->level != 0)
+ && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level as upper bound */
+ && !(qual->updated & IW_QUAL_LEVEL_INVALID)) {
+ /* Relative power values (RSSI) */
+
+ int level = qual->level;
+
+ /* Signal level is relavtive (0 -> max_qual->level) */
+ level = CLAMP (level, 0, max_qual->level);
+ level_percent = (int)(100 * ((double)level / (double)max_qual->level));
+ nm_log_dbg (LOGD_WIFI, "QL2: level_percent is %d. max_level %d, level %d.",
+ level_percent, max_qual->level, level);
+ } else if (percent == -1) {
+ nm_log_dbg (LOGD_WIFI, "QL: Could not get quality %% value from driver. Driver is probably buggy.");
+ }
+
+ /* If the quality percent was 0 or doesn't exist, then try to use signal levels instead */
+ if ((percent < 1) && (level_percent >= 0))
+ percent = level_percent;
+
+ nm_log_dbg (LOGD_WIFI, "QL: Final quality percent is %d (%d).",
+ percent, CLAMP (percent, 0, 100));
+ return (CLAMP (percent, 0, 100));
+}
+
+
+/*
* nm_device_wifi_update_signal_strength
*
* Update the device's idea of the strength of its connection to the
@@ -619,10 +693,7 @@ constructor (GType type,
/* Connect to the supplicant manager */
priv->supplicant.mgr = nm_supplicant_manager_get ();
- priv->supplicant.mgr_state_id = g_signal_connect (priv->supplicant.mgr,
- "state",
- G_CALLBACK (supplicant_mgr_state_cb),
- self);
+ g_assert (priv->supplicant.mgr);
/* The ipw2x00 drivers don't integrate with the kernel rfkill subsystem until
* 2.6.33. Thus all our nice libgudev magic is useless. So we get to poll.
@@ -651,17 +722,13 @@ static gboolean
supplicant_interface_acquire (NMDeviceWifi *self)
{
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
- guint id, mgr_state;
+ guint id, i = 0;
g_return_val_if_fail (self != NULL, FALSE);
- g_return_val_if_fail (priv->supplicant.mgr != NULL, FALSE);
/* interface already acquired? */
g_return_val_if_fail (priv->supplicant.iface == NULL, TRUE);
- mgr_state = nm_supplicant_manager_get_state (priv->supplicant.mgr);
- g_return_val_if_fail (mgr_state == NM_SUPPLICANT_MANAGER_STATE_IDLE, FALSE);
-
- priv->supplicant.iface = nm_supplicant_manager_get_iface (priv->supplicant.mgr,
+ priv->supplicant.iface = nm_supplicant_manager_iface_get (priv->supplicant.mgr,
nm_device_get_iface (NM_DEVICE (self)),
TRUE);
if (priv->supplicant.iface == NULL) {
@@ -670,70 +737,36 @@ supplicant_interface_acquire (NMDeviceWifi *self)
return FALSE;
}
- id = g_signal_connect (priv->supplicant.iface,
- "state",
- G_CALLBACK (supplicant_iface_state_cb),
- self);
- priv->supplicant.iface_state_id = id;
-
- id = g_signal_connect (priv->supplicant.iface,
- "scanned-ap",
- G_CALLBACK (supplicant_iface_scanned_ap_cb),
- self);
- priv->supplicant.iface_scanned_ap_id = id;
+ memset (priv->supplicant.sig_ids, 0, sizeof (priv->supplicant.sig_ids));
id = g_signal_connect (priv->supplicant.iface,
- "scan-req-result",
- G_CALLBACK (supplicant_iface_scan_request_result_cb),
+ NM_SUPPLICANT_INTERFACE_STATE,
+ G_CALLBACK (supplicant_iface_state_cb),
self);
- priv->supplicant.iface_scan_request_result_id = id;
+ priv->supplicant.sig_ids[i++] = id;
id = g_signal_connect (priv->supplicant.iface,
- "scan-results",
- G_CALLBACK (supplicant_iface_scan_results_cb),
+ NM_SUPPLICANT_INTERFACE_NEW_BSS,
+ G_CALLBACK (supplicant_iface_new_bss_cb),
self);
- priv->supplicant.iface_scan_results_id = id;
+ priv->supplicant.sig_ids[i++] = id;
id = g_signal_connect (priv->supplicant.iface,
- "connection-state",
- G_CALLBACK (supplicant_iface_connection_state_cb),
+ NM_SUPPLICANT_INTERFACE_SCAN_DONE,
+ G_CALLBACK (supplicant_iface_scan_done_cb),
self);
- priv->supplicant.iface_con_state_id = id;
+ priv->supplicant.sig_ids[i++] = id;
id = g_signal_connect (priv->supplicant.iface,
"notify::scanning",
G_CALLBACK (supplicant_iface_notify_scanning_cb),
self);
- priv->supplicant.iface_notify_scanning_id = id;
+ priv->supplicant.sig_ids[i++] = id;
return TRUE;
}
static void
-finish_supplicant_task (SupplicantStateTask *task, gboolean remove_source)
-{
- NMDeviceWifi *self = task->self;
- NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-
- /* idle/timeout handlers should pass FALSE for remove_source, since they
- * will tell glib to remove their source from the mainloop by returning
- * FALSE when they exit. When called from this NMDevice's dispose handler,
- * remove_source should be TRUE to cancel all outstanding idle/timeout
- * handlers asynchronously.
- */
- if (task->source_id && remove_source)
- g_source_remove (task->source_id);
-
- if (task->mgr_task)
- priv->supplicant.mgr_tasks = g_slist_remove (priv->supplicant.mgr_tasks, task);
- else
- priv->supplicant.iface_tasks = g_slist_remove (priv->supplicant.iface_tasks, task);
-
- memset (task, 0, sizeof (SupplicantStateTask));
- g_slice_free (SupplicantStateTask, task);
-}
-
-static void
remove_supplicant_interface_error_handler (NMDeviceWifi *self)
{
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
@@ -756,6 +789,7 @@ static void
supplicant_interface_release (NMDeviceWifi *self)
{
NMDeviceWifiPrivate *priv;
+ guint i;
g_return_if_fail (self != NULL);
@@ -770,45 +804,18 @@ supplicant_interface_release (NMDeviceWifi *self)
remove_supplicant_interface_error_handler (self);
- /* Clean up all pending supplicant interface state idle tasks */
- while (priv->supplicant.iface_tasks)
- finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.iface_tasks->data, TRUE);
-
- if (priv->supplicant.iface_state_id > 0) {
- g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_state_id);
- priv->supplicant.iface_state_id = 0;
- }
-
- if (priv->supplicant.iface_scanned_ap_id > 0) {
- g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_scanned_ap_id);
- priv->supplicant.iface_scanned_ap_id = 0;
- }
-
- if (priv->supplicant.iface_scan_request_result_id > 0) {
- g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_scan_request_result_id);
- priv->supplicant.iface_scan_request_result_id = 0;
- }
-
- if (priv->supplicant.iface_scan_results_id > 0) {
- g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_scan_results_id);
- priv->supplicant.iface_scan_results_id = 0;
- }
-
- if (priv->supplicant.iface_con_state_id > 0) {
- g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_con_state_id);
- priv->supplicant.iface_con_state_id = 0;
- }
-
- if (priv->supplicant.iface_notify_scanning_id > 0) {
- g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.iface_notify_scanning_id);
- priv->supplicant.iface_notify_scanning_id = 0;
+ /* Clear supplicant interface signal handlers */
+ for (i = 0; i < SUP_SIG_ID_LEN; i++) {
+ if (priv->supplicant.sig_ids[i] > 0)
+ g_signal_handler_disconnect (priv->supplicant.iface, priv->supplicant.sig_ids[i]);
}
+ memset (priv->supplicant.sig_ids, 0, sizeof (priv->supplicant.sig_ids));
if (priv->supplicant.iface) {
/* Tell the supplicant to disconnect from the current AP */
nm_supplicant_interface_disconnect (priv->supplicant.iface);
- nm_supplicant_manager_release_iface (priv->supplicant.mgr, priv->supplicant.iface);
+ nm_supplicant_manager_iface_release (priv->supplicant.mgr, priv->supplicant.iface);
priv->supplicant.iface = NULL;
}
}
@@ -824,6 +831,11 @@ get_active_ap (NMDeviceWifi *self,
const GByteArray *ssid;
GSList *iter;
int i = 0;
+ NMAccessPoint *match_nofreq = NULL;
+ gboolean found_a_band = FALSE;
+ gboolean found_bg_band = FALSE;
+ NM80211Mode devmode;
+ guint32 devfreq;
nm_device_wifi_get_bssid (self, &bssid);
nm_log_dbg (LOGD_WIFI, "(%s): active BSSID: %02x:%02x:%02x:%02x:%02x:%02x",
@@ -842,6 +854,9 @@ get_active_ap (NMDeviceWifi *self,
ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)",
ssid ? "'" : "");
+ devmode = nm_device_wifi_get_mode (self);
+ devfreq = nm_device_wifi_get_frequency (self);
+
/* When matching hidden APs, do a second pass that ignores the SSID check,
* because NM might not yet know the SSID of the hidden AP in the scan list
* and therefore it won't get matched the first time around.
@@ -854,8 +869,8 @@ get_active_ap (NMDeviceWifi *self,
NMAccessPoint *ap = NM_AP (iter->data);
const struct ether_addr *ap_bssid = nm_ap_get_address (ap);
const GByteArray *ap_ssid = nm_ap_get_ssid (ap);
- NM80211Mode devmode, apmode;
- guint32 devfreq, apfreq;
+ NM80211Mode apmode;
+ guint32 apfreq;
nm_log_dbg (LOGD_WIFI, " AP: %s%s%s %02x:%02x:%02x:%02x:%02x:%02x",
ap_ssid ? "'" : "",
@@ -880,7 +895,6 @@ get_active_ap (NMDeviceWifi *self,
continue;
}
- devmode = nm_device_wifi_get_mode (self);
apmode = nm_ap_get_mode (ap);
if (devmode != apmode) {
nm_log_dbg (LOGD_WIFI, " mode mismatch (device %d, ap %d)",
@@ -888,11 +902,18 @@ get_active_ap (NMDeviceWifi *self,
continue;
}
- devfreq = nm_device_wifi_get_frequency (self);
apfreq = nm_ap_get_freq (ap);
if (devfreq != apfreq) {
nm_log_dbg (LOGD_WIFI, " frequency mismatch (device %u, ap %u)",
devfreq, apfreq);
+
+ if (match_nofreq == NULL)
+ match_nofreq = ap;
+
+ if (apfreq > 4000)
+ found_a_band = TRUE;
+ else if (apfreq > 2000)
+ found_bg_band = TRUE;
continue;
}
@@ -902,6 +923,32 @@ get_active_ap (NMDeviceWifi *self,
}
}
+ /* Some proprietary drivers (wl.o) report tuned frequency (like when
+ * scanning) instead of the associated AP's frequency. This is a great
+ * example of how WEXT is underspecified. We use frequency to find the
+ * active AP in the scan list because some configurations use the same
+ * SSID/BSSID on the 2GHz and 5GHz bands simultaneously, and we need to
+ * make sure we get the right AP in the right band. This configuration
+ * is uncommon though, and the frequency check penalizes closed drivers we
+ * can't fix. Because we're not total dicks, ignore the frequency condition
+ * if the associated BSSID/SSID exists only in one band since that's most
+ * likely the AP we want.
+ */
+ if (match_nofreq && (found_a_band != found_bg_band)) {
+ const struct ether_addr *ap_bssid = nm_ap_get_address (match_nofreq);
+ const GByteArray *ap_ssid = nm_ap_get_ssid (match_nofreq);
+
+ nm_log_dbg (LOGD_WIFI, " matched %s%s%s %02x:%02x:%02x:%02x:%02x:%02x",
+ ap_ssid ? "'" : "",
+ ap_ssid ? nm_utils_escape_ssid (ap_ssid->data, ap_ssid->len) : "(none)",
+ ap_ssid ? "'" : "",
+ ap_bssid->ether_addr_octet[0], ap_bssid->ether_addr_octet[1],
+ ap_bssid->ether_addr_octet[2], ap_bssid->ether_addr_octet[3],
+ ap_bssid->ether_addr_octet[4], ap_bssid->ether_addr_octet[5]);
+
+ return match_nofreq;
+ }
+
nm_log_dbg (LOGD_WIFI, " No matching AP found.");
return NULL;
}
@@ -966,8 +1013,10 @@ periodic_update (NMDeviceWifi *self)
struct ether_addr bssid = { {0x0, 0x0, 0x0, 0x0, 0x0, 0x0} };
nm_device_wifi_get_bssid (self, &bssid);
- /* 0x02 is the first byte of IBSS BSSIDs */
- if ( (bssid.ether_addr_octet[0] == 0x02)
+ /* 0x02 means "locally administered" and should be OR-ed into
+ * the first byte of IBSS BSSIDs.
+ */
+ if ( (bssid.ether_addr_octet[0] & 0x02)
&& nm_ethernet_address_is_valid (&bssid))
nm_ap_set_address (priv->current_ap, &bssid);
}
@@ -1083,6 +1132,62 @@ real_bring_up (NMDevice *dev)
}
static void
+_update_hw_addr (NMDeviceWifi *self, const guint8 *addr)
+{
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+
+ g_return_if_fail (addr != NULL);
+
+ if (memcmp (&priv->hw_addr, addr, ETH_ALEN)) {
+ memcpy (&priv->hw_addr, addr, ETH_ALEN);
+ g_object_notify (G_OBJECT (self), NM_DEVICE_WIFI_HW_ADDRESS);
+ }
+}
+
+static gboolean
+_set_hw_addr (NMDeviceWifi *self, const guint8 *addr, const char *detail)
+{
+ NMDevice *dev = NM_DEVICE (self);
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+ const char *iface;
+ char *mac_str = NULL;
+ gboolean success = FALSE;
+
+ g_return_val_if_fail (addr != NULL, FALSE);
+
+ iface = nm_device_get_iface (dev);
+
+ mac_str = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+
+ /* Do nothing if current MAC is same */
+ if (!memcmp (&priv->hw_addr, addr, ETH_ALEN)) {
+ nm_log_dbg (LOGD_DEVICE | LOGD_ETHER, "(%s): no MAC address change needed",
+ iface, detail, mac_str);
+ g_free (mac_str);
+ return TRUE;
+ }
+
+ /* Can't change MAC address while device is up */
+ real_hw_take_down (dev);
+
+ success = nm_system_device_set_mac (iface, (struct ether_addr *) addr);
+ if (success) {
+ /* MAC address succesfully changed; update the current MAC to match */
+ _update_hw_addr (self, addr);
+ nm_log_info (LOGD_DEVICE | LOGD_ETHER, "(%s): %s MAC address to %s",
+ iface, detail, mac_str);
+ } else {
+ nm_log_warn (LOGD_DEVICE | LOGD_ETHER, "(%s): failed to %s MAC address to %s",
+ iface, detail, mac_str);
+ }
+ real_hw_bring_up (dev, NULL);
+ g_free (mac_str);
+
+ return success;
+}
+
+static void
access_point_removed (NMDeviceWifi *device, NMAccessPoint *ap)
{
g_signal_emit (device, signals[ACCESS_POINT_REMOVED], 0, ap);
@@ -1146,6 +1251,9 @@ real_deactivate_quickly (NMDevice *dev)
priv->ap_list = g_slist_remove (priv->ap_list, orig_ap);
g_object_unref (orig_ap);
}
+
+ /* Reset MAC address back to initial address */
+ _set_hw_addr (self, priv->initial_hw_addr, "reset");
}
static void
@@ -1188,7 +1296,7 @@ real_check_connection_compatible (NMDevice *device,
}
mac = nm_setting_wireless_get_mac_address (s_wireless);
- if (mac && memcmp (mac->data, &(priv->hw_addr.ether_addr_octet), ETH_ALEN)) {
+ if (mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN)) {
g_set_error (error,
NM_WIFI_ERROR, NM_WIFI_ERROR_CONNECTION_INCOMPATIBLE,
"The connection's MAC address did not match this device.");
@@ -1263,7 +1371,7 @@ real_get_best_auto_connection (NMDevice *dev,
continue;
mac = nm_setting_wireless_get_mac_address (s_wireless);
- if (mac && memcmp (mac->data, priv->hw_addr.ether_addr_octet, ETH_ALEN))
+ if (mac && memcmp (mac->data, &priv->perm_hw_addr, ETH_ALEN))
continue;
/* Use the connection if it's a shared connection */
@@ -1303,7 +1411,7 @@ nm_device_wifi_get_address (NMDeviceWifi *self,
g_return_if_fail (addr != NULL);
priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
- memcpy (addr, &(priv->hw_addr), sizeof (struct ether_addr));
+ memcpy (addr, &priv->hw_addr, sizeof (struct ether_addr));
}
static void
@@ -1481,107 +1589,6 @@ nm_device_wifi_get_frequency (NMDeviceWifi *self)
}
/*
- * wireless_stats_to_percent
- *
- * Convert an iw_stats structure from a scan or the card into
- * a magical signal strength percentage.
- *
- */
-static int
-wireless_qual_to_percent (const struct iw_quality *qual,
- const struct iw_quality *max_qual)
-{
- int percent = -1;
- int level_percent = -1;
-
- g_return_val_if_fail (qual != NULL, -1);
- g_return_val_if_fail (max_qual != NULL, -1);
-
- nm_log_dbg (LOGD_WIFI,
- "QL: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X ** MAX: qual %d/%u/0x%X, level %d/%u/0x%X, noise %d/%u/0x%X, updated: 0x%X",
- (__s8) qual->qual, qual->qual, qual->qual,
- (__s8) qual->level, qual->level, qual->level,
- (__s8) qual->noise, qual->noise, qual->noise,
- qual->updated,
- (__s8) max_qual->qual, max_qual->qual, max_qual->qual,
- (__s8) max_qual->level, max_qual->level, max_qual->level,
- (__s8) max_qual->noise, max_qual->noise, max_qual->noise,
- max_qual->updated);
-
- /* Try using the card's idea of the signal quality first as long as it tells us what the max quality is.
- * Drivers that fill in quality values MUST treat them as percentages, ie the "Link Quality" MUST be
- * bounded by 0 and max_qual->qual, and MUST change in a linear fashion. Within those bounds, drivers
- * are free to use whatever they want to calculate "Link Quality".
- */
- if ((max_qual->qual != 0) && !(max_qual->updated & IW_QUAL_QUAL_INVALID) && !(qual->updated & IW_QUAL_QUAL_INVALID))
- percent = (int)(100 * ((double)qual->qual / (double)max_qual->qual));
-
- /* If the driver doesn't specify a complete and valid quality, we have two options:
- *
- * 1) dBm: driver must specify max_qual->level = 0, and have valid values for
- * qual->level and (qual->noise OR max_qual->noise)
- * 2) raw RSSI: driver must specify max_qual->level > 0, and have valid values for
- * qual->level and max_qual->level
- *
- * This is the WEXT spec. If this interpretation is wrong, I'll fix it. Otherwise,
- * If drivers don't conform to it, they are wrong and need to be fixed.
- */
-
- if ( (max_qual->level == 0) && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level == 0 */
- && !(qual->updated & IW_QUAL_LEVEL_INVALID) /* Must have valid qual->level */
- && ( ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID)) /* Must have valid max_qual->noise */
- || ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID))) /* OR valid qual->noise */
- ) {
- /* Absolute power values (dBm) */
-
- /* Reasonable fallbacks for dumb drivers that don't specify either level. */
- #define FALLBACK_NOISE_FLOOR_DBM -90
- #define FALLBACK_SIGNAL_MAX_DBM -20
- int max_level = FALLBACK_SIGNAL_MAX_DBM;
- int noise = FALLBACK_NOISE_FLOOR_DBM;
- int level = qual->level - 0x100;
-
- level = CLAMP (level, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM);
-
- if ((qual->noise > 0) && !(qual->updated & IW_QUAL_NOISE_INVALID))
- noise = qual->noise - 0x100;
- else if ((max_qual->noise > 0) && !(max_qual->updated & IW_QUAL_NOISE_INVALID))
- noise = max_qual->noise - 0x100;
- noise = CLAMP (noise, FALLBACK_NOISE_FLOOR_DBM, FALLBACK_SIGNAL_MAX_DBM);
-
- /* A sort of signal-to-noise ratio calculation */
- level_percent = (int)(100 - 70 *(
- ((double)max_level - (double)level) /
- ((double)max_level - (double)noise)));
- nm_log_dbg (LOGD_WIFI, "QL1: level_percent is %d. max_level %d, level %d, noise_floor %d.",
- level_percent, max_level, level, noise);
- } else if ( (max_qual->level != 0)
- && !(max_qual->updated & IW_QUAL_LEVEL_INVALID) /* Valid max_qual->level as upper bound */
- && !(qual->updated & IW_QUAL_LEVEL_INVALID)) {
- /* Relative power values (RSSI) */
-
- int level = qual->level;
-
- /* Signal level is relavtive (0 -> max_qual->level) */
- level = CLAMP (level, 0, max_qual->level);
- level_percent = (int)(100 * ((double)level / (double)max_qual->level));
- nm_log_dbg (LOGD_WIFI, "QL2: level_percent is %d. max_level %d, level %d.",
- level_percent, max_qual->level, level);
- } else if (percent == -1) {
- nm_log_dbg (LOGD_WIFI, "QL: Could not get quality %% value from driver. Driver is probably buggy.");
- }
-
- /* If the quality percent was 0 or doesn't exist, then try to use signal levels instead */
- if ((percent < 1) && (level_percent >= 0))
- percent = level_percent;
-
- nm_log_dbg (LOGD_WIFI, "QL: Final quality percent is %d (%d).",
- percent, CLAMP (percent, 0, 100));
- return (CLAMP (percent, 0, 100));
-}
-
-
-/*
* nm_device_wifi_get_ssid
*
* If a device is wireless, return the ssid that it is attempting
@@ -1730,21 +1737,23 @@ scanning_allowed (NMDeviceWifi *self)
}
/* Don't scan if the supplicant is busy */
- sup_state = nm_supplicant_interface_get_connection_state (priv->supplicant.iface);
- if ( sup_state == NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATING
- || sup_state == NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATED
- || sup_state == NM_SUPPLICANT_INTERFACE_CON_STATE_4WAY_HANDSHAKE
- || sup_state == NM_SUPPLICANT_INTERFACE_CON_STATE_GROUP_HANDSHAKE
+ sup_state = nm_supplicant_interface_get_state (priv->supplicant.iface);
+ if ( sup_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING
+ || sup_state == NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED
+ || sup_state == NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE
+ || sup_state == NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE
|| nm_supplicant_interface_get_scanning (priv->supplicant.iface))
return FALSE;
- /* Don't scan when a shared connection is active; it makes drivers mad */
req = nm_device_get_act_request (NM_DEVICE (self));
if (req) {
NMConnection *connection;
NMSettingIP4Config *s_ip4;
+ NMSettingWireless *s_wifi;
const char *ip4_method = NULL;
+ const GByteArray *bssid;
+ /* Don't scan when a shared connection is active; it makes drivers mad */
connection = nm_act_request_get_connection (req);
s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (connection, NM_TYPE_SETTING_IP4_CONFIG);
if (s_ip4)
@@ -1752,6 +1761,16 @@ scanning_allowed (NMDeviceWifi *self)
if (s_ip4 && !strcmp (ip4_method, NM_SETTING_IP4_CONFIG_METHOD_SHARED))
return FALSE;
+
+ /* Don't scan when the connection is locked to a specifc AP, since
+ * intra-ESS roaming (which requires periodic scanning) isn't being
+ * used due to the specific AP lock. (bgo #513820)
+ */
+ s_wifi = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS);
+ g_assert (s_wifi);
+ bssid = nm_setting_wireless_get_bssid (s_wifi);
+ if (bssid && bssid->len == ETH_ALEN)
+ return FALSE;
}
return TRUE;
@@ -1878,28 +1897,19 @@ cancel_pending_scan (NMDeviceWifi *self)
}
}
-
static void
-supplicant_iface_scan_request_result_cb (NMSupplicantInterface *iface,
- gboolean success,
- NMDeviceWifi *self)
+supplicant_iface_scan_done_cb (NMSupplicantInterface *iface,
+ gboolean success,
+ NMDeviceWifi *self)
{
- nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan request %s",
+ nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan %s",
nm_device_get_iface (NM_DEVICE (self)),
success ? "successful" : "failed");
if (check_scanning_allowed (self))
schedule_scan (self, TRUE);
-}
-static void
-supplicant_iface_scan_results_cb (NMSupplicantInterface *iface,
- guint32 num_results,
- NMDeviceWifi *self)
-{
- nm_log_dbg (LOGD_WIFI_SCAN, "(%s): scan results available (%d APs found)",
- nm_device_get_iface (NM_DEVICE (self)),
- num_results);
+#if 0
if (num_results == 0) {
/* ensure that old APs get culled, which otherwise only
* happens when there are actual scan results to process.
@@ -1907,6 +1917,7 @@ supplicant_iface_scan_results_cb (NMSupplicantInterface *iface,
cull_scan_list (self);
nm_device_wifi_ap_list_print (self);
}
+#endif
}
static gboolean
@@ -2126,48 +2137,10 @@ cull_scan_list (NMDeviceWifi *self)
removed, total);
}
-#define SET_QUALITY_MEMBER(qual_item, lc_member, uc_member) \
- if (lc_member != -1) { \
- qual_item.lc_member = lc_member; \
- qual_item.updated |= IW_QUAL_##uc_member##_UPDATED; \
- } else { \
- qual_item.updated |= IW_QUAL_##uc_member##_INVALID; \
- }
-
static void
-set_ap_strength_from_properties (NMDeviceWifi *self,
- NMAccessPoint *ap,
- GHashTable *properties)
-{
- NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
- int qual, level, noise;
- struct iw_quality quality;
- GValue *value;
- gint8 strength;
-
- value = (GValue *) g_hash_table_lookup (properties, "quality");
- qual = value ? g_value_get_int (value) : -1;
-
- value = (GValue *) g_hash_table_lookup (properties, "level");
- level = value ? g_value_get_int (value) : -1;
-
- value = (GValue *) g_hash_table_lookup (properties, "noise");
- noise = value ? g_value_get_int (value) : -1;
-
- /* Calculate and set the AP's signal quality */
- memset (&quality, 0, sizeof (struct iw_quality));
- SET_QUALITY_MEMBER (quality, qual, QUAL);
- SET_QUALITY_MEMBER (quality, level, LEVEL);
- SET_QUALITY_MEMBER (quality, noise, NOISE);
-
- strength = wireless_qual_to_percent (&quality, &priv->max_qual);
- nm_ap_set_strength (ap, strength);
-}
-
-static void
-supplicant_iface_scanned_ap_cb (NMSupplicantInterface *iface,
- GHashTable *properties,
- NMDeviceWifi *self)
+supplicant_iface_new_bss_cb (NMSupplicantInterface *iface,
+ GHashTable *properties,
+ NMDeviceWifi *self)
{
NMDeviceState state;
NMAccessPoint *ap;
@@ -2183,8 +2156,6 @@ supplicant_iface_scanned_ap_cb (NMSupplicantInterface *iface,
ap = nm_ap_new_from_properties (properties);
if (ap) {
- set_ap_strength_from_properties (self, ap, properties);
-
nm_ap_print_self (ap, "AP: ");
/* Add the AP to the device's AP list */
@@ -2322,247 +2293,97 @@ time_out:
return FALSE;
}
-static gboolean
-schedule_state_handler (NMDeviceWifi *self,
- GSourceFunc handler,
- guint32 new_state,
- guint32 old_state,
- gboolean mgr_task)
+static void
+supplicant_iface_state_cb (NMSupplicantInterface *iface,
+ guint32 new_state,
+ guint32 old_state,
+ gpointer user_data)
{
- NMDeviceWifiPrivate *priv;
- SupplicantStateTask *task;
-
- g_return_val_if_fail (self != NULL, FALSE);
- g_return_val_if_fail (handler != NULL, FALSE);
+ NMDeviceWifi *self = NM_DEVICE_WIFI (user_data);
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+ NMDevice *device = NM_DEVICE (self);
+ NMDeviceState devstate;
+ gboolean scanning;
if (new_state == old_state)
- return TRUE;
-
- priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
-
- task = g_slice_new0 (SupplicantStateTask);
- if (!task) {
- nm_log_err (LOGD_WIFI, "Not enough memory to process supplicant manager state change.");
- return FALSE;
- }
-
- task->self = self;
- task->new_state = new_state;
- task->old_state = old_state;
- task->mgr_task = mgr_task;
-
- task->source_id = g_idle_add (handler, task);
- if (mgr_task)
- priv->supplicant.mgr_tasks = g_slist_append (priv->supplicant.mgr_tasks, task);
- else
- priv->supplicant.iface_tasks = g_slist_append (priv->supplicant.iface_tasks, task);
-
- return TRUE;
-}
-
-static gboolean
-supplicant_iface_state_cb_handler (gpointer user_data)
-{
- SupplicantStateTask *task = (SupplicantStateTask *) user_data;
- NMDeviceWifi *self;
- NMDeviceWifiPrivate *priv;
-
- g_return_val_if_fail (task != NULL, FALSE);
+ return;
- self = task->self;
- priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+ nm_log_info (LOGD_DEVICE | LOGD_WIFI,
+ "(%s): supplicant interface state: %s -> %s",
+ nm_device_get_iface (device),
+ nm_supplicant_interface_state_to_string (old_state),
+ nm_supplicant_interface_state_to_string (new_state));
- nm_log_info (LOGD_WIFI, "(%s): supplicant interface state: %s -> %s",
- nm_device_get_iface (NM_DEVICE (self)),
- nm_supplicant_interface_state_to_string (task->old_state),
- nm_supplicant_interface_state_to_string (task->new_state));
+ devstate = nm_device_get_state (device);
+ scanning = nm_supplicant_interface_get_scanning (iface);
- if (task->new_state == NM_SUPPLICANT_INTERFACE_STATE_READY) {
+ switch (new_state) {
+ case NM_SUPPLICANT_INTERFACE_STATE_READY:
priv->scan_interval = SCAN_INTERVAL_MIN;
/* If the interface can now be activated because the supplicant is now
* available, transition to DISCONNECTED.
*/
- if ( (nm_device_get_state (NM_DEVICE (self)) == NM_DEVICE_STATE_UNAVAILABLE)
- && nm_device_is_available (NM_DEVICE (self))) {
- nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_DISCONNECTED,
+ if ((devstate == NM_DEVICE_STATE_UNAVAILABLE) && nm_device_is_available (device)) {
+ nm_device_state_changed (device,
+ NM_DEVICE_STATE_DISCONNECTED,
NM_DEVICE_STATE_REASON_SUPPLICANT_AVAILABLE);
}
- nm_log_dbg (LOGD_WIFI_SCAN, "(%s): supplicant ready, requesting initial scan",
- nm_device_get_iface (NM_DEVICE (self)));
+ nm_log_dbg (LOGD_WIFI_SCAN,
+ "(%s): supplicant ready, requesting initial scan",
+ nm_device_get_iface (device));
/* Request a scan to get latest results */
cancel_pending_scan (self);
request_wireless_scan (self);
- } else if (task->new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) {
- cleanup_association_attempt (self, FALSE);
- supplicant_interface_release (self);
- nm_device_state_changed (NM_DEVICE (self), NM_DEVICE_STATE_UNAVAILABLE,
- NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
- }
-
- finish_supplicant_task (task, FALSE);
- return FALSE;
-}
-
-
-static void
-supplicant_iface_state_cb (NMSupplicantInterface * iface,
- guint32 new_state,
- guint32 old_state,
- NMDeviceWifi *self)
-{
- g_return_if_fail (self != NULL);
-
- schedule_state_handler (self,
- supplicant_iface_state_cb_handler,
- new_state,
- old_state,
- FALSE);
-}
-
-
-static gboolean
-supplicant_iface_connection_state_cb_handler (gpointer user_data)
-{
- SupplicantStateTask *task = (SupplicantStateTask *) user_data;
- NMDeviceWifi *self = task->self;
- NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
- NMDevice *dev = NM_DEVICE (self);
- gboolean scanning;
-
- if (!nm_device_get_act_request (dev)) {
- /* The device is not activating or already activated; do nothing. */
- goto out;
- }
-
- nm_log_info (LOGD_WIFI, "(%s): supplicant connection state: %s -> %s",
- nm_device_get_iface (dev),
- nm_supplicant_interface_connection_state_to_string (task->old_state),
- nm_supplicant_interface_connection_state_to_string (task->new_state));
-
- scanning = nm_supplicant_interface_get_scanning (priv->supplicant.iface);
-
- if (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED) {
+ break;
+ case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED:
remove_supplicant_interface_error_handler (self);
remove_supplicant_timeouts (self);
/* If this is the initial association during device activation,
* schedule the next activation stage.
*/
- if (nm_device_get_state (dev) == NM_DEVICE_STATE_CONFIG) {
+ if (devstate == NM_DEVICE_STATE_CONFIG) {
NMAccessPoint *ap = nm_device_wifi_get_activation_ap (self);
- const GByteArray * ssid = nm_ap_get_ssid (ap);
+ const GByteArray *ssid = nm_ap_get_ssid (ap);
nm_log_info (LOGD_DEVICE | LOGD_WIFI,
"Activation (%s/wireless) Stage 2 of 5 (Device Configure) "
"successful. Connected to wireless network '%s'.",
- nm_device_get_iface (dev),
+ nm_device_get_iface (device),
ssid ? nm_utils_escape_ssid (ssid->data, ssid->len) : "(none)");
- nm_device_activate_schedule_stage3_ip_config_start (dev);
+ nm_device_activate_schedule_stage3_ip_config_start (device);
}
- } else if (task->new_state == NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED) {
- if (nm_device_get_state (dev) == NM_DEVICE_STATE_ACTIVATED || nm_device_is_activating (dev)) {
+ break;
+ case NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED:
+ if ((devstate == NM_DEVICE_STATE_ACTIVATED) || nm_device_is_activating (device)) {
/* Start the link timeout so we allow some time for reauthentication,
* use a longer timeout if we are scanning since some cards take a
* while to scan.
*/
if (!priv->link_timeout_id) {
priv->link_timeout_id = g_timeout_add_seconds (scanning ? 30 : 15,
- link_timeout_cb, self);
- }
- }
- }
-
-out:
- finish_supplicant_task (task, FALSE);
- return FALSE;
-}
-
-
-static void
-supplicant_iface_connection_state_cb (NMSupplicantInterface * iface,
- guint32 new_state,
- guint32 old_state,
- NMDeviceWifi *self)
-{
- g_return_if_fail (self != NULL);
-
- schedule_state_handler (self,
- supplicant_iface_connection_state_cb_handler,
- new_state,
- old_state,
- FALSE);
-}
-
-
-static gboolean
-supplicant_mgr_state_cb_handler (gpointer user_data)
-{
- SupplicantStateTask *task = (SupplicantStateTask *) user_data;
- NMDeviceWifi *self;
- NMDeviceWifiPrivate *priv;
- NMDevice *dev;
- NMDeviceState dev_state;
-
- g_return_val_if_fail (task != NULL, FALSE);
-
- self = task->self;
- priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
- dev = NM_DEVICE (self);
-
- nm_log_info (LOGD_WIFI, "(%s): supplicant manager state: %s -> %s",
- nm_device_get_iface (NM_DEVICE (self)),
- nm_supplicant_manager_state_to_string (task->old_state),
- nm_supplicant_manager_state_to_string (task->new_state));
-
- /* If the supplicant went away, release the supplicant interface */
- if (task->new_state == NM_SUPPLICANT_MANAGER_STATE_DOWN) {
- if (priv->supplicant.iface) {
- cleanup_association_attempt (self, FALSE);
- supplicant_interface_release (self);
- }
-
- if (nm_device_get_state (dev) > NM_DEVICE_STATE_UNAVAILABLE) {
- nm_device_state_changed (dev, NM_DEVICE_STATE_UNAVAILABLE,
- NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
- }
- } else if (task->new_state == NM_SUPPLICANT_MANAGER_STATE_IDLE) {
- dev_state = nm_device_get_state (dev);
- if ( priv->enabled
- && !priv->supplicant.iface
- && (dev_state >= NM_DEVICE_STATE_UNAVAILABLE)) {
- /* request a supplicant interface from the supplicant manager */
- supplicant_interface_acquire (self);
-
- /* if wireless is enabled and we have a supplicant interface,
- * we can transition to the DISCONNECTED state.
- */
- if (priv->supplicant.iface) {
- nm_device_state_changed (dev, NM_DEVICE_STATE_DISCONNECTED,
- NM_DEVICE_STATE_REASON_NONE);
+ link_timeout_cb, self);
}
}
+ break;
+ case NM_SUPPLICANT_INTERFACE_STATE_DOWN:
+ cleanup_association_attempt (self, FALSE);
+ supplicant_interface_release (self);
+ nm_device_state_changed (device,
+ NM_DEVICE_STATE_UNAVAILABLE,
+ NM_DEVICE_STATE_REASON_SUPPLICANT_FAILED);
+ break;
+ default:
+ break;
}
- finish_supplicant_task (task, FALSE);
- return FALSE;
-}
-
-static void
-supplicant_mgr_state_cb (NMSupplicantInterface * iface,
- guint32 new_state,
- guint32 old_state,
- NMDeviceWifi *self)
-{
- g_return_if_fail (self != NULL);
-
- schedule_state_handler (self,
- supplicant_mgr_state_cb_handler,
- new_state,
- old_state,
- TRUE);
+ /* Signal scanning state changes */
+ if ( new_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING
+ || old_state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING)
+ g_object_notify (G_OBJECT (self), "scanning");
}
struct iface_con_error_cb_data {
@@ -2920,7 +2741,6 @@ static void
real_update_hw_address (NMDevice *dev)
{
NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
- NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
struct ifreq req;
int fd;
@@ -2932,21 +2752,84 @@ real_update_hw_address (NMDevice *dev)
memset (&req, 0, sizeof (struct ifreq));
strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ);
+ errno = 0;
if (ioctl (fd, SIOCGIFHWADDR, &req) < 0) {
- nm_log_err (LOGD_HW | LOGD_WIFI, "(%s) error getting hardware address: %d",
+ nm_log_err (LOGD_HW | LOGD_WIFI, "(%s): unable to read hardware address (error %d)",
nm_device_get_iface (dev), errno);
- goto out;
+ } else
+ _update_hw_addr (self, (const guint8 *) &req.ifr_hwaddr.sa_data);
+
+ close (fd);
+}
+
+static void
+real_update_permanent_hw_address (NMDevice *dev)
+{
+ NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+ struct ifreq req;
+ struct ethtool_perm_addr *epaddr = NULL;
+ int fd, ret;
+
+ fd = socket (PF_INET, SOCK_DGRAM, 0);
+ if (fd < 0) {
+ nm_log_err (LOGD_HW, "could not open control socket.");
+ return;
}
- if (memcmp (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr))) {
- memcpy (&priv->hw_addr, &req.ifr_hwaddr.sa_data, sizeof (struct ether_addr));
- g_object_notify (G_OBJECT (dev), NM_DEVICE_WIFI_HW_ADDRESS);
+ /* Get permanent MAC address */
+ memset (&req, 0, sizeof (struct ifreq));
+ strncpy (req.ifr_name, nm_device_get_iface (dev), IFNAMSIZ);
+
+ epaddr = g_malloc0 (sizeof (struct ethtool_perm_addr) + ETH_ALEN);
+ epaddr->cmd = ETHTOOL_GPERMADDR;
+ epaddr->size = ETH_ALEN;
+ req.ifr_data = (void *) epaddr;
+
+ errno = 0;
+ ret = ioctl (fd, SIOCETHTOOL, &req);
+ if ((ret < 0) || !nm_ethernet_address_is_valid ((struct ether_addr *) epaddr->data)) {
+ nm_log_err (LOGD_HW | LOGD_ETHER, "(%s): unable to read permanent MAC address (error %d)",
+ nm_device_get_iface (dev), errno);
+ /* Fall back to current address */
+ memcpy (epaddr->data, &priv->hw_addr, ETH_ALEN);
}
-out:
+ if (memcmp (&priv->perm_hw_addr, epaddr->data, ETH_ALEN)) {
+ memcpy (&priv->perm_hw_addr, epaddr->data, ETH_ALEN);
+ g_object_notify (G_OBJECT (dev), NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS);
+ }
+
+ g_free (epaddr);
close (fd);
}
+static void
+real_update_initial_hw_address (NMDevice *dev)
+{
+ NMDeviceWifi *self = NM_DEVICE_WIFI (dev);
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (self);
+ char *mac_str = NULL;
+ guint8 *addr = priv->initial_hw_addr;
+ guint8 zero[ETH_ALEN] = {0,0,0,0,0,0};
+
+ /* This sets initial MAC address from current MAC address. It should only
+ * be called from NMDevice constructor() to really get the initial address.
+ */
+ if (!memcmp (&priv->hw_addr, &zero, ETH_ALEN))
+ real_update_hw_address (dev);
+
+ if (memcmp (&priv->initial_hw_addr, &priv->hw_addr, ETH_ALEN))
+ memcpy (&priv->initial_hw_addr, &priv->hw_addr, ETH_ALEN);
+
+ mac_str = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X",
+ addr[0], addr[1], addr[2], addr[3], addr[4], addr[5]);
+
+ nm_log_dbg (LOGD_DEVICE | LOGD_ETHER, "(%s): read initial MAC address %s",
+ nm_device_get_iface (dev), mac_str);
+
+ g_free (mac_str);
+}
static NMActStageReturn
real_act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
@@ -2956,8 +2839,24 @@ real_act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
NMAccessPoint *ap = NULL;
NMActRequest *req;
NMConnection *connection;
+ NMSettingWireless *s_wireless;
+ const GByteArray *cloned_mac;
GSList *iter;
+ req = nm_device_get_act_request (NM_DEVICE (self));
+ g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+ connection = nm_act_request_get_connection (req);
+ g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE);
+
+ /* Set spoof MAC to the interface */
+ s_wireless = (NMSettingWireless *) nm_connection_get_setting (connection, NM_TYPE_SETTING_WIRELESS);
+ g_assert (s_wireless);
+
+ cloned_mac = nm_setting_wireless_get_cloned_mac_address (s_wireless);
+ if (cloned_mac && (cloned_mac->len == ETH_ALEN))
+ _set_hw_addr (self, (const guint8 *) cloned_mac->data, "set");
+
/* If the user is trying to connect to an AP that NM doesn't yet know about
* (hidden network or something), create an fake AP from the security
* settings in the connection to use until the AP is recognized from the
@@ -2967,12 +2866,6 @@ real_act_stage1_prepare (NMDevice *dev, NMDeviceStateReason *reason)
if (ap)
goto done;
- req = nm_device_get_act_request (NM_DEVICE (self));
- g_return_val_if_fail (req != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
- connection = nm_act_request_get_connection (req);
- g_return_val_if_fail (connection != NULL, NM_ACT_STAGE_RETURN_FAILURE);
-
/* Find a compatible AP in the scan list */
for (iter = priv->ap_list; iter; iter = g_slist_next (iter)) {
NMAccessPoint *candidate = NM_AP (iter->data);
@@ -3121,7 +3014,7 @@ real_act_stage2_config (NMDevice *dev, NMDeviceStateReason *reason)
/* Hook up error signal handler to capture association errors */
id = g_signal_connect (priv->supplicant.iface,
- "connection-error",
+ NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR,
G_CALLBACK (supplicant_iface_connection_error_cb),
self);
priv->supplicant.iface_error_id = id;
@@ -3424,12 +3317,11 @@ real_get_type_capabilities (NMDevice *dev)
static gboolean
spec_match_list (NMDevice *device, const GSList *specs)
{
- struct ether_addr ether;
+ NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device);
char *hwaddr;
gboolean matched;
- nm_device_wifi_get_address (NM_DEVICE_WIFI (device), &ether);
- hwaddr = nm_ether_ntop (&ether);
+ hwaddr = nm_ether_ntop ((struct ether_addr *) &priv->perm_hw_addr);
matched = nm_match_spec_hwaddr (specs, hwaddr);
g_free (hwaddr);
@@ -3477,7 +3369,7 @@ device_state_changed (NMDevice *device,
* acquire a supplicant interface and transition to DISCONNECTED because
* the device is now ready to use.
*/
- if (priv->enabled) {
+ if (priv->enabled && (nm_device_get_firmware_missing (device) == FALSE)) {
gboolean success;
struct iw_range range;
@@ -3570,20 +3462,23 @@ real_set_enabled (NMDeviceInterface *device, gboolean enabled)
nm_log_dbg (LOGD_WIFI, "(%s): enable blocked by failure to bring device up",
nm_device_get_iface (NM_DEVICE (device)));
- /* The device sucks, or HAL was lying to us about the killswitch state */
- priv->enabled = FALSE;
+ if (no_firmware)
+ nm_device_set_firmware_missing (NM_DEVICE (device), TRUE);
+ else {
+ /* The device sucks, or the kernel was lying to us about the killswitch state */
+ priv->enabled = FALSE;
+ }
return;
}
/* Wait for some drivers like ipw3945 to come back to life */
success = wireless_get_range (self, &range, NULL);
- /* iface should be NULL here, but handle it anyway if it's not */
- g_warn_if_fail (priv->supplicant.iface == NULL);
+ /* Re-initialize the supplicant interface and wait for it to be ready */
if (priv->supplicant.iface)
supplicant_interface_release (self);
-
supplicant_interface_acquire (self);
+
nm_log_dbg (LOGD_WIFI, "(%s): enable waiting on supplicant state",
nm_device_get_iface (NM_DEVICE (device)));
} else {
@@ -3645,20 +3540,9 @@ dispose (GObject *object)
priv->periodic_source_id = 0;
}
- /* Clean up all pending supplicant tasks */
- while (priv->supplicant.iface_tasks)
- finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.iface_tasks->data, TRUE);
- while (priv->supplicant.mgr_tasks)
- finish_supplicant_task ((SupplicantStateTask *) priv->supplicant.mgr_tasks->data, TRUE);
-
cleanup_association_attempt (self, TRUE);
supplicant_interface_release (self);
- if (priv->supplicant.mgr_state_id) {
- g_signal_handler_disconnect (priv->supplicant.mgr, priv->supplicant.mgr_state_id);
- priv->supplicant.mgr_state_id = 0;
- }
-
if (priv->supplicant.mgr) {
g_object_unref (priv->supplicant.mgr);
priv->supplicant.mgr = NULL;
@@ -3687,12 +3571,13 @@ get_property (GObject *object, guint prop_id,
{
NMDeviceWifi *device = NM_DEVICE_WIFI (object);
NMDeviceWifiPrivate *priv = NM_DEVICE_WIFI_GET_PRIVATE (device);
- struct ether_addr hw_addr;
switch (prop_id) {
case PROP_HW_ADDRESS:
- nm_device_wifi_get_address (device, &hw_addr);
- g_value_take_string (value, nm_ether_ntop (&hw_addr));
+ g_value_take_string (value, nm_ether_ntop ((struct ether_addr *) &priv->hw_addr));
+ break;
+ case PROP_PERM_HW_ADDRESS:
+ g_value_take_string (value, nm_ether_ntop ((struct ether_addr *) &priv->perm_hw_addr));
break;
case PROP_MODE:
g_value_set_uint (value, nm_device_wifi_get_mode (device));
@@ -3761,6 +3646,8 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
parent_class->bring_up = real_bring_up;
parent_class->take_down = real_take_down;
parent_class->update_hw_address = real_update_hw_address;
+ parent_class->update_permanent_hw_address = real_update_permanent_hw_address;
+ parent_class->update_initial_hw_address = real_update_initial_hw_address;
parent_class->get_best_auto_connection = real_get_best_auto_connection;
parent_class->is_available = real_is_available;
parent_class->connection_secrets_updated = real_connection_secrets_updated;
@@ -3781,8 +3668,15 @@ nm_device_wifi_class_init (NMDeviceWifiClass *klass)
/* Properties */
g_object_class_install_property (object_class, PROP_HW_ADDRESS,
g_param_spec_string (NM_DEVICE_WIFI_HW_ADDRESS,
- "MAC Address",
- "Hardware MAC address",
+ "Active MAC Address",
+ "Currently set hardware MAC address",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property (object_class, PROP_PERM_HW_ADDRESS,
+ g_param_spec_string (NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS,
+ "Permanent MAC Address",
+ "Permanent hardware MAC address",
NULL,
G_PARAM_READABLE));
diff --git a/src/nm-device-wifi.h b/src/nm-device-wifi.h
index 11ac885735..31ac5ad5b5 100644
--- a/src/nm-device-wifi.h
+++ b/src/nm-device-wifi.h
@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2005 - 2008 Red Hat, Inc.
+ * Copyright (C) 2005 - 2010 Red Hat, Inc.
* Copyright (C) 2006 - 2008 Novell, Inc.
*/
@@ -43,6 +43,7 @@ G_BEGIN_DECLS
#define NM_DEVICE_WIFI_HW_ADDRESS "hw-address"
+#define NM_DEVICE_WIFI_PERMANENT_HW_ADDRESS "perm-hw-address"
#define NM_DEVICE_WIFI_MODE "mode"
#define NM_DEVICE_WIFI_BITRATE "bitrate"
#define NM_DEVICE_WIFI_ACTIVE_ACCESS_POINT "active-access-point"
@@ -85,18 +86,15 @@ NMDevice *nm_device_wifi_new (const char *udi,
const char *iface,
const char *driver);
-void nm_device_wifi_get_address (NMDeviceWifi *dev,
- struct ether_addr *addr);
+void nm_device_wifi_get_address (NMDeviceWifi *dev, struct ether_addr *addr);
-void nm_device_wifi_get_bssid (NMDeviceWifi *dev,
- struct ether_addr *bssid);
+void nm_device_wifi_get_bssid (NMDeviceWifi *dev, struct ether_addr *bssid);
-const GByteArray * nm_device_wifi_get_ssid (NMDeviceWifi *self);
+const GByteArray * nm_device_wifi_get_ssid (NMDeviceWifi *self);
-gboolean nm_device_wifi_set_mode (NMDeviceWifi *self,
- const NM80211Mode mode);
+gboolean nm_device_wifi_set_mode (NMDeviceWifi *self, const NM80211Mode mode);
-NM80211Mode nm_device_wifi_get_mode (NMDeviceWifi *self);
+NM80211Mode nm_device_wifi_get_mode (NMDeviceWifi *self);
NMAccessPoint * nm_device_wifi_get_activation_ap (NMDeviceWifi *self);
diff --git a/src/nm-device.c b/src/nm-device.c
index 7d9913d478..e8f0b206ca 100644
--- a/src/nm-device.c
+++ b/src/nm-device.c
@@ -19,6 +19,7 @@
* Copyright (C) 2006 - 2008 Novell, Inc.
*/
+#include <config.h>
#include <glib.h>
#include <glib/gi18n.h>
#include <dbus/dbus.h>
@@ -42,7 +43,7 @@
#include "nm-system.h"
#include "nm-dhcp-manager.h"
#include "nm-dbus-manager.h"
-#include "nm-named-manager.h"
+#include "nm-dns-manager.h"
#include "nm-utils.h"
#include "nm-logging.h"
#include "nm-netlink-monitor.h"
@@ -92,6 +93,7 @@ typedef struct {
char * driver;
gboolean managed; /* whether managed by NM or not */
RfKillType rfkill_type;
+ gboolean firmware_missing;
guint32 ip4_address;
@@ -158,6 +160,7 @@ static void nm_device_deactivate (NMDeviceInterface *device, NMDeviceStateReason
static gboolean device_disconnect (NMDeviceInterface *device, GError **error);
static gboolean spec_match_list (NMDeviceInterface *device, const GSList *specs);
static NMConnection *connection_match_config (NMDeviceInterface *device, const GSList *connections);
+static gboolean can_assume_connections (NMDeviceInterface *device);
static void nm_device_activate_schedule_stage5_ip_config_commit (NMDevice *self, int family);
@@ -195,6 +198,7 @@ device_interface_init (NMDeviceInterface *device_interface_class)
device_interface_class->disconnect = device_disconnect;
device_interface_class->spec_match_list = spec_match_list;
device_interface_class->connection_match_config = connection_match_config;
+ device_interface_class->can_assume_connections = can_assume_connections;
}
@@ -282,6 +286,12 @@ constructor (GType type,
if (NM_DEVICE_GET_CLASS (dev)->update_hw_address)
NM_DEVICE_GET_CLASS (dev)->update_hw_address (dev);
+ if (NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address)
+ NM_DEVICE_GET_CLASS (dev)->update_permanent_hw_address (dev);
+
+ if (NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address)
+ NM_DEVICE_GET_CLASS (dev)->update_initial_hw_address (dev);
+
priv->dhcp_manager = nm_dhcp_manager_get ();
update_accept_ra_save (dev);
@@ -387,11 +397,12 @@ void
nm_device_set_ip_iface (NMDevice *self, const char *iface)
{
NMDevicePrivate *priv;
+ char *old_ip_iface;
g_return_if_fail (NM_IS_DEVICE (self));
priv = NM_DEVICE_GET_PRIVATE (self);
- g_free (priv->ip_iface);
+ old_ip_iface = priv->ip_iface;
priv->ip_ifindex = 0;
priv->ip_iface = g_strdup (iface);
@@ -401,6 +412,11 @@ nm_device_set_ip_iface (NMDevice *self, const char *iface)
nm_log_warn (LOGD_HW, "(%s): failed to look up interface index", iface);
}
}
+
+ /* Emit change notification */
+ if (g_strcmp0 (old_ip_iface, priv->ip_iface))
+ g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_IP_IFACE);
+ g_free (old_ip_iface);
}
@@ -492,6 +508,11 @@ nm_device_get_act_request (NMDevice *self)
gboolean
nm_device_is_available (NMDevice *self)
{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+
+ if (priv->firmware_missing)
+ return FALSE;
+
if (NM_DEVICE_GET_CLASS (self)->is_available)
return NM_DEVICE_GET_CLASS (self)->is_available (self);
return TRUE;
@@ -1013,6 +1034,7 @@ aipd_get_ip4_config (NMDevice *self, NMDeviceStateReason *reason)
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
NMIP4Config *config = NULL;
NMIP4Address *addr;
+ NMIP4Route *route;
g_return_val_if_fail (priv->aipd_addr > 0, NULL);
@@ -1027,6 +1049,14 @@ aipd_get_ip4_config (NMDevice *self, NMDeviceStateReason *reason)
nm_ip4_address_set_prefix (addr, 16);
nm_ip4_config_take_address (config, addr);
+ /* Add a multicast route for link-local connections: destination= 224.0.0.0, netmask=240.0.0.0 */
+ route = nm_ip4_route_new ();
+ nm_ip4_route_set_dest (route, (guint32) htonl (0xE0000000L));
+ nm_ip4_route_set_prefix (route, 4);
+ nm_ip4_route_set_next_hop (route, (guint32) 0);
+ nm_ip4_route_set_metric (route, 0);
+ nm_ip4_config_take_route (config, route);
+
return config;
}
@@ -1213,7 +1243,7 @@ static gboolean
aipd_exec (NMDevice *self, GError **error)
{
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
- char *argv[5];
+ char *argv[6], *cmdline;
gboolean success = FALSE;
const char **aipd_binary = NULL;
static const char *aipd_paths[] = {
@@ -1221,6 +1251,7 @@ aipd_exec (NMDevice *self, GError **error)
"/usr/local/sbin/avahi-autoipd",
NULL
};
+ int i = 0;
aipd_cleanup (self);
@@ -1237,11 +1268,17 @@ aipd_exec (NMDevice *self, GError **error)
return FALSE;
}
- argv[0] = (char *) (*aipd_binary);
- argv[1] = "--script";
- argv[2] = LIBEXECDIR "/nm-avahi-autoipd.action";
- argv[3] = (char *) nm_device_get_ip_iface (self);
- argv[4] = NULL;
+ argv[i++] = (char *) (*aipd_binary);
+ argv[i++] = "--script";
+ argv[i++] = LIBEXECDIR "/nm-avahi-autoipd.action";
+ if (nm_logging_level_enabled (LOGL_DEBUG))
+ argv[i++] = "--debug";
+ argv[i++] = (char *) nm_device_get_ip_iface (self);
+ argv[i++] = NULL;
+
+ cmdline = g_strjoinv (" ", argv);
+ nm_log_dbg(LOGD_AUTOIP4, "running: %s", cmdline);
+ g_free (cmdline);
success = g_spawn_async ("/", argv, NULL, G_SPAWN_DO_NOT_REAP_CHILD,
&aipd_child_setup, NULL, &(priv->aipd_pid), error);
@@ -1311,6 +1348,7 @@ handle_dhcp_lease_change (NMDevice *device, gboolean ipv6)
nm_dhcp_client_foreach_option (priv->dhcp6_client,
dhcp6_add_option_cb,
priv->dhcp6_config);
+ nm_utils_call_dispatcher ("dhcp6-change", connection, device, NULL);
} else {
nm_log_warn (LOGD_DHCP6, "(%s): failed to update IPv6 config in response to DHCP event.",
nm_device_get_ip_iface (device));
@@ -1335,6 +1373,7 @@ handle_dhcp_lease_change (NMDevice *device, gboolean ipv6)
nm_dhcp_client_foreach_option (priv->dhcp4_client,
dhcp4_add_option_cb,
priv->dhcp4_config);
+ nm_utils_call_dispatcher ("dhcp4-change", connection, device, NULL);
} else {
nm_log_warn (LOGD_DHCP6, "(%s): failed to update IPv4 config in response to DHCP event.",
nm_device_get_ip_iface (device));
@@ -1415,22 +1454,6 @@ dhcp_state_changed (NMDHCPClient *client,
} else if (nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED)
nm_device_state_changed (device, NM_DEVICE_STATE_FAILED, NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
break;
- case DHC_STOP:
- case DHC_STOP6:
- case DHC_EXPIRE:
- case DHC_EXPIRE6:
- if (dev_state == NM_DEVICE_STATE_ACTIVATED) {
- if (ipv6)
- nm_dhcp6_config_reset (priv->dhcp6_config);
- else
- nm_dhcp4_config_reset (priv->dhcp4_config);
-
- /* dhclient quit and can't get/renew a lease; so kill the connection */
- nm_device_state_changed (device,
- NM_DEVICE_STATE_FAILED,
- NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED);
- }
- break;
default:
break;
}
@@ -1564,6 +1587,8 @@ dhcp6_start (NMDevice *self,
NMSettingConnection *s_con;
const char *uuid;
const char *ip_iface;
+ const struct in6_addr dest = { { { 0xFF,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 } } };
+ int err;
if (!connection) {
NMActRequest *req;
@@ -1584,6 +1609,18 @@ dhcp6_start (NMDevice *self,
g_object_unref (priv->dhcp6_config);
priv->dhcp6_config = nm_dhcp6_config_new ();
+ /* DHCPv6 communicates with the DHCPv6 server via two multicast addresses,
+ * ff02::1:2 (link-scope) and ff05::1:3 (site-scope). Make sure we have
+ * a multicast route (ff00::/8) for client <-> server communication.
+ */
+ err = nm_system_set_ip6_route (priv->ip_iface ? priv->ip_ifindex : priv->ifindex,
+ &dest, 8, NULL, 256, 0, RTPROT_BOOT, RT_TABLE_LOCAL, NULL);
+ if (err) {
+ nm_log_err (LOGD_DEVICE | LOGD_IP6,
+ "(%s): failed to add IPv6 multicast route: %s",
+ priv->ip_iface ? priv->ip_iface : priv->iface, nl_geterror ());
+ }
+
s_con = (NMSettingConnection *) nm_connection_get_setting (connection, NM_TYPE_SETTING_CONNECTION);
g_assert (s_con);
uuid = nm_setting_connection_get_uuid (s_con);
@@ -2739,6 +2776,8 @@ nm_device_deactivate_quickly (NMDevice *self)
dnsmasq_cleanup (self);
aipd_cleanup (self);
+ nm_device_set_ip_iface (self, NULL);
+
/* Turn off router advertisements until they are needed */
if (priv->ip6_accept_ra_path)
nm_utils_do_sysctl (priv->ip6_accept_ra_path, "0\n");
@@ -2986,7 +3025,7 @@ nm_device_set_ip4_config (NMDevice *self,
NMIP4Config *old_config = NULL;
gboolean success = TRUE;
NMIP4ConfigCompareFlags diff = NM_IP4_COMPARE_FLAG_ALL;
- NMNamedManager *named_mgr;
+ NMDnsManager *dns_mgr;
g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
g_return_val_if_fail (reason != NULL, FALSE);
@@ -3003,10 +3042,10 @@ nm_device_set_ip4_config (NMDevice *self,
if (diff == NM_IP4_COMPARE_FLAG_NONE)
return TRUE;
- named_mgr = nm_named_manager_get ();
+ dns_mgr = nm_dns_manager_get (NULL);
if (old_config) {
- /* Remove any previous IP4 Config from the named manager */
- nm_named_manager_remove_ip4_config (named_mgr, ip_iface, old_config);
+ /* Remove any previous IP4 Config from the DNS manager */
+ nm_dns_manager_remove_ip4_config (dns_mgr, ip_iface, old_config);
g_object_unref (old_config);
priv->ip4_config = NULL;
}
@@ -3025,13 +3064,13 @@ nm_device_set_ip4_config (NMDevice *self,
if (!nm_ip4_config_get_dbus_path (new_config))
nm_ip4_config_export (new_config);
- /* Add the DNS information to the named manager */
- nm_named_manager_add_ip4_config (named_mgr, ip_iface, new_config, NM_NAMED_IP_CONFIG_TYPE_DEFAULT);
+ /* Add the DNS information to the DNS manager */
+ nm_dns_manager_add_ip4_config (dns_mgr, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
nm_device_update_ip4_address (self);
}
}
- g_object_unref (named_mgr);
+ g_object_unref (dns_mgr);
g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_IP4_CONFIG);
@@ -3089,7 +3128,7 @@ nm_device_set_ip6_config (NMDevice *self,
NMIP6Config *old_config = NULL;
gboolean success = TRUE;
NMIP6ConfigCompareFlags diff = NM_IP6_COMPARE_FLAG_ALL;
- NMNamedManager *named_mgr;
+ NMDnsManager *dns_mgr;
g_return_val_if_fail (NM_IS_DEVICE (self), FALSE);
g_return_val_if_fail (reason != NULL, FALSE);
@@ -3106,10 +3145,10 @@ nm_device_set_ip6_config (NMDevice *self,
if (diff == NM_IP6_COMPARE_FLAG_NONE)
return TRUE;
- named_mgr = nm_named_manager_get ();
+ dns_mgr = nm_dns_manager_get (NULL);
if (old_config) {
- /* Remove any previous IP6 Config from the named manager */
- nm_named_manager_remove_ip6_config (named_mgr, ip_iface, old_config);
+ /* Remove any previous IP6 Config from the DNS manager */
+ nm_dns_manager_remove_ip6_config (dns_mgr, ip_iface, old_config);
g_object_unref (old_config);
priv->ip6_config = NULL;
}
@@ -3128,11 +3167,11 @@ nm_device_set_ip6_config (NMDevice *self,
if (!nm_ip6_config_get_dbus_path (new_config))
nm_ip6_config_export (new_config);
- /* Add the DNS information to the named manager */
- nm_named_manager_add_ip6_config (named_mgr, ip_iface, new_config, NM_NAMED_IP_CONFIG_TYPE_DEFAULT);
+ /* Add the DNS information to the DNS manager */
+ nm_dns_manager_add_ip6_config (dns_mgr, ip_iface, new_config, NM_DNS_IP_CONFIG_TYPE_DEFAULT);
}
}
- g_object_unref (named_mgr);
+ g_object_unref (dns_mgr);
g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_IP6_CONFIG);
@@ -3279,7 +3318,7 @@ dispose (GObject *object)
/* Don't down can-assume-connection capable devices that are activated with
* a connection that can be assumed.
*/
- if ( nm_device_interface_can_assume_connection (NM_DEVICE_INTERFACE (self))
+ if ( nm_device_interface_can_assume_connections (NM_DEVICE_INTERFACE (self))
&& (nm_device_get_state (self) == NM_DEVICE_STATE_ACTIVATED)) {
NMConnection *connection;
NMSettingIP4Config *s_ip4;
@@ -3314,13 +3353,6 @@ dispose (GObject *object)
addrconf6_cleanup (self);
dnsmasq_cleanup (self);
- /* reset the saved RA value */
- if (priv->ip6_accept_ra_path) {
- nm_utils_do_sysctl (priv->ip6_accept_ra_path,
- priv->ip6_accept_ra_save ? "1\n" : "0\n");
- }
- g_free (priv->ip6_accept_ra_path);
-
/* Take the device itself down and clear its IPv4 configuration */
if (priv->managed && take_down) {
NMDeviceStateReason ignored = NM_DEVICE_STATE_REASON_NONE;
@@ -3329,6 +3361,13 @@ dispose (GObject *object)
nm_device_set_ip4_config (self, NULL, FALSE, &ignored);
}
+ /* reset the saved RA value */
+ if (priv->ip6_accept_ra_path) {
+ nm_utils_do_sysctl (priv->ip6_accept_ra_path,
+ priv->ip6_accept_ra_save ? "1\n" : "0\n");
+ }
+ g_free (priv->ip6_accept_ra_path);
+
activation_source_clear (self, TRUE, AF_INET);
activation_source_clear (self, TRUE, AF_INET6);
@@ -3344,7 +3383,8 @@ finalize (GObject *object)
NMDevice *self = NM_DEVICE (object);
NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
- g_object_unref (priv->dhcp_manager);
+ if (priv->dhcp_manager)
+ g_object_unref (priv->dhcp_manager);
g_free (priv->udi);
g_free (priv->iface);
@@ -3380,6 +3420,8 @@ set_property (GObject *object, guint prop_id,
}
}
break;
+ case NM_DEVICE_INTERFACE_PROP_IP_IFACE:
+ break;
case NM_DEVICE_INTERFACE_PROP_DRIVER:
priv->driver = g_strdup (g_value_get_string (value));
break;
@@ -3392,6 +3434,9 @@ set_property (GObject *object, guint prop_id,
case NM_DEVICE_INTERFACE_PROP_MANAGED:
priv->managed = g_value_get_boolean (value);
break;
+ case NM_DEVICE_INTERFACE_PROP_FIRMWARE_MISSING:
+ priv->firmware_missing = g_value_get_boolean (value);
+ break;
case NM_DEVICE_INTERFACE_PROP_DEVICE_TYPE:
g_return_if_fail (priv->type == NM_DEVICE_TYPE_UNKNOWN);
priv->type = g_value_get_uint (value);
@@ -3426,6 +3471,12 @@ get_property (GObject *object, guint prop_id,
case NM_DEVICE_INTERFACE_PROP_IFACE:
g_value_set_string (value, priv->iface);
break;
+ case NM_DEVICE_INTERFACE_PROP_IP_IFACE:
+ if ((state == NM_DEVICE_STATE_ACTIVATED) || (state == NM_DEVICE_STATE_IP_CONFIG))
+ g_value_set_string (value, nm_device_get_ip_iface (self));
+ else
+ g_value_set_string (value, NULL);
+ break;
case NM_DEVICE_INTERFACE_PROP_IFINDEX:
g_value_set_int (value, priv->ifindex);
break;
@@ -3479,6 +3530,9 @@ get_property (GObject *object, guint prop_id,
case NM_DEVICE_INTERFACE_PROP_MANAGED:
g_value_set_boolean (value, priv->managed);
break;
+ case NM_DEVICE_INTERFACE_PROP_FIRMWARE_MISSING:
+ g_value_set_boolean (value, priv->firmware_missing);
+ break;
case NM_DEVICE_INTERFACE_PROP_TYPE_DESC:
g_value_set_string (value, priv->type_desc);
break;
@@ -3528,6 +3582,10 @@ nm_device_class_init (NMDeviceClass *klass)
NM_DEVICE_INTERFACE_IFACE);
g_object_class_override_property (object_class,
+ NM_DEVICE_INTERFACE_PROP_IP_IFACE,
+ NM_DEVICE_INTERFACE_IP_IFACE);
+
+ g_object_class_override_property (object_class,
NM_DEVICE_INTERFACE_PROP_IFINDEX,
NM_DEVICE_INTERFACE_IFINDEX);
@@ -3572,6 +3630,10 @@ nm_device_class_init (NMDeviceClass *klass)
NM_DEVICE_INTERFACE_MANAGED);
g_object_class_override_property (object_class,
+ NM_DEVICE_INTERFACE_PROP_FIRMWARE_MISSING,
+ NM_DEVICE_INTERFACE_FIRMWARE_MISSING);
+
+ g_object_class_override_property (object_class,
NM_DEVICE_INTERFACE_PROP_TYPE_DESC,
NM_DEVICE_INTERFACE_TYPE_DESC);
@@ -3616,6 +3678,27 @@ unavailable_to_disconnected (gpointer user_data)
}
void
+nm_device_set_firmware_missing (NMDevice *self, gboolean new_missing)
+{
+ NMDevicePrivate *priv;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (NM_IS_DEVICE (self));
+
+ priv = NM_DEVICE_GET_PRIVATE (self);
+ if (priv->firmware_missing != new_missing) {
+ priv->firmware_missing = new_missing;
+ g_object_notify (G_OBJECT (self), NM_DEVICE_INTERFACE_FIRMWARE_MISSING);
+ }
+}
+
+gboolean
+nm_device_get_firmware_missing (NMDevice *self)
+{
+ return NM_DEVICE_GET_PRIVATE (self)->firmware_missing;
+}
+
+void
nm_device_state_changed (NMDevice *device,
NMDeviceState state,
NMDeviceStateReason reason)
@@ -3627,7 +3710,12 @@ nm_device_state_changed (NMDevice *device,
g_return_if_fail (NM_IS_DEVICE (device));
- if (priv->state == state)
+ /* Do nothing if state isn't changing, but as a special case allow
+ * re-setting UNAVAILABLE if the device is missing firmware so that we
+ * can retry device initialization.
+ */
+ if ( (priv->state == state)
+ && !(state == NM_DEVICE_STATE_UNAVAILABLE && priv->firmware_missing))
return;
old_state = priv->state;
@@ -3647,14 +3735,15 @@ nm_device_state_changed (NMDevice *device,
*/
switch (state) {
case NM_DEVICE_STATE_UNMANAGED:
+ nm_device_set_firmware_missing (device, FALSE);
if (old_state > NM_DEVICE_STATE_UNMANAGED)
nm_device_take_down (device, TRUE, reason);
break;
case NM_DEVICE_STATE_UNAVAILABLE:
- if (old_state == NM_DEVICE_STATE_UNMANAGED) {
- if (!nm_device_bring_up (device, TRUE, &no_firmware) && no_firmware) {
- nm_log_warn (LOGD_HW, "%s: firmware may be missing.", nm_device_get_iface (device));
- }
+ if (old_state == NM_DEVICE_STATE_UNMANAGED || priv->firmware_missing) {
+ if (!nm_device_bring_up (device, TRUE, &no_firmware) && no_firmware)
+ nm_log_warn (LOGD_HW, "(%s): firmware may be missing.", nm_device_get_iface (device));
+ nm_device_set_firmware_missing (device, no_firmware ? TRUE : FALSE);
}
/* Ensure the device gets deactivated in response to stuff like
* carrier changes or rfkill. But don't deactivate devices that are
@@ -3682,7 +3771,7 @@ nm_device_state_changed (NMDevice *device,
case NM_DEVICE_STATE_UNAVAILABLE:
/* If the device can activate now (ie, it's got a carrier, the supplicant
* is active, or whatever) schedule a delayed transition to DISCONNECTED
- * to get things rolling. The device can't transition immediately becuase
+ * to get things rolling. The device can't transition immediately because
* we can't change states again from the state handler for a variety of
* reasons.
*/
@@ -3703,7 +3792,7 @@ nm_device_state_changed (NMDevice *device,
case NM_DEVICE_STATE_FAILED:
nm_log_warn (LOGD_DEVICE, "Activation (%s) failed.", nm_device_get_iface (device));
/* Schedule the transition to DISCONNECTED. The device can't transition
- * immediately becuase we can't change states again from the state
+ * immediately because we can't change states again from the state
* handler for a variety of reasons.
*/
priv->failed_to_disconnected_id = g_idle_add (failed_to_disconnected, device);
@@ -3787,6 +3876,14 @@ connection_match_config (NMDeviceInterface *device, const GSList *connections)
return NULL;
}
+static gboolean
+can_assume_connections (NMDeviceInterface *device)
+{
+ g_return_val_if_fail (device != NULL, FALSE);
+
+ return !!NM_DEVICE_GET_CLASS (device)->connection_match_config;
+}
+
void
nm_device_set_dhcp_timeout (NMDevice *device, guint32 timeout)
{
diff --git a/src/nm-device.h b/src/nm-device.h
index 202f392413..db2b1b7db0 100644
--- a/src/nm-device.h
+++ b/src/nm-device.h
@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2005 - 2008 Red Hat, Inc.
+ * Copyright (C) 2005 - 2010 Red Hat, Inc.
* Copyright (C) 2006 - 2008 Novell, Inc.
*/
@@ -72,6 +72,8 @@ typedef struct {
void (*take_down) (NMDevice *self);
void (* update_hw_address) (NMDevice *self);
+ void (* update_permanent_hw_address) (NMDevice *self);
+ void (* update_initial_hw_address) (NMDevice *self);
guint32 (* get_type_capabilities) (NMDevice *self);
guint32 (* get_generic_capabilities) (NMDevice *self);
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index 6dcc2dbcfe..0ae3d1bee4 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -56,6 +56,9 @@ typedef struct {
GArray *wins;
+ GArray *nis;
+ char * nis_domain;
+
GSList *routes;
gboolean never_default;
@@ -488,6 +491,70 @@ nm_ip4_config_set_never_default (NMIP4Config *config, gboolean never_default)
NM_IP4_CONFIG_GET_PRIVATE (config)->never_default = never_default;
}
+void nm_ip4_config_add_nis_server (NMIP4Config *config, guint32 nis)
+{
+ NMIP4ConfigPrivate *priv;
+ int i;
+
+ g_return_if_fail (NM_IS_IP4_CONFIG (config));
+ g_return_if_fail (nis > 0);
+
+ priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+ for (i = 0; i < priv->nis->len; i++) {
+ guint32 s = g_array_index (priv->nis, guint32, i);
+
+ /* No dupes */
+ g_return_if_fail (nis != s);
+ }
+
+ g_array_append_val (priv->nis, nis);
+}
+
+guint32 nm_ip4_config_get_nis_server (NMIP4Config *config, guint i)
+{
+ g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0);
+
+ return g_array_index (NM_IP4_CONFIG_GET_PRIVATE (config)->nis, guint32, i);
+}
+
+guint32 nm_ip4_config_get_num_nis_servers (NMIP4Config *config)
+{
+ g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0);
+
+ return NM_IP4_CONFIG_GET_PRIVATE (config)->nis->len;
+}
+
+void nm_ip4_config_reset_nis_servers (NMIP4Config *config)
+{
+ NMIP4ConfigPrivate *priv;
+
+ g_return_if_fail (NM_IS_IP4_CONFIG (config));
+
+ priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+ if (priv->nis->len)
+ g_array_remove_range (priv->nis, 0, priv->nis->len);
+}
+
+void
+nm_ip4_config_set_nis_domain (NMIP4Config *config, const char *domain)
+{
+ NMIP4ConfigPrivate *priv;
+
+ g_return_if_fail (NM_IS_IP4_CONFIG (config));
+
+ priv = NM_IP4_CONFIG_GET_PRIVATE (config);
+ g_free (priv->nis_domain);
+ priv->nis_domain = g_strdup (domain);
+}
+
+const char *
+nm_ip4_config_get_nis_domain (NMIP4Config *config)
+{
+ g_return_val_if_fail (NM_IS_IP4_CONFIG (config), 0);
+
+ return NM_IP4_CONFIG_GET_PRIVATE (config)->nis_domain;
+}
+
/* libnl convenience/conversion functions */
static int ip4_addr_to_rtnl_local (guint32 ip4_address, struct rtnl_addr *addr)
@@ -700,6 +767,15 @@ nm_ip4_config_diff (NMIP4Config *a, NMIP4Config *b)
|| !addr_array_compare (b_priv->wins, a_priv->wins))
flags |= NM_IP4_COMPARE_FLAG_WINS_SERVERS;
+ if ( (a_priv->nis->len != b_priv->nis->len)
+ || !addr_array_compare (a_priv->nis, b_priv->nis)
+ || !addr_array_compare (b_priv->nis, a_priv->nis))
+ flags |= NM_IP4_COMPARE_FLAG_NIS_SERVERS;
+
+ if ( (a_priv->nis_domain || b_priv->nis_domain)
+ && (g_strcmp0 (a_priv->nis_domain, b_priv->nis_domain) != 0))
+ flags |= NM_IP4_COMPARE_FLAG_NIS_DOMAIN;
+
if ( !route_slist_compare (a_priv->routes, b_priv->routes)
|| !route_slist_compare (b_priv->routes, a_priv->routes))
flags |= NM_IP4_COMPARE_FLAG_ROUTES;
@@ -732,6 +808,7 @@ nm_ip4_config_init (NMIP4Config *config)
priv->wins = g_array_new (FALSE, TRUE, sizeof (guint32));
priv->domains = g_ptr_array_sized_new (3);
priv->searches = g_ptr_array_sized_new (3);
+ priv->nis = g_array_new (FALSE, TRUE, sizeof (guint32));
}
static void
@@ -745,6 +822,8 @@ finalize (GObject *object)
g_array_free (priv->nameservers, TRUE);
g_ptr_array_free (priv->domains, TRUE);
g_ptr_array_free (priv->searches, TRUE);
+ g_array_free (priv->nis, TRUE);
+ g_free (priv->nis_domain);
G_OBJECT_CLASS (nm_ip4_config_parent_class)->finalize (object);
}
diff --git a/src/nm-ip4-config.h b/src/nm-ip4-config.h
index 04999f8779..2d27acff03 100644
--- a/src/nm-ip4-config.h
+++ b/src/nm-ip4-config.h
@@ -99,6 +99,15 @@ void nm_ip4_config_set_mss (NMIP4Config *config, guint32 ms
gboolean nm_ip4_config_get_never_default (NMIP4Config *config);
void nm_ip4_config_set_never_default (NMIP4Config *config, gboolean never_default);
+void nm_ip4_config_add_nis_server (NMIP4Config *config, guint32 nis);
+guint32 nm_ip4_config_get_nis_server (NMIP4Config *config, guint i);
+guint32 nm_ip4_config_get_num_nis_servers (NMIP4Config *config);
+void nm_ip4_config_reset_nis_servers (NMIP4Config *config);
+
+void nm_ip4_config_set_nis_domain (NMIP4Config *config, const char *domain);
+const char * nm_ip4_config_get_nis_domain (NMIP4Config *config);
+
+
/* Flags for nm_ip4_config_to_rtnl_addr() */
#define NM_RTNL_ADDR_NONE 0x0000
#define NM_RTNL_ADDR_ADDR 0x0001
@@ -122,6 +131,8 @@ typedef enum {
NM_IP4_COMPARE_FLAG_MTU = 0x00000040,
NM_IP4_COMPARE_FLAG_MSS = 0x00000080,
NM_IP4_COMPARE_FLAG_WINS_SERVERS= 0x00000100,
+ NM_IP4_COMPARE_FLAG_NIS_SERVERS = 0x00000200,
+ NM_IP4_COMPARE_FLAG_NIS_DOMAIN = 0x00000400,
NM_IP4_COMPARE_FLAG_ALL = 0xFFFFFFFF /* match everything */
} NMIP4ConfigCompareFlags;
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index 2fea38f331..4c6c5c6273 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.c
@@ -655,6 +655,27 @@ finalize (GObject *object)
}
static void
+nameservers_to_gvalue (GArray *array, GValue *value)
+{
+ GPtrArray *dns;
+ guint i = 0;
+
+ dns = g_ptr_array_new ();
+
+ while (array && (i < array->len)) {
+ struct in6_addr *addr;
+ GByteArray *bytearray;
+ addr = &g_array_index (array, struct in6_addr, i++);
+
+ bytearray = g_byte_array_sized_new (16);
+ g_byte_array_append (bytearray, (guint8 *) addr->s6_addr, 16);
+ g_ptr_array_add (dns, bytearray);
+ }
+
+ g_value_take_boxed (value, dns);
+}
+
+static void
get_property (GObject *object, guint prop_id,
GValue *value, GParamSpec *pspec)
{
@@ -665,7 +686,7 @@ get_property (GObject *object, guint prop_id,
nm_utils_ip6_addresses_to_gvalue (priv->addresses, value);
break;
case PROP_NAMESERVERS:
- g_value_set_boxed (value, priv->nameservers);
+ nameservers_to_gvalue (priv->nameservers, value);
break;
case PROP_DOMAINS:
g_value_set_boxed (value, priv->domains);
diff --git a/src/nm-manager-auth.c b/src/nm-manager-auth.c
new file mode 100644
index 0000000000..44c82c23e6
--- /dev/null
+++ b/src/nm-manager-auth.c
@@ -0,0 +1,424 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#include "nm-manager-auth.h"
+#include "nm-logging.h"
+
+#include <dbus/dbus-glib-lowlevel.h>
+#include <string.h>
+
+struct NMAuthChain {
+ guint32 refcount;
+ PolkitAuthority *authority;
+ GSList *calls;
+ GHashTable *data;
+
+ DBusGMethodInvocation *context;
+ char *owner;
+ GError *error;
+
+ NMAuthChainResultFunc done_func;
+ NMAuthChainCallFunc call_func;
+ gpointer user_data;
+};
+
+typedef struct {
+ NMAuthChain *chain;
+ GCancellable *cancellable;
+ char *permission;
+ gboolean disposed;
+} PolkitCall;
+
+typedef struct {
+ gpointer data;
+ GDestroyNotify destroy;
+} ChainData;
+
+static void
+free_data (gpointer data)
+{
+ ChainData *tmp = data;
+
+ if (tmp->destroy)
+ tmp->destroy (tmp->data);
+ memset (tmp, 0, sizeof (ChainData));
+ g_free (tmp);
+}
+
+static void
+default_call_func (NMAuthChain *chain,
+ const char *permission,
+ GError *error,
+ NMAuthCallResult result,
+ gpointer user_data)
+{
+ if (!error)
+ nm_auth_chain_set_data (chain, permission, GUINT_TO_POINTER (result), NULL);
+}
+
+static NMAuthChain *
+_auth_chain_new (PolkitAuthority *authority,
+ DBusGMethodInvocation *context,
+ DBusGProxy *proxy,
+ DBusMessage *message,
+ NMAuthChainResultFunc done_func,
+ gpointer user_data)
+{
+ NMAuthChain *self;
+
+ g_return_val_if_fail (context || proxy || message, NULL);
+
+ self = g_malloc0 (sizeof (NMAuthChain));
+ self->refcount = 1;
+ self->authority = g_object_ref (authority);
+ self->data = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, free_data);
+ self->done_func = done_func;
+ self->call_func = /* call_func ? call_func : */ default_call_func;
+ self->user_data = user_data;
+ self->context = context;
+
+ if (proxy)
+ self->owner = g_strdup (dbus_g_proxy_get_bus_name (proxy));
+ else if (context)
+ self->owner = dbus_g_method_get_sender (context);
+ else if (message)
+ self->owner = g_strdup (dbus_message_get_sender (message));
+
+ if (!self->owner) {
+ /* Need an owner */
+ g_warn_if_fail (self->owner);
+ nm_auth_chain_unref (self);
+ self = NULL;
+ }
+
+ return self;
+}
+
+NMAuthChain *
+nm_auth_chain_new (PolkitAuthority *authority,
+ DBusGMethodInvocation *context,
+ DBusGProxy *proxy,
+ NMAuthChainResultFunc done_func,
+ gpointer user_data)
+{
+ return _auth_chain_new (authority, context, proxy, NULL, done_func, user_data);
+}
+
+NMAuthChain *
+nm_auth_chain_new_raw_message (PolkitAuthority *authority,
+ DBusMessage *message,
+ NMAuthChainResultFunc done_func,
+ gpointer user_data)
+{
+ return _auth_chain_new (authority, NULL, NULL, message, done_func, user_data);
+}
+
+gpointer
+nm_auth_chain_get_data (NMAuthChain *self, const char *tag)
+{
+ ChainData *tmp;
+
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (tag != NULL, NULL);
+
+ tmp = g_hash_table_lookup (self->data, tag);
+ return tmp ? tmp->data : NULL;
+}
+
+void
+nm_auth_chain_set_data (NMAuthChain *self,
+ const char *tag,
+ gpointer data,
+ GDestroyNotify data_destroy)
+{
+ ChainData *tmp;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (tag != NULL);
+
+ if (data == NULL)
+ g_hash_table_remove (self->data, tag);
+ else {
+ tmp = g_malloc0 (sizeof (ChainData));
+ tmp->data = data;
+ tmp->destroy = data_destroy;
+
+ g_hash_table_insert (self->data, g_strdup (tag), tmp);
+ }
+}
+
+static void
+nm_auth_chain_check_done (NMAuthChain *self)
+{
+ g_return_if_fail (self != NULL);
+
+ if (g_slist_length (self->calls) == 0) {
+ /* Ensure we say alive across the callback */
+ self->refcount++;
+ self->done_func (self, self->error, self->context, self->user_data);
+ nm_auth_chain_unref (self);
+ }
+}
+
+static void
+polkit_call_cancel (PolkitCall *call)
+{
+ call->disposed = TRUE;
+ g_cancellable_cancel (call->cancellable);
+}
+
+static void
+polkit_call_free (PolkitCall *call)
+{
+ g_return_if_fail (call != NULL);
+
+ call->disposed = TRUE;
+ g_free (call->permission);
+ call->permission = NULL;
+ call->chain = NULL;
+ g_object_unref (call->cancellable);
+ call->cancellable = NULL;
+ g_free (call);
+}
+
+static void
+pk_call_cb (GObject *object, GAsyncResult *result, gpointer user_data)
+{
+ PolkitCall *call = user_data;
+ NMAuthChain *chain;
+ PolkitAuthorizationResult *pk_result;
+ GError *error = NULL;
+ guint call_result = NM_AUTH_CALL_RESULT_UNKNOWN;
+
+ /* If the call is already disposed do nothing */
+ if (call->disposed) {
+ polkit_call_free (call);
+ return;
+ }
+
+ chain = call->chain;
+ chain->calls = g_slist_remove (chain->calls, call);
+
+ pk_result = polkit_authority_check_authorization_finish (chain->authority,
+ result,
+ &error);
+ if (error) {
+ if (!chain->error)
+ chain->error = g_error_copy (error);
+
+ nm_log_warn (LOGD_CORE, "error requesting auth for %s: (%d) %s",
+ call->permission,
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ } else {
+ if (polkit_authorization_result_get_is_authorized (pk_result)) {
+ /* Caller has the permission */
+ call_result = NM_AUTH_CALL_RESULT_YES;
+ } else if (polkit_authorization_result_get_is_challenge (pk_result)) {
+ /* Caller could authenticate to get the permission */
+ call_result = NM_AUTH_CALL_RESULT_AUTH;
+ } else
+ call_result = NM_AUTH_CALL_RESULT_NO;
+ }
+
+ chain->call_func (chain, call->permission, error, call_result, chain->user_data);
+ nm_auth_chain_check_done (chain);
+
+ g_clear_error (&error);
+ polkit_call_free (call);
+ if (pk_result)
+ g_object_unref (pk_result);
+}
+
+gboolean
+nm_auth_chain_add_call (NMAuthChain *self,
+ const char *permission,
+ gboolean allow_interaction)
+{
+ PolkitCall *call;
+ PolkitSubject *subject;
+ PolkitCheckAuthorizationFlags flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_NONE;
+
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (self->owner != NULL, FALSE);
+ g_return_val_if_fail (permission != NULL, FALSE);
+
+ subject = polkit_system_bus_name_new (self->owner);
+ if (!subject)
+ return FALSE;
+
+ call = g_malloc0 (sizeof (PolkitCall));
+ call->chain = self;
+ call->permission = g_strdup (permission);
+ call->cancellable = g_cancellable_new ();
+
+ self->calls = g_slist_append (self->calls, call);
+
+ if (allow_interaction)
+ flags = POLKIT_CHECK_AUTHORIZATION_FLAGS_ALLOW_USER_INTERACTION;
+
+ polkit_authority_check_authorization (self->authority,
+ subject,
+ permission,
+ NULL,
+ flags,
+ call->cancellable,
+ pk_call_cb,
+ call);
+ g_object_unref (subject);
+ return TRUE;
+}
+
+void
+nm_auth_chain_unref (NMAuthChain *self)
+{
+ GSList *iter;
+
+ g_return_if_fail (self != NULL);
+
+ self->refcount--;
+ if (self->refcount > 0)
+ return;
+
+ g_object_unref (self->authority);
+ g_free (self->owner);
+
+ for (iter = self->calls; iter; iter = g_slist_next (iter))
+ polkit_call_cancel ((PolkitCall *) iter->data);
+ g_slist_free (self->calls);
+
+ g_clear_error (&self->error);
+ g_hash_table_destroy (self->data);
+
+ memset (self, 0, sizeof (NMAuthChain));
+ g_free (self);
+}
+
+/************ utils **************/
+
+gboolean
+nm_auth_get_caller_uid (DBusGMethodInvocation *context,
+ NMDBusManager *dbus_mgr,
+ gulong *out_uid,
+ const char **out_error_desc)
+{
+ DBusConnection *connection;
+ char *sender = NULL;
+ gboolean success = FALSE;
+ DBusError dbus_error;
+
+ g_return_val_if_fail (context != NULL, FALSE);
+ g_return_val_if_fail (dbus_mgr != NULL, FALSE);
+ g_return_val_if_fail (out_uid != NULL, FALSE);
+
+ *out_uid = G_MAXULONG;
+
+ sender = dbus_g_method_get_sender (context);
+ if (!sender) {
+ if (out_error_desc)
+ *out_error_desc = "Could not determine D-Bus requestor";
+ goto out;
+ }
+
+ connection = nm_dbus_manager_get_dbus_connection (dbus_mgr);
+ if (!connection) {
+ if (out_error_desc)
+ *out_error_desc = "Could not get the D-Bus system bus";
+ goto out;
+ }
+
+ dbus_error_init (&dbus_error);
+ /* FIXME: do this async */
+ *out_uid = dbus_bus_get_unix_user (connection, sender, &dbus_error);
+ if (dbus_error_is_set (&dbus_error)) {
+ if (out_error_desc)
+ *out_error_desc = "Could not determine the user ID of the requestor";
+ dbus_error_free (&dbus_error);
+ *out_uid = G_MAXULONG;
+ } else
+ success = TRUE;
+
+out:
+ g_free (sender);
+ return success;
+}
+
+gboolean
+nm_auth_uid_authorized (gulong uid,
+ NMDBusManager *dbus_mgr,
+ DBusGProxy *user_proxy,
+ const char **out_error_desc)
+{
+ DBusConnection *connection;
+ DBusError dbus_error;
+ char *service_owner = NULL;
+ const char *service_name;
+ gulong service_uid = G_MAXULONG;
+
+ g_return_val_if_fail (dbus_mgr != NULL, FALSE);
+ g_return_val_if_fail (out_error_desc != NULL, FALSE);
+
+ /* Ensure the request to activate the user connection came from the
+ * same session as the user settings service. FIXME: use ConsoleKit
+ * too.
+ */
+
+ if (!user_proxy) {
+ *out_error_desc = "No user settings service available";
+ return FALSE;
+ }
+
+ service_name = dbus_g_proxy_get_bus_name (user_proxy);
+ if (!service_name) {
+ *out_error_desc = "Could not determine user settings service name";
+ return FALSE;
+ }
+
+ connection = nm_dbus_manager_get_dbus_connection (dbus_mgr);
+ if (!connection) {
+ *out_error_desc = "Could not get the D-Bus system bus";
+ return FALSE;
+ }
+
+ service_owner = nm_dbus_manager_get_name_owner (dbus_mgr, service_name, NULL);
+ if (!service_owner) {
+ *out_error_desc = "Could not determine D-Bus owner of the user settings service";
+ return FALSE;
+ }
+
+ dbus_error_init (&dbus_error);
+ service_uid = dbus_bus_get_unix_user (connection, service_owner, &dbus_error);
+ g_free (service_owner);
+
+ if (dbus_error_is_set (&dbus_error)) {
+ dbus_error_free (&dbus_error);
+ *out_error_desc = "Could not determine the Unix UID of the sender of the request";
+ return FALSE;
+ }
+
+ /* And finally, the actual UID check */
+ if (uid != service_uid) {
+ *out_error_desc = "Requestor UID does not match the UID of the user settings service";
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
diff --git a/src/nm-manager-auth.h b/src/nm-manager-auth.h
new file mode 100644
index 0000000000..6682f91ca1
--- /dev/null
+++ b/src/nm-manager-auth.h
@@ -0,0 +1,94 @@
+/* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
+/* NetworkManager -- Network link manager
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef NM_MANAGER_AUTH_H
+#define NM_MANAGER_AUTH_H
+
+#include <polkit/polkit.h>
+#include <glib.h>
+#include <dbus/dbus-glib.h>
+
+#include "nm-dbus-manager.h"
+
+#define NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK "org.freedesktop.NetworkManager.enable-disable-network"
+#define NM_AUTH_PERMISSION_SLEEP_WAKE "org.freedesktop.NetworkManager.sleep-wake"
+#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI "org.freedesktop.NetworkManager.enable-disable-wifi"
+#define NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN "org.freedesktop.NetworkManager.enable-disable-wwan"
+#define NM_AUTH_PERMISSION_USE_USER_CONNECTIONS "org.freedesktop.NetworkManager.use-user-connections"
+#define NM_AUTH_PERMISSION_NETWORK_CONTROL "org.freedesktop.NetworkManager.network-control"
+
+
+typedef struct NMAuthChain NMAuthChain;
+
+typedef enum {
+ NM_AUTH_CALL_RESULT_UNKNOWN,
+ NM_AUTH_CALL_RESULT_YES,
+ NM_AUTH_CALL_RESULT_AUTH,
+ NM_AUTH_CALL_RESULT_NO,
+} NMAuthCallResult;
+
+typedef void (*NMAuthChainResultFunc) (NMAuthChain *chain,
+ GError *error,
+ DBusGMethodInvocation *context,
+ gpointer user_data);
+
+typedef void (*NMAuthChainCallFunc) (NMAuthChain *chain,
+ const char *permission,
+ GError *error,
+ NMAuthCallResult result,
+ gpointer user_data);
+
+NMAuthChain *nm_auth_chain_new (PolkitAuthority *authority,
+ DBusGMethodInvocation *context,
+ DBusGProxy *proxy,
+ NMAuthChainResultFunc done_func,
+ gpointer user_data);
+
+NMAuthChain *nm_auth_chain_new_raw_message (PolkitAuthority *authority,
+ DBusMessage *message,
+ NMAuthChainResultFunc done_func,
+ gpointer user_data);
+
+gpointer nm_auth_chain_get_data (NMAuthChain *chain, const char *tag);
+
+void nm_auth_chain_set_data (NMAuthChain *chain,
+ const char *tag,
+ gpointer data,
+ GDestroyNotify data_destroy);
+
+gboolean nm_auth_chain_add_call (NMAuthChain *chain,
+ const char *permission,
+ gboolean allow_interaction);
+
+void nm_auth_chain_unref (NMAuthChain *chain);
+
+/* Utils */
+gboolean nm_auth_get_caller_uid (DBusGMethodInvocation *context,
+ NMDBusManager *dbus_mgr,
+ gulong *out_uid,
+ const char **out_error_desc);
+
+gboolean nm_auth_uid_authorized (gulong uid,
+ NMDBusManager *dbus_mgr,
+ DBusGProxy *user_proxy,
+ const char **out_error_desc);
+
+#endif /* NM_MANAGER_AUTH_H */
+
diff --git a/src/nm-manager.c b/src/nm-manager.c
index ee7a536ddc..55e4b45ba8 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -19,6 +19,8 @@
* Copyright (C) 2007 - 2010 Red Hat, Inc.
*/
+#include <config.h>
+
#include <netinet/ether.h>
#include <string.h>
#include <dbus/dbus-glib-lowlevel.h>
@@ -55,32 +57,35 @@
#include "nm-secrets-provider-interface.h"
#include "nm-settings-interface.h"
#include "nm-settings-system-interface.h"
+#include "nm-manager-auth.h"
#define NM_AUTOIP_DBUS_SERVICE "org.freedesktop.nm_avahi_autoipd"
#define NM_AUTOIP_DBUS_IFACE "org.freedesktop.nm_avahi_autoipd"
-#define NM_MANAGER_STATE "state"
-#define NM_MANAGER_WIRELESS_ENABLED "wireless-enabled"
-#define NM_MANAGER_WIRELESS_HARDWARE_ENABLED "wireless-hardware-enabled"
-#define NM_MANAGER_WWAN_ENABLED "wwan-enabled"
-#define NM_MANAGER_WWAN_HARDWARE_ENABLED "wwan-hardware-enabled"
-#define NM_MANAGER_WIMAX_ENABLED "wimax-enabled"
-#define NM_MANAGER_WIMAX_HARDWARE_ENABLED "wimax-hardware-enabled"
-#define NM_MANAGER_ACTIVE_CONNECTIONS "active-connections"
+#define UPOWER_DBUS_SERVICE "org.freedesktop.UPower"
static gboolean impl_manager_get_devices (NMManager *manager, GPtrArray **devices, GError **err);
static void impl_manager_activate_connection (NMManager *manager,
- const char *service_name,
- const char *connection_path,
- const char *device_path,
- const char *specific_object_path,
- DBusGMethodInvocation *context);
+ const char *service_name,
+ const char *connection_path,
+ const char *device_path,
+ const char *specific_object_path,
+ DBusGMethodInvocation *context);
+
+static void impl_manager_deactivate_connection (NMManager *manager,
+ const char *connection_path,
+ DBusGMethodInvocation *context);
-static gboolean impl_manager_deactivate_connection (NMManager *manager,
- const char *connection_path,
- GError **error);
+static void impl_manager_sleep (NMManager *manager,
+ gboolean do_sleep,
+ DBusGMethodInvocation *context);
-static gboolean impl_manager_sleep (NMManager *manager, gboolean sleep, GError **err);
+static void impl_manager_enable (NMManager *manager,
+ gboolean enable,
+ DBusGMethodInvocation *context);
+
+static void impl_manager_get_permissions (NMManager *manager,
+ DBusGMethodInvocation *context);
static gboolean impl_manager_set_logging (NMManager *manager,
const char *level,
@@ -89,14 +94,12 @@ static gboolean impl_manager_set_logging (NMManager *manager,
/* Legacy 0.6 compatibility interface */
-static gboolean impl_manager_legacy_sleep (NMManager *manager, GError **err);
-static gboolean impl_manager_legacy_wake (NMManager *manager, GError **err);
+static void impl_manager_legacy_sleep (NMManager *manager, DBusGMethodInvocation *context);
+static void impl_manager_legacy_wake (NMManager *manager, DBusGMethodInvocation *context);
static gboolean impl_manager_legacy_state (NMManager *manager, guint32 *state, GError **err);
#include "nm-manager-glue.h"
-static void user_destroy_connections (NMManager *manager);
-
static void connection_added_default_handler (NMManager *manager,
NMConnection *connection,
NMConnectionScope scope);
@@ -141,25 +144,52 @@ static NMDevice *find_device_by_iface (NMManager *self, const gchar *iface);
static GSList * remove_one_device (NMManager *manager,
GSList *list,
NMDevice *device,
- gboolean quitting,
- gboolean force_unmanage);
+ gboolean quitting);
static NMDevice *nm_manager_get_device_by_udi (NMManager *manager, const char *udi);
+/* Fix for polkit 0.97 and later */
+#if !HAVE_POLKIT_AUTHORITY_GET_SYNC
+static inline PolkitAuthority *
+polkit_authority_get_sync (GCancellable *cancellable, GError **error)
+{
+ PolkitAuthority *authority;
+
+ authority = polkit_authority_get ();
+ if (!authority)
+ g_set_error (error, 0, 0, "failed to get the PolicyKit authority");
+ return authority;
+}
+#endif
+
#define SSD_POKE_INTERVAL 120
#define ORIGDEV_TAG "originating-device"
-typedef struct {
+typedef struct PendingActivation PendingActivation;
+typedef void (*PendingActivationFunc) (PendingActivation *pending,
+ GError *error);
+
+struct PendingActivation {
+ NMManager *manager;
+
DBusGMethodInvocation *context;
+ PolkitAuthority *authority;
+ PendingActivationFunc callback;
+ NMAuthChain *chain;
+
+ gboolean have_connection;
+ gboolean authorized;
+
NMConnectionScope scope;
char *connection_path;
char *specific_object_path;
char *device_path;
guint timeout_id;
-} PendingConnectionInfo;
+};
typedef struct {
- gboolean enabled;
+ gboolean user_enabled;
+ gboolean sw_enabled;
gboolean hw_enabled;
RfKillType rtype;
const char *desc;
@@ -182,6 +212,8 @@ typedef struct {
GHashTable *user_connections;
DBusGProxy *user_proxy;
+ NMAuthCallResult user_con_perm;
+ NMAuthCallResult user_net_perm;
GHashTable *system_connections;
NMSysconfigSettings *sys_settings;
@@ -189,10 +221,11 @@ typedef struct {
GSList *secrets_calls;
- PendingConnectionInfo *pending_connection_info;
+ GSList *pending_activations;
RadioState radio_states[RFKILL_TYPE_MAX];
gboolean sleeping;
+ gboolean net_enabled;
NMVPNManager *vpn_manager;
guint vpn_manager_id;
@@ -202,6 +235,18 @@ typedef struct {
guint modem_removed_id;
DBusGProxy *aipd_proxy;
+ DBusGProxy *upower_proxy;
+
+ PolkitAuthority *authority;
+ guint auth_changed_id;
+ GSList *auth_chains;
+
+ /* Firmware dir monitor */
+ GFileMonitor *fw_monitor;
+ guint fw_monitor_id;
+ guint fw_changed_id;
+
+ guint timestamp_update_id;
gboolean disposed;
} NMManagerPrivate;
@@ -222,6 +267,8 @@ enum {
CONNECTION_ADDED,
CONNECTION_UPDATED,
CONNECTION_REMOVED,
+ CHECK_PERMISSIONS,
+ USER_PERMISSIONS_CHANGED,
LAST_SIGNAL
};
@@ -230,7 +277,9 @@ static guint signals[LAST_SIGNAL] = { 0 };
enum {
PROP_0,
+ PROP_VERSION,
PROP_STATE,
+ PROP_NETWORKING_ENABLED,
PROP_WIRELESS_ENABLED,
PROP_WIRELESS_HARDWARE_ENABLED,
PROP_WWAN_ENABLED,
@@ -256,6 +305,7 @@ typedef enum
NM_MANAGER_ERROR_PERMISSION_DENIED,
NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE,
NM_MANAGER_ERROR_ALREADY_ASLEEP_OR_AWAKE,
+ NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED,
} NMManagerError;
#define NM_MANAGER_ERROR (nm_manager_error_quark ())
@@ -298,6 +348,8 @@ nm_manager_error_get_type (void)
ENUM_ENTRY (NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE, "ConnectionNotActive"),
/* The manager is already in the requested sleep state */
ENUM_ENTRY (NM_MANAGER_ERROR_ALREADY_ASLEEP_OR_AWAKE, "AlreadyAsleepOrAwake"),
+ /* The manager is already in the requested enabled/disabled state */
+ ENUM_ENTRY (NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED, "AlreadyEnabledOrDisabled"),
{ 0, 0, 0 },
};
etype = g_enum_register_static ("NMManagerError", values);
@@ -305,6 +357,16 @@ nm_manager_error_get_type (void)
return etype;
}
+static gboolean
+manager_sleeping (NMManager *self)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+ if (priv->sleeping || !priv->net_enabled)
+ return TRUE;
+ return FALSE;
+}
+
static void
vpn_manager_connection_deactivated_cb (NMVPNManager *manager,
NMVPNConnection *vpn,
@@ -334,8 +396,7 @@ modem_added (NMModemManager *modem_manager,
priv->devices = remove_one_device (NM_MANAGER (user_data),
priv->devices,
replace_device,
- FALSE,
- TRUE);
+ FALSE);
}
/* Give Bluetooth DUN devices first chance to claim the modem */
@@ -377,9 +438,9 @@ nm_manager_update_state (NMManager *manager)
priv = NM_MANAGER_GET_PRIVATE (manager);
- if (priv->sleeping) {
+ if (manager_sleeping (manager))
new_state = NM_STATE_ASLEEP;
- } else {
+ else {
GSList *iter;
for (iter = priv->devices; iter; iter = iter->next) {
@@ -406,6 +467,45 @@ nm_manager_update_state (NMManager *manager)
}
static void
+ignore_cb (NMSettingsConnectionInterface *connection, GError *error, gpointer user_data)
+{
+}
+
+static void
+update_active_connection_timestamp (NMManager *manager, NMDevice *device)
+{
+ NMActRequest *req;
+ NMConnection *connection;
+ NMSettingConnection *s_con;
+ NMSettingsConnectionInterface *connection_interface;
+ NMManagerPrivate *priv;
+
+ g_return_if_fail (NM_IS_DEVICE (device));
+
+ priv = NM_MANAGER_GET_PRIVATE (manager);
+ req = nm_device_get_act_request (device);
+ if (!req)
+ return;
+
+ connection = nm_act_request_get_connection (req);
+ g_assert (connection);
+
+ if (nm_connection_get_scope (connection) != NM_CONNECTION_SCOPE_SYSTEM)
+ return;
+
+ s_con = NM_SETTING_CONNECTION (nm_connection_get_setting (NM_CONNECTION (connection), NM_TYPE_SETTING_CONNECTION));
+ g_assert (s_con);
+ g_object_set (s_con, NM_SETTING_CONNECTION_TIMESTAMP, (guint64) time (NULL), NULL);
+
+ if (nm_setting_connection_get_read_only (s_con))
+ return;
+
+ connection_interface = nm_settings_interface_get_connection_by_path (NM_SETTINGS_INTERFACE (priv->sys_settings),
+ nm_connection_get_path (connection));
+ nm_settings_connection_interface_update (connection_interface, ignore_cb, NULL);
+}
+
+static void
manager_device_state_changed (NMDevice *device,
NMDeviceState new_state,
NMDeviceState old_state,
@@ -427,6 +527,9 @@ manager_device_state_changed (NMDevice *device,
}
nm_manager_update_state (manager);
+
+ if (new_state == NM_DEVICE_STATE_ACTIVATED)
+ update_active_connection_timestamp (manager, device);
}
/* Removes a device from a device list; returns the start of the new device list */
@@ -434,20 +537,22 @@ static GSList *
remove_one_device (NMManager *manager,
GSList *list,
NMDevice *device,
- gboolean quitting,
- gboolean force_unmanage)
+ gboolean quitting)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
if (nm_device_get_managed (device)) {
- gboolean unmanage = !quitting;
-
- /* Don't unmanage active assume-connection-capable devices at shutdown */
- if ( nm_device_interface_can_assume_connection (NM_DEVICE_INTERFACE (device))
- && nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED)
- unmanage = FALSE;
+ /* When quitting, we want to leave up interfaces & connections
+ * that can be taken over again (ie, "assumed") when NM restarts
+ * so that '/etc/init.d/NetworkManager restart' will not distrupt
+ * networking for interfaces that support connection assumption.
+ * All other devices get unmanaged when NM quits so that their
+ * connections get torn down and the interface is deactivated.
+ */
- if (unmanage || force_unmanage)
+ if ( !nm_device_interface_can_assume_connections (NM_DEVICE_INTERFACE (device))
+ || (nm_device_get_state (device) != NM_DEVICE_STATE_ACTIVATED)
+ || !quitting)
nm_device_set_managed (device, FALSE, NM_DEVICE_STATE_REASON_REMOVED);
}
@@ -481,7 +586,7 @@ modem_removed (NMModemManager *modem_manager,
/* Otherwise remove the standalone modem */
found = nm_manager_get_device_by_udi (self, nm_modem_get_path (modem));
if (found)
- priv->devices = remove_one_device (self, priv->devices, found, FALSE, TRUE);
+ priv->devices = remove_one_device (self, priv->devices, found, FALSE);
}
static void
@@ -555,19 +660,237 @@ emit_removed (gpointer key, gpointer value, gpointer user_data)
}
static void
-pending_connection_info_destroy (PendingConnectionInfo *info)
+nm_manager_pending_activation_remove (NMManager *self,
+ PendingActivation *pending)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+ priv->pending_activations = g_slist_remove (priv->pending_activations, pending);
+}
+
+static PendingActivation *
+pending_activation_new (NMManager *manager,
+ PolkitAuthority *authority,
+ DBusGMethodInvocation *context,
+ const char *device_path,
+ NMConnectionScope scope,
+ const char *connection_path,
+ const char *specific_object_path,
+ PendingActivationFunc callback)
+{
+ PendingActivation *pending;
+
+ g_return_val_if_fail (manager != NULL, NULL);
+ g_return_val_if_fail (authority != NULL, NULL);
+ g_return_val_if_fail (context != NULL, NULL);
+ g_return_val_if_fail (device_path != NULL, NULL);
+ g_return_val_if_fail (connection_path != NULL, NULL);
+
+ pending = g_slice_new0 (PendingActivation);
+ pending->manager = manager;
+ pending->authority = authority;
+ pending->context = context;
+ pending->callback = callback;
+
+ pending->device_path = g_strdup (device_path);
+ pending->scope = scope;
+ pending->connection_path = g_strdup (connection_path);
+
+ /* "/" is special-cased to NULL to get through D-Bus */
+ if (specific_object_path && strcmp (specific_object_path, "/"))
+ pending->specific_object_path = g_strdup (specific_object_path);
+
+ return pending;
+}
+
+static void
+pending_auth_user_done (NMAuthChain *chain,
+ GError *error,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ PendingActivation *pending = user_data;
+ NMAuthCallResult result;
+
+ pending->chain = NULL;
+
+ if (error) {
+ pending->callback (pending, error);
+ goto out;
+ }
+
+ /* Caller has had a chance to obtain authorization, so we only need to
+ * check for 'yes' here.
+ */
+ result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS));
+ if (result != NM_AUTH_CALL_RESULT_YES) {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ "Not authorized to use user connections.");
+ pending->callback (pending, error);
+ g_error_free (error);
+ } else
+ pending->callback (pending, NULL);
+
+out:
+ nm_auth_chain_unref (chain);
+}
+
+static void
+pending_auth_net_done (NMAuthChain *chain,
+ GError *error,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ PendingActivation *pending = user_data;
+ NMAuthCallResult result;
+
+ pending->chain = NULL;
+
+ if (error) {
+ pending->callback (pending, error);
+ goto out;
+ }
+
+ /* Caller has had a chance to obtain authorization, so we only need to
+ * check for 'yes' here.
+ */
+ result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL));
+ if (result != NM_AUTH_CALL_RESULT_YES) {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ "Not authorized to control networking.");
+ pending->callback (pending, error);
+ g_error_free (error);
+ goto out;
+ }
+
+ if (pending->scope == NM_CONNECTION_SCOPE_SYSTEM) {
+ /* System connection and the user is authorized for that if they have
+ * the network-control permission.
+ */
+ pending->callback (pending, NULL);
+ } else {
+ g_assert (pending->scope == NM_CONNECTION_SCOPE_USER);
+
+ /* User connection, check the 'use-user-connections' permission */
+ pending->chain = nm_auth_chain_new (pending->authority,
+ pending->context,
+ NULL,
+ pending_auth_user_done,
+ pending);
+ nm_auth_chain_add_call (pending->chain,
+ NM_AUTH_PERMISSION_USE_USER_CONNECTIONS,
+ TRUE);
+ }
+
+out:
+ nm_auth_chain_unref (chain);
+}
+
+static gboolean
+check_user_authorized (NMDBusManager *dbus_mgr,
+ DBusGProxy *user_proxy,
+ DBusGMethodInvocation *context,
+ NMConnectionScope scope,
+ gulong *out_sender_uid,
+ const char **out_error_desc)
+{
+ g_return_val_if_fail (dbus_mgr != NULL, FALSE);
+ g_return_val_if_fail (context != NULL, FALSE);
+ g_return_val_if_fail (out_sender_uid != NULL, FALSE);
+ g_return_val_if_fail (out_error_desc != NULL, FALSE);
+
+ *out_sender_uid = G_MAXULONG;
+
+ /* Get the UID */
+ if (!nm_auth_get_caller_uid (context, dbus_mgr, out_sender_uid, out_error_desc))
+ return FALSE;
+
+ /* root gets to do anything */
+ if (0 == *out_sender_uid)
+ return TRUE;
+
+ /* Check whether the UID is authorized for user connections */
+ if ( scope == NM_CONNECTION_SCOPE_USER
+ && !nm_auth_uid_authorized (*out_sender_uid,
+ dbus_mgr,
+ user_proxy,
+ out_error_desc))
+ return FALSE;
+
+ return TRUE;
+}
+
+static void
+pending_activation_check_authorized (PendingActivation *pending,
+ NMDBusManager *dbus_mgr,
+ DBusGProxy *user_proxy)
{
- if (!info)
+ const char *error_desc = NULL;
+ gulong sender_uid = G_MAXULONG;
+ GError *error;
+
+ g_return_if_fail (pending != NULL);
+ g_return_if_fail (dbus_mgr != NULL);
+
+ if (!check_user_authorized (dbus_mgr,
+ user_proxy,
+ pending->context,
+ pending->scope,
+ &sender_uid,
+ &error_desc)) {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ error_desc);
+ pending->callback (pending, error);
+ g_error_free (error);
return;
+ }
- if (info->timeout_id)
- g_source_remove (info->timeout_id);
+ /* Yay for root */
+ if (0 == sender_uid) {
+ pending->callback (pending, NULL);
+ return;
+ }
- g_free (info->connection_path);
- g_free (info->specific_object_path);
- g_free (info->device_path);
+ /* First check if the user is allowed to use networking at all, giving
+ * the user a chance to authenticate to gain the permission.
+ */
+ pending->chain = nm_auth_chain_new (pending->authority,
+ pending->context,
+ NULL,
+ pending_auth_net_done,
+ pending);
+ g_assert (pending->chain);
+ nm_auth_chain_add_call (pending->chain,
+ NM_AUTH_PERMISSION_NETWORK_CONTROL,
+ TRUE);
+}
+
+static void
+pending_activation_destroy (PendingActivation *pending,
+ GError *error,
+ const char *ac_path)
+{
+ g_return_if_fail (pending != NULL);
+
+ if (pending->timeout_id)
+ g_source_remove (pending->timeout_id);
+ g_free (pending->connection_path);
+ g_free (pending->specific_object_path);
+ g_free (pending->device_path);
+
+ if (error)
+ dbus_g_method_return_error (pending->context, error);
+ else if (ac_path)
+ dbus_g_method_return (pending->context, ac_path);
+
+ if (pending->chain)
+ nm_auth_chain_unref (pending->chain);
- g_slice_free (PendingConnectionInfo, info);
+ memset (pending, 0, sizeof (PendingActivation));
+ g_slice_free (PendingActivation, pending);
}
static GPtrArray *
@@ -627,27 +950,34 @@ remove_connection (NMManager *manager,
/*******************************************************************/
static void
-user_destroy_connections (NMManager *manager)
+user_proxy_cleanup (NMManager *self, gboolean resync_bt)
{
- NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
if (priv->user_connections) {
- g_hash_table_foreach (priv->user_connections, emit_removed, manager);
+ g_hash_table_foreach (priv->user_connections, emit_removed, self);
g_hash_table_remove_all (priv->user_connections);
}
+ priv->user_net_perm = NM_AUTH_CALL_RESULT_UNKNOWN;
+ priv->user_con_perm = NM_AUTH_CALL_RESULT_UNKNOWN;
+
if (priv->user_proxy) {
g_object_unref (priv->user_proxy);
priv->user_proxy = NULL;
}
+
+ if (resync_bt) {
+ /* Resync BT devices since they are generated from connections */
+ bluez_manager_resync_devices (self);
+ }
}
typedef struct GetSettingsInfo {
NMManager *manager;
NMConnection *connection;
DBusGProxy *proxy;
- DBusGProxyCall *call;
- GSList **calls;
+ guint32 *calls;
} GetSettingsInfo;
static void
@@ -659,10 +989,9 @@ free_get_settings_info (gpointer data)
* send out the connections-added signal.
*/
if (info->calls) {
- *(info->calls) = g_slist_remove (*(info->calls), info->call);
- if (g_slist_length (*(info->calls)) == 0) {
- g_slist_free (*(info->calls));
- g_slice_free (GSList, (gpointer) info->calls);
+ (*info->calls)--;
+ if (*info->calls == 0) {
+ g_slice_free (guint32, (gpointer) info->calls);
g_signal_emit (info->manager, signals[CONNECTIONS_ADDED], 0, NM_CONNECTION_SCOPE_USER);
/* Update the Bluetooth connections for all the new connections */
@@ -833,20 +1162,18 @@ user_connection_updated_cb (DBusGProxy *proxy,
}
static void
-user_internal_new_connection_cb (DBusGProxy *proxy,
+user_internal_new_connection_cb (NMManager *manager,
const char *path,
- NMManager *manager,
- GSList **calls)
+ guint32 *counter)
{
- struct GetSettingsInfo *info;
+ GetSettingsInfo *info;
DBusGProxy *con_proxy;
DBusGConnection *g_connection;
- DBusGProxyCall *call;
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
con_proxy = dbus_g_proxy_new_for_name (g_connection,
- dbus_g_proxy_get_bus_name (proxy),
+ NM_DBUS_SERVICE_USER_SETTINGS,
path,
NM_DBUS_IFACE_SETTINGS_CONNECTION);
if (!con_proxy) {
@@ -870,16 +1197,16 @@ user_internal_new_connection_cb (DBusGProxy *proxy,
info = g_slice_new0 (GetSettingsInfo);
info->manager = g_object_ref (manager);
- info->calls = calls;
- call = dbus_g_proxy_begin_call (con_proxy, "GetSettings",
- user_connection_get_settings_cb,
- info,
- free_get_settings_info,
- G_TYPE_INVALID);
- info->call = call;
info->proxy = con_proxy;
- if (info->calls)
- *(info->calls) = g_slist_prepend (*(info->calls), call);
+ if (counter) {
+ info->calls = counter;
+ (*info->calls)++;
+ }
+ dbus_g_proxy_begin_call (con_proxy, "GetSettings",
+ user_connection_get_settings_cb,
+ info,
+ free_get_settings_info,
+ G_TYPE_INVALID);
}
static void
@@ -890,7 +1217,7 @@ user_list_connections_cb (DBusGProxy *proxy,
NMManager *manager = NM_MANAGER (user_data);
GError *err = NULL;
GPtrArray *ops;
- GSList **calls = NULL;
+ guint32 *counter = NULL;
int i;
if (!dbus_g_proxy_end_call (proxy, call_id, &err,
@@ -898,72 +1225,172 @@ user_list_connections_cb (DBusGProxy *proxy,
G_TYPE_INVALID)) {
nm_log_warn (LOGD_USER_SET, "couldn't retrieve connections: %s",
err && err->message ? err->message : "(unknown)");
- g_error_free (err);
- goto out;
+ g_clear_error (&err);
+ return;
}
/* Keep track of all calls made here; don't want to emit connection-added for
* each one, but emit connections-added when they are all done.
*/
- calls = g_slice_new0 (GSList *);
-
+ counter = g_slice_new0 (guint32);
for (i = 0; i < ops->len; i++) {
char *op = g_ptr_array_index (ops, i);
- user_internal_new_connection_cb (proxy, op, manager, calls);
+ user_internal_new_connection_cb (manager, op, counter);
g_free (op);
}
-
g_ptr_array_free (ops, TRUE);
+}
-out:
- return;
+static void
+user_proxy_destroyed_cb (DBusGProxy *proxy, NMManager *self)
+{
+ nm_log_dbg (LOGD_USER_SET, "Removing user connections...");
+
+ /* At this point the user proxy is already being disposed */
+ NM_MANAGER_GET_PRIVATE (self)->user_proxy = NULL;
+
+ /* User Settings service disappeared; throw away user connections */
+ user_proxy_cleanup (self, TRUE);
}
static void
user_new_connection_cb (DBusGProxy *proxy, const char *path, gpointer user_data)
{
- user_internal_new_connection_cb (proxy, path, NM_MANAGER (user_data), NULL);
+ user_internal_new_connection_cb (NM_MANAGER (user_data), path, NULL);
+}
+
+static gboolean
+user_settings_authorized (NMManager *self, NMAuthChain *chain)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ NMAuthCallResult old_net_perm = priv->user_net_perm;
+ NMAuthCallResult old_con_perm = priv->user_con_perm;
+
+ /* If the user could potentially get authorization to use networking and/or
+ * to use user connections, the user settings service is authorized.
+ */
+ priv->user_net_perm = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL));
+ priv->user_con_perm = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS));
+
+ nm_log_dbg (LOGD_USER_SET, "User connections permissions: net %d, con %d",
+ priv->user_net_perm, priv->user_con_perm);
+
+ if (old_net_perm != priv->user_net_perm || old_con_perm != priv->user_con_perm)
+ g_signal_emit (self, signals[USER_PERMISSIONS_CHANGED], 0);
+
+ /* If the user can't control the network they certainly aren't allowed
+ * to provide user connections.
+ */
+ if ( priv->user_net_perm == NM_AUTH_CALL_RESULT_UNKNOWN
+ || priv->user_net_perm == NM_AUTH_CALL_RESULT_NO)
+ return FALSE;
+
+ /* And of course if they aren't allowed to use user connections, they can't
+ * provide them either.
+ */
+ if ( priv->user_con_perm == NM_AUTH_CALL_RESULT_UNKNOWN
+ || priv->user_con_perm == NM_AUTH_CALL_RESULT_NO)
+ return FALSE;
+
+ return TRUE;
}
static void
-user_query_connections (NMManager *manager)
+user_proxy_auth_done (NMAuthChain *chain,
+ GError *error,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
{
- NMManagerPrivate *priv;
- DBusGProxyCall *call;
- DBusGConnection *g_connection;
+ NMManager *self = NM_MANAGER (user_data);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ gboolean authorized = FALSE;
- g_return_if_fail (NM_IS_MANAGER (manager));
+ priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
- priv = NM_MANAGER_GET_PRIVATE (manager);
- if (!priv->user_proxy) {
- g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
- priv->user_proxy = dbus_g_proxy_new_for_name (g_connection,
- NM_DBUS_SERVICE_USER_SETTINGS,
- NM_DBUS_PATH_SETTINGS,
- NM_DBUS_IFACE_SETTINGS);
- if (!priv->user_proxy) {
- nm_log_err (LOGD_USER_SET, "could not init user settings proxy");
- return;
- }
+ if (error) {
+ nm_log_warn (LOGD_USER_SET, "User connections unavailable: (%d) %s",
+ error->code, error->message ? error->message : "(unknown)");
+ } else
+ authorized = user_settings_authorized (self, chain);
- dbus_g_proxy_add_signal (priv->user_proxy,
- "NewConnection",
- DBUS_TYPE_G_OBJECT_PATH,
- G_TYPE_INVALID);
+ if (authorized) {
+ /* If authorized, finish setting up the user settings service proxy */
+ nm_log_dbg (LOGD_USER_SET, "Requesting user connections...");
+
+ authorized = TRUE;
+ dbus_g_proxy_add_signal (priv->user_proxy,
+ "NewConnection",
+ DBUS_TYPE_G_OBJECT_PATH,
+ G_TYPE_INVALID);
dbus_g_proxy_connect_signal (priv->user_proxy, "NewConnection",
- G_CALLBACK (user_new_connection_cb),
- manager,
- NULL);
+ G_CALLBACK (user_new_connection_cb),
+ self,
+ NULL);
+
+ /* Clean up when the user settings proxy goes away */
+ g_signal_connect (priv->user_proxy, "destroy",
+ G_CALLBACK (user_proxy_destroyed_cb),
+ self);
+
+ /* Request user connections */
+ dbus_g_proxy_begin_call (priv->user_proxy, "ListConnections",
+ user_list_connections_cb,
+ self,
+ NULL,
+ G_TYPE_INVALID);
+ } else {
+ /* Otherwise, we ignore the user settings service completely */
+ user_proxy_cleanup (self, TRUE);
+ }
+
+ nm_auth_chain_unref (chain);
+}
+
+static void
+user_proxy_init (NMManager *self)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ DBusGConnection *bus;
+ NMAuthChain *chain;
+ GError *error = NULL;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (priv->user_proxy == NULL);
+
+ /* Don't try to initialize the user settings proxy if the user
+ * settings service doesn't actually exist.
+ */
+ if (!nm_dbus_manager_name_has_owner (priv->dbus_mgr, NM_DBUS_SERVICE_USER_SETTINGS))
+ return;
+
+ bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
+ priv->user_proxy = dbus_g_proxy_new_for_name_owner (bus,
+ NM_DBUS_SERVICE_USER_SETTINGS,
+ NM_DBUS_PATH_SETTINGS,
+ NM_DBUS_IFACE_SETTINGS,
+ &error);
+ if (!priv->user_proxy) {
+ nm_log_err (LOGD_USER_SET, "could not init user settings proxy: (%d) %s",
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ return;
}
- /* grab connections */
- call = dbus_g_proxy_begin_call (priv->user_proxy, "ListConnections",
- user_list_connections_cb,
- manager,
- NULL,
- G_TYPE_INVALID);
+ /* Kick off some PolicyKit authorization requests to figure out what
+ * permissions this user settings service has.
+ */
+ chain = nm_auth_chain_new (priv->authority,
+ NULL,
+ priv->user_proxy,
+ user_proxy_auth_done,
+ self);
+ priv->auth_chains = g_slist_prepend (priv->auth_chains, chain);
+
+ nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, FALSE);
+ nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, FALSE);
}
/*******************************************************************/
@@ -1037,6 +1464,7 @@ system_internal_new_connection (NMManager *manager,
path = nm_connection_get_path (NM_CONNECTION (connection));
g_hash_table_insert (priv->system_connections, g_strdup (path),
g_object_ref (connection));
+ g_signal_emit (manager, signals[CONNECTION_ADDED], 0, connection, NM_CONNECTION_SCOPE_SYSTEM);
}
static void
@@ -1145,14 +1573,10 @@ nm_manager_name_owner_changed (NMDBusManager *mgr,
gboolean new_owner_good = (new && (strlen (new) > 0));
if (strcmp (name, NM_DBUS_SERVICE_USER_SETTINGS) == 0) {
- if (!old_owner_good && new_owner_good) {
- /* User Settings service appeared, update stuff */
- user_query_connections (manager);
- } else {
- /* User Settings service disappeared, throw them away (?) */
- user_destroy_connections (manager);
- bluez_manager_resync_devices (manager);
- }
+ if (!old_owner_good && new_owner_good)
+ user_proxy_init (manager);
+ else
+ user_proxy_cleanup (manager, TRUE);
}
}
@@ -1206,50 +1630,40 @@ write_value_to_state_file (const char *filename,
return ret;
}
+static gboolean
+radio_enabled_for_rstate (RadioState *rstate)
+{
+ return rstate->user_enabled && rstate->sw_enabled && rstate->hw_enabled;
+}
+
+static gboolean
+radio_enabled_for_type (NMManager *self, RfKillType rtype)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+ return radio_enabled_for_rstate (&priv->radio_states[rtype]);
+}
+
static void
-manager_set_radio_enabled (NMManager *manager,
- RadioState *rstate,
- gboolean enabled)
+manager_update_radio_enabled (NMManager *self, RadioState *rstate)
{
- NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
GSList *iter;
- GError *error = NULL;
/* Do nothing for radio types not yet implemented */
if (!rstate->prop)
return;
- if (rstate->enabled == enabled)
- return;
-
- /* Can't set wireless enabled if it's disabled in hardware */
- if (!rstate->hw_enabled && enabled)
- return;
-
- rstate->enabled = enabled;
-
- g_object_notify (G_OBJECT (manager), rstate->prop);
-
- /* Update enabled key in state file */
- if (priv->state_file) {
- if (!write_value_to_state_file (priv->state_file,
- "main", rstate->key,
- G_TYPE_BOOLEAN, (gpointer) &enabled,
- &error)) {
- nm_log_warn (LOGD_CORE, "writing to state file %s failed: (%d) %s.",
- priv->state_file,
- error ? error->code : -1,
- (error && error->message) ? error->message : "unknown");
- }
- }
+ g_object_notify (G_OBJECT (self), rstate->prop);
/* Don't touch devices if asleep/networking disabled */
- if (priv->sleeping)
+ if (manager_sleeping (self))
return;
/* enable/disable wireless devices as required */
for (iter = priv->devices; iter; iter = iter->next) {
RfKillType devtype = RFKILL_TYPE_UNKNOWN;
+ gboolean enabled = radio_enabled_for_rstate (rstate);
g_object_get (G_OBJECT (iter->data), NM_DEVICE_INTERFACE_RFKILL_TYPE, &devtype, NULL);
if (devtype == rstate->rtype) {
@@ -1372,6 +1786,21 @@ nm_manager_get_modem_enabled_state (NMManager *self)
}
static void
+update_rstate_from_rfkill (RadioState *rstate, RfKillState rfkill)
+{
+ if (rfkill == RFKILL_UNBLOCKED) {
+ rstate->sw_enabled = TRUE;
+ rstate->hw_enabled = TRUE;
+ } else if (rfkill == RFKILL_SOFT_BLOCKED) {
+ rstate->sw_enabled = FALSE;
+ rstate->hw_enabled = TRUE;
+ } else if (rfkill == RFKILL_HARD_BLOCKED) {
+ rstate->sw_enabled = FALSE;
+ rstate->hw_enabled = FALSE;
+ }
+}
+
+static void
manager_rfkill_update_one_type (NMManager *self,
RadioState *rstate,
RfKillType rtype)
@@ -1380,7 +1809,12 @@ manager_rfkill_update_one_type (NMManager *self,
RfKillState udev_state = RFKILL_UNBLOCKED;
RfKillState other_state = RFKILL_UNBLOCKED;
RfKillState composite;
- gboolean new_e = TRUE, new_he = TRUE;
+ gboolean old_enabled, new_enabled, old_rfkilled, new_rfkilled;
+ gboolean old_hwe;
+
+ old_enabled = radio_enabled_for_rstate (rstate);
+ old_rfkilled = rstate->hw_enabled && rstate->sw_enabled;
+ old_hwe = rstate->hw_enabled;
udev_state = nm_udev_manager_get_rfkill_state (priv->udev_mgr, rtype);
@@ -1395,38 +1829,31 @@ manager_rfkill_update_one_type (NMManager *self,
else
composite = RFKILL_UNBLOCKED;
- switch (composite) {
- case RFKILL_UNBLOCKED:
- new_e = TRUE;
- new_he = TRUE;
- break;
- case RFKILL_SOFT_BLOCKED:
- new_e = FALSE;
- new_he = TRUE;
- break;
- case RFKILL_HARD_BLOCKED:
- new_e = FALSE;
- new_he = FALSE;
- break;
- default:
- break;
- }
+ update_rstate_from_rfkill (rstate, composite);
if (rstate->desc) {
- nm_log_dbg (LOGD_RFKILL, "%s hw-enabled %d enabled %d",
- rstate->desc, new_he, new_e);
+ nm_log_dbg (LOGD_RFKILL, "%s hw-enabled %d sw-enabled %d",
+ rstate->desc, rstate->hw_enabled, rstate->sw_enabled);
}
- if (new_he != rstate->hw_enabled) {
+ /* Log new killswitch state */
+ new_rfkilled = rstate->hw_enabled && rstate->sw_enabled;
+ if (old_rfkilled != new_rfkilled) {
nm_log_info (LOGD_RFKILL, "%s now %s by radio killswitch",
rstate->desc,
- (new_e && new_he) ? "enabled" : "disabled");
+ new_rfkilled ? "enabled" : "disabled");
+ }
- rstate->hw_enabled = new_he;
+ /* Send out property changed signal for HW enabled */
+ if (rstate->hw_enabled != old_hwe) {
if (rstate->hw_prop)
g_object_notify (G_OBJECT (self), rstate->hw_prop);
}
- manager_set_radio_enabled (self, rstate, new_e);
+
+ /* And finally update the actual device radio state itself */
+ new_enabled = radio_enabled_for_rstate (rstate);
+ if (new_enabled != old_enabled)
+ manager_update_radio_enabled (self, rstate);
}
static void
@@ -1454,13 +1881,177 @@ manager_ipw_rfkill_state_changed (NMDeviceWifi *device,
}
static void
-manager_modem_enabled_changed (NMModem *device,
- GParamSpec *pspec,
- gpointer user_data)
+manager_modem_enabled_changed (NMModem *device, gpointer user_data)
{
nm_manager_rfkill_update (NM_MANAGER (user_data), RFKILL_TYPE_WWAN);
}
+static GError *
+deactivate_disconnect_check_error (GError *auth_error,
+ NMAuthCallResult result,
+ const char *detail)
+{
+ if (auth_error) {
+ nm_log_dbg (LOGD_CORE, "%s request failed: %s", detail, auth_error->message);
+ return g_error_new (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ "%s request failed: %s",
+ detail, auth_error->message);
+ } else if (result != NM_AUTH_CALL_RESULT_YES) {
+ return g_error_new (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ "Not authorized to %s user connections",
+ detail);
+ }
+ return NULL;
+}
+
+static void
+disconnect_user_auth_done_cb (NMAuthChain *chain,
+ GError *error,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ NMManager *self = NM_MANAGER (user_data);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ GError *ret_error = NULL;
+ NMAuthCallResult result;
+ NMDevice *device;
+
+ priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
+
+ result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS));
+ ret_error = deactivate_disconnect_check_error (error, result, "Disconnect");
+ if (!ret_error) {
+ /* Everything authorized, deactivate the connection */
+ device = nm_auth_chain_get_data (chain, "device");
+ if (nm_device_interface_disconnect (NM_DEVICE_INTERFACE (device), &ret_error))
+ dbus_g_method_return (context);
+ }
+
+ if (ret_error)
+ dbus_g_method_return_error (context, ret_error);
+ g_clear_error (&ret_error);
+
+ nm_auth_chain_unref (chain);
+}
+
+static void
+disconnect_net_auth_done_cb (NMAuthChain *chain,
+ GError *error,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ NMManager *self = NM_MANAGER (user_data);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ GError *ret_error = NULL;
+ NMAuthCallResult result;
+ NMConnectionScope scope;
+ NMDevice *device;
+
+ priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
+
+ result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL));
+ ret_error = deactivate_disconnect_check_error (error, result, "Disconnect");
+ if (ret_error) {
+ dbus_g_method_return_error (context, ret_error);
+ g_error_free (ret_error);
+ goto done;
+ }
+
+ /* If it's a system connection, we're done */
+ device = nm_auth_chain_get_data (chain, "device");
+ scope = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "scope"));
+ if (scope == NM_CONNECTION_SCOPE_USER) {
+ NMAuthChain *user_chain;
+
+ /* It's a user connection, so we need to ensure the caller is
+ * authorized to manipulate user connections.
+ */
+ user_chain = nm_auth_chain_new (priv->authority, context, NULL, disconnect_user_auth_done_cb, self);
+ g_assert (user_chain);
+ priv->auth_chains = g_slist_append (priv->auth_chains, user_chain);
+
+ nm_auth_chain_set_data (user_chain, "device", g_object_ref (device), g_object_unref);
+ nm_auth_chain_set_data (user_chain, "scope", GUINT_TO_POINTER (scope), NULL);
+ nm_auth_chain_add_call (user_chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, TRUE);
+ } else {
+ if (!nm_device_interface_disconnect (NM_DEVICE_INTERFACE (device), &ret_error)) {
+ dbus_g_method_return_error (context, ret_error);
+ g_clear_error (&ret_error);
+ } else
+ dbus_g_method_return (context);
+ }
+
+done:
+ nm_auth_chain_unref (chain);
+}
+
+static void
+manager_device_disconnect_request (NMDevice *device,
+ DBusGMethodInvocation *context,
+ NMManager *self)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ NMActRequest *req;
+ NMConnection *connection;
+ GError *error = NULL;
+ NMConnectionScope scope;
+ gulong sender_uid = G_MAXULONG;
+ const char *error_desc = NULL;
+
+ req = nm_device_get_act_request (device);
+ if (!req) {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
+ "This device is not active");
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ connection = nm_act_request_get_connection (req);
+ g_assert (connection);
+
+ /* Need to check the caller's permissions and stuff before we can
+ * deactivate the connection.
+ */
+ scope = nm_connection_get_scope (connection);
+ if (!check_user_authorized (priv->dbus_mgr,
+ priv->user_proxy,
+ context,
+ scope,
+ &sender_uid,
+ &error_desc)) {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ error_desc);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ /* Yay for root */
+ if (0 == sender_uid) {
+ if (!nm_device_interface_disconnect (NM_DEVICE_INTERFACE (device), &error)) {
+ dbus_g_method_return_error (context, error);
+ g_clear_error (&error);
+ } else
+ dbus_g_method_return (context);
+ } else {
+ NMAuthChain *chain;
+
+ /* Otherwise validate the user request */
+ chain = nm_auth_chain_new (priv->authority, context, NULL, disconnect_net_auth_done_cb, self);
+ g_assert (chain);
+ priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+
+ nm_auth_chain_set_data (chain, "device", g_object_ref (device), g_object_unref);
+ nm_auth_chain_set_data (chain, "scope", GUINT_TO_POINTER (scope), NULL);
+ nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE);
+ }
+}
+
static void
add_device (NMManager *self, NMDevice *device)
{
@@ -1472,7 +2063,7 @@ add_device (NMManager *self, NMDevice *device)
NMConnection *existing = NULL;
GHashTableIter iter;
gpointer value;
- gboolean managed = FALSE;
+ gboolean managed = FALSE, enabled = FALSE;
iface = nm_device_get_ip_iface (device);
g_assert (iface);
@@ -1488,6 +2079,10 @@ add_device (NMManager *self, NMDevice *device)
G_CALLBACK (manager_device_state_changed),
self);
+ g_signal_connect (device, NM_DEVICE_INTERFACE_DISCONNECT_REQUEST,
+ G_CALLBACK (manager_device_disconnect_request),
+ self);
+
if (NM_IS_DEVICE_WIFI (device)) {
/* Attach to the access-point-added signal so that the manager can fill
* non-SSID-broadcasting APs with an SSID.
@@ -1507,14 +2102,15 @@ add_device (NMManager *self, NMDevice *device)
* then set this device's rfkill state based on the global state.
*/
nm_manager_rfkill_update (self, RFKILL_TYPE_WLAN);
- nm_device_interface_set_enabled (NM_DEVICE_INTERFACE (device),
- priv->radio_states[RFKILL_TYPE_WLAN].enabled);
+ enabled = radio_enabled_for_type (self, RFKILL_TYPE_WLAN);
+ nm_device_interface_set_enabled (NM_DEVICE_INTERFACE (device), enabled);
} else if (NM_IS_DEVICE_MODEM (device)) {
- g_signal_connect (device, "notify::" NM_MODEM_ENABLED,
+ g_signal_connect (device, NM_DEVICE_MODEM_ENABLE_CHANGED,
G_CALLBACK (manager_modem_enabled_changed),
self);
nm_manager_rfkill_update (self, RFKILL_TYPE_WWAN);
+ enabled = radio_enabled_for_type (self, RFKILL_TYPE_WWAN);
/* Until we start respecting WWAN rfkill switches the modem itself
* is the source of the enabled/disabled state, so the manager shouldn't
* touch it here.
@@ -1546,7 +2142,7 @@ add_device (NMManager *self, NMDevice *device)
/* Check if we should assume the device's active connection by matching its
* config with an existing system connection.
*/
- if (nm_device_interface_can_assume_connection (NM_DEVICE_INTERFACE (device))) {
+ if (nm_device_interface_can_assume_connections (NM_DEVICE_INTERFACE (device))) {
GSList *connections = NULL;
g_hash_table_iter_init (&iter, priv->system_connections);
@@ -1568,7 +2164,7 @@ add_device (NMManager *self, NMDevice *device)
/* Start the device if it's supposed to be managed */
unmanaged_specs = nm_sysconfig_settings_get_unmanaged_specs (priv->sys_settings);
- if ( !priv->sleeping
+ if ( !manager_sleeping (self)
&& !nm_device_interface_spec_match_list (NM_DEVICE_INTERFACE (device), unmanaged_specs)) {
nm_device_set_managed (device,
TRUE,
@@ -1706,7 +2302,7 @@ bluez_manager_resync_devices (NMManager *self)
priv->devices = keep;
while (g_slist_length (gone))
- gone = remove_one_device (self, gone, NM_DEVICE (gone->data), FALSE, TRUE);
+ gone = remove_one_device (self, gone, NM_DEVICE (gone->data), FALSE);
} else {
g_slist_free (keep);
g_slist_free (gone);
@@ -1775,7 +2371,7 @@ bluez_manager_bdaddr_removed_cb (NMBluezManager *bluez_mgr,
NMDevice *device = NM_DEVICE (iter->data);
if (!strcmp (nm_device_get_udi (device), object_path)) {
- priv->devices = remove_one_device (self, priv->devices, device, FALSE, TRUE);
+ priv->devices = remove_one_device (self, priv->devices, device, FALSE);
break;
}
}
@@ -1819,7 +2415,6 @@ udev_device_added_cb (NMUdevManager *udev_mgr,
gpointer user_data)
{
NMManager *self = NM_MANAGER (user_data);
- NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
GObject *device;
guint32 ifindex;
@@ -1827,7 +2422,7 @@ udev_device_added_cb (NMUdevManager *udev_mgr,
if (find_device_by_ifindex (self, ifindex))
return;
- device = creator_fn (udev_mgr, udev_device, priv->sleeping);
+ device = creator_fn (udev_mgr, udev_device, manager_sleeping (self));
if (device)
add_device (self, NM_DEVICE (device));
}
@@ -1844,8 +2439,16 @@ udev_device_removed_cb (NMUdevManager *manager,
ifindex = g_udev_device_get_property_as_int (udev_device, "IFINDEX");
device = find_device_by_ifindex (self, ifindex);
+ if (!device) {
+ /* On removal we won't always be able to read properties anymore, as
+ * they may have already been removed from sysfs. Instead, we just
+ * have to fall back to the device's interface name.
+ */
+ device = find_device_by_iface (self, g_udev_device_get_name (udev_device));
+ }
+
if (device)
- priv->devices = remove_one_device (self, priv->devices, device, FALSE, TRUE);
+ priv->devices = remove_one_device (self, priv->devices, device, FALSE);
}
static void
@@ -2242,25 +2845,21 @@ internal_activate_device (NMManager *manager,
static gboolean
wait_for_connection_expired (gpointer data)
{
- NMManager *manager = NM_MANAGER (data);
- NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
- PendingConnectionInfo *info = priv->pending_connection_info;
+ PendingActivation *pending = data;
GError *error = NULL;
- g_return_val_if_fail (info != NULL, FALSE);
+ g_return_val_if_fail (pending != NULL, FALSE);
- g_set_error (&error,
- NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
- "%s", "Connection was not provided by any settings service");
- nm_log_warn (LOGD_CORE, "connection (%d) %s failed to activate (timeout): (%d) %s",
- info->scope, info->connection_path, error->code, error->message);
- dbus_g_method_return_error (info->context, error);
- g_error_free (error);
+ nm_log_warn (LOGD_CORE, "connection %s (scope %d) failed to activate (timeout)",
+ pending->connection_path, pending->scope);
- info->timeout_id = 0;
- pending_connection_info_destroy (priv->pending_connection_info);
- priv->pending_connection_info = NULL;
+ nm_manager_pending_activation_remove (pending->manager, pending);
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
+ "Connection was not provided by any settings service");
+ pending_activation_destroy (pending, error, NULL);
+ g_error_free (error);
return FALSE;
}
@@ -2276,7 +2875,7 @@ nm_manager_activate_connection (NMManager *manager,
NMDevice *device = NULL;
NMSettingConnection *s_con;
NMVPNConnection *vpn_connection;
- const char *path;
+ const char *path = NULL;
g_return_val_if_fail (manager != NULL, NULL);
g_return_val_if_fail (connection != NULL, NULL);
@@ -2333,11 +2932,13 @@ nm_manager_activate_connection (NMManager *manager,
req,
device,
error);
- g_signal_connect (vpn_connection, "manager-get-secrets",
- G_CALLBACK (provider_get_secrets), manager);
- g_signal_connect (vpn_connection, "manager-cancel-secrets",
- G_CALLBACK (provider_cancel_secrets), manager);
- path = nm_vpn_connection_get_active_connection_path (vpn_connection);
+ if (vpn_connection) {
+ g_signal_connect (vpn_connection, "manager-get-secrets",
+ G_CALLBACK (provider_get_secrets), manager);
+ g_signal_connect (vpn_connection, "manager-cancel-secrets",
+ G_CALLBACK (provider_cancel_secrets), manager);
+ path = nm_vpn_connection_get_active_connection_path (vpn_connection);
+ }
g_object_unref (vpn_manager);
} else {
NMDeviceState state;
@@ -2355,7 +2956,7 @@ nm_manager_activate_connection (NMManager *manager,
if (state < NM_DEVICE_STATE_DISCONNECTED) {
g_set_error (error,
NM_MANAGER_ERROR, NM_MANAGER_ERROR_UNMANAGED_DEVICE,
- "%s", "Device not managed by NetworkManager");
+ "%s", "Device not managed by NetworkManager or unavailable");
return NULL;
}
@@ -2371,229 +2972,145 @@ nm_manager_activate_connection (NMManager *manager,
return path;
}
+static PendingActivation *
+nm_manager_pending_activation_find (NMManager *self,
+ const char *path,
+ NMConnectionScope scope)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ GSList *iter;
+
+ for (iter = priv->pending_activations; iter; iter = g_slist_next (iter)) {
+ PendingActivation *pending = iter->data;
+
+ if (!strcmp (pending->connection_path, path) && (pending->scope == scope))
+ return pending;
+ }
+ return NULL;
+}
+
static void
-connection_added_default_handler (NMManager *manager,
- NMConnection *connection,
- NMConnectionScope scope)
+check_pending_ready (NMManager *self, PendingActivation *pending)
{
- NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
- PendingConnectionInfo *info = priv->pending_connection_info;
- const char *path;
+ NMConnection *connection;
+ const char *path = NULL;
GError *error = NULL;
- if (!info)
+ if (!pending->have_connection || !pending->authorized)
return;
- if (scope != info->scope)
- return;
+ /* Ok, we're authorized and the connection is available */
- if (strcmp (info->connection_path, nm_connection_get_path (connection)))
- return;
+ nm_manager_pending_activation_remove (self, pending);
- /* Will destroy below; can't be valid during the initial activation start */
- priv->pending_connection_info = NULL;
+ connection = nm_manager_get_connection_by_object_path (self,
+ pending->scope,
+ pending->connection_path);
+ if (!connection) {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_UNKNOWN_CONNECTION,
+ "Connection could not be found.");
+ goto out;
+ }
- path = nm_manager_activate_connection (manager,
+ path = nm_manager_activate_connection (self,
connection,
- info->specific_object_path,
- info->device_path,
+ pending->specific_object_path,
+ pending->device_path,
TRUE,
&error);
- if (path) {
- dbus_g_method_return (info->context, path);
- g_object_notify (G_OBJECT (manager), NM_MANAGER_ACTIVE_CONNECTIONS);
- } else {
- dbus_g_method_return_error (info->context, error);
+ if (!path) {
nm_log_warn (LOGD_CORE, "connection (%d) %s failed to activate: (%d) %s",
- scope, info->connection_path, error->code, error->message);
- g_error_free (error);
- }
+ pending->scope, pending->connection_path, error->code, error->message);
+ } else
+ g_object_notify (G_OBJECT (pending->manager), NM_MANAGER_ACTIVE_CONNECTIONS);
- pending_connection_info_destroy (info);
+out:
+ pending_activation_destroy (pending, error, path);
+ g_clear_error (&error);
}
-static gboolean
-is_user_request_authorized (NMManager *manager,
- DBusGMethodInvocation *context,
- GError **error)
+static void
+connection_added_default_handler (NMManager *self,
+ NMConnection *connection,
+ NMConnectionScope scope)
{
- NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
- DBusConnection *connection;
- char *sender = NULL;
- gulong sender_uid = G_MAXULONG;
- DBusError dbus_error;
- char *service_owner = NULL;
- const char *service_name;
- gulong service_uid = G_MAXULONG;
- gboolean success = FALSE;
-
- /* Ensure the request to activate the user connection came from the
- * same session as the user settings service. FIXME: use ConsoleKit
- * too.
- */
- if (!priv->user_proxy) {
- g_set_error (error, NM_MANAGER_ERROR,
- NM_MANAGER_ERROR_INVALID_SERVICE,
- "%s", "No user settings service available");
- goto out;
- }
-
- sender = dbus_g_method_get_sender (context);
- if (!sender) {
- g_set_error (error, NM_MANAGER_ERROR,
- NM_MANAGER_ERROR_PERMISSION_DENIED,
- "%s", "Could not determine D-Bus requestor");
- goto out;
- }
-
- connection = nm_dbus_manager_get_dbus_connection (priv->dbus_mgr);
- if (!connection) {
- g_set_error (error, NM_MANAGER_ERROR,
- NM_MANAGER_ERROR_PERMISSION_DENIED,
- "%s", "Could not get the D-Bus system bus");
- goto out;
- }
-
- dbus_error_init (&dbus_error);
- /* FIXME: do this async */
- sender_uid = dbus_bus_get_unix_user (connection, sender, &dbus_error);
- if (dbus_error_is_set (&dbus_error)) {
- dbus_error_free (&dbus_error);
- g_set_error (error, NM_MANAGER_ERROR,
- NM_MANAGER_ERROR_PERMISSION_DENIED,
- "%s", "Could not determine the Unix user ID of the requestor");
- goto out;
- }
-
- /* Let root activate anything.
- * FIXME: use a PolicyKit permission instead
- */
- if (0 == sender_uid) {
- success = TRUE;
- goto out;
+ PendingActivation *pending;
+
+ pending = nm_manager_pending_activation_find (self,
+ nm_connection_get_path (connection),
+ scope);
+ if (pending) {
+ pending->have_connection = TRUE;
+ check_pending_ready (self, pending);
}
+}
- service_name = dbus_g_proxy_get_bus_name (priv->user_proxy);
- if (!service_name) {
- g_set_error (error, NM_MANAGER_ERROR,
- NM_MANAGER_ERROR_PERMISSION_DENIED,
- "%s", "Could not determine user settings service name");
- goto out;
- }
+static void
+activation_auth_done (PendingActivation *pending, GError *error)
+{
+ if (error) {
+ nm_manager_pending_activation_remove (pending->manager, pending);
+ pending_activation_destroy (pending, error, NULL);
+ return;
+ } else {
+ pending->authorized = TRUE;
- service_owner = nm_dbus_manager_get_name_owner (priv->dbus_mgr, service_name, NULL);
- if (!service_owner) {
- g_set_error (error, NM_MANAGER_ERROR,
- NM_MANAGER_ERROR_PERMISSION_DENIED,
- "%s", "Could not determine D-Bus owner of the user settings service");
- goto out;
- }
+ /* Now that we're authorized, if the connection hasn't shown up yet,
+ * start a timer and wait for it.
+ */
+ if (!pending->have_connection && !pending->timeout_id)
+ pending->timeout_id = g_timeout_add_seconds (5, wait_for_connection_expired, pending);
- dbus_error_init (&dbus_error);
- /* FIXME: do this async */
- service_uid = dbus_bus_get_unix_user (connection, service_owner, &dbus_error);
- if (dbus_error_is_set (&dbus_error)) {
- dbus_error_free (&dbus_error);
- g_set_error (error, NM_MANAGER_ERROR,
- NM_MANAGER_ERROR_PERMISSION_DENIED,
- "%s", "Could not determine the Unix UID of the sender of the request");
- goto out;
+ check_pending_ready (pending->manager, pending);
}
-
- /* And finally, the actual UID check */
- if (sender_uid != service_uid) {
- g_set_error (error, NM_MANAGER_ERROR,
- NM_MANAGER_ERROR_PERMISSION_DENIED,
- "%s", "Requestor UID does not match the UID of the user settings service");
- goto out;
- }
-
- success = TRUE;
-
-out:
- g_free (sender);
- g_free (service_owner);
- return success;
}
static void
-impl_manager_activate_connection (NMManager *manager,
+impl_manager_activate_connection (NMManager *self,
const char *service_name,
const char *connection_path,
const char *device_path,
const char *specific_object_path,
DBusGMethodInvocation *context)
{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
NMConnectionScope scope = NM_CONNECTION_SCOPE_UNKNOWN;
- NMConnection *connection;
+ PendingActivation *pending;
GError *error = NULL;
- char *real_sop = NULL;
- char *path = NULL;
-
- if (!strcmp (service_name, NM_DBUS_SERVICE_USER_SETTINGS)) {
- if (!is_user_request_authorized (manager, context, &error))
- goto err;
+ if (!strcmp (service_name, NM_DBUS_SERVICE_USER_SETTINGS))
scope = NM_CONNECTION_SCOPE_USER;
- } else if (!strcmp (service_name, NM_DBUS_SERVICE_SYSTEM_SETTINGS))
+ else if (!strcmp (service_name, NM_DBUS_SERVICE_SYSTEM_SETTINGS))
scope = NM_CONNECTION_SCOPE_SYSTEM;
else {
- g_set_error (&error,
- NM_MANAGER_ERROR, NM_MANAGER_ERROR_INVALID_SERVICE,
- "%s", "Invalid settings service name");
- goto err;
- }
-
- /* "/" is special-cased to NULL to get through D-Bus */
- if (specific_object_path && strcmp (specific_object_path, "/"))
- real_sop = g_strdup (specific_object_path);
-
- connection = nm_manager_get_connection_by_object_path (manager, scope, connection_path);
- if (connection) {
- path = (char *) nm_manager_activate_connection (manager,
- connection,
- real_sop,
- device_path,
- TRUE,
- &error);
- if (path) {
- dbus_g_method_return (context, path);
- g_object_notify (G_OBJECT (manager), NM_MANAGER_ACTIVE_CONNECTIONS);
- }
- } else {
- PendingConnectionInfo *info;
- NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
-
- if (priv->pending_connection_info) {
- pending_connection_info_destroy (priv->pending_connection_info);
- priv->pending_connection_info = NULL;
- }
-
- /* Don't have the connection quite yet, probably created by
- * the client on-the-fly. Defer the activation until we have it
- */
-
- info = g_slice_new0 (PendingConnectionInfo);
- info->context = context;
- info->device_path = g_strdup (device_path);
- info->scope = scope;
- info->connection_path = g_strdup (connection_path);
- info->specific_object_path = g_strdup (real_sop);
- info->timeout_id = g_timeout_add_seconds (5, wait_for_connection_expired, manager);
-
- // FIXME: should probably be per-device, not global to the manager
- NM_MANAGER_GET_PRIVATE (manager)->pending_connection_info = info;
- }
-
- err:
- if (error) {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_INVALID_SERVICE,
+ "Invalid settings service name");
dbus_g_method_return_error (context, error);
nm_log_warn (LOGD_CORE, "connection (%d) %s failed to activate: (%d) %s",
scope, connection_path, error->code, error->message);
g_error_free (error);
+ return;
}
- g_free (real_sop);
+ /* Need to check the caller's permissions and stuff before we can
+ * activate the connection.
+ */
+ pending = pending_activation_new (self,
+ priv->authority,
+ context,
+ device_path,
+ scope,
+ connection_path,
+ specific_object_path,
+ activation_auth_done);
+ priv->pending_activations = g_slist_prepend (priv->pending_activations, pending);
+
+ if (nm_manager_get_connection_by_object_path (self, scope, connection_path))
+ pending->have_connection = TRUE;
+
+ pending_activation_check_authorized (pending, priv->dbus_mgr, priv->user_proxy);
}
gboolean
@@ -2644,65 +3161,194 @@ done:
return success;
}
-static gboolean
-impl_manager_deactivate_connection (NMManager *manager,
- const char *connection_path,
- GError **error)
+static void
+deactivate_user_auth_done_cb (NMAuthChain *chain,
+ GError *error,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
{
- return nm_manager_deactivate_connection (manager,
- connection_path,
- NM_DEVICE_STATE_REASON_USER_REQUESTED,
- error);
+ NMManager *self = NM_MANAGER (user_data);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ GError *ret_error = NULL;
+ NMAuthCallResult result;
+
+ priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
+
+ result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS));
+ ret_error = deactivate_disconnect_check_error (error, result, "Deactivate");
+ if (!ret_error) {
+ /* Everything authorized, deactivate the connection */
+ if (nm_manager_deactivate_connection (self,
+ nm_auth_chain_get_data (chain, "path"),
+ NM_DEVICE_STATE_REASON_USER_REQUESTED,
+ &ret_error))
+ dbus_g_method_return (context);
+ }
+
+ if (ret_error)
+ dbus_g_method_return_error (context, ret_error);
+ g_clear_error (&ret_error);
+
+ nm_auth_chain_unref (chain);
}
-static gboolean
-impl_manager_sleep (NMManager *self, gboolean sleep, GError **error)
+static void
+deactivate_net_auth_done_cb (NMAuthChain *chain,
+ GError *error,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
{
- NMManagerPrivate *priv;
- GSList *iter;
+ NMManager *self = NM_MANAGER (user_data);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ GError *ret_error = NULL;
+ NMAuthCallResult result;
+ const char *active_path;
+ NMConnectionScope scope;
- g_return_val_if_fail (NM_IS_MANAGER (self), FALSE);
+ priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
- priv = NM_MANAGER_GET_PRIVATE (self);
+ result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL));
+ ret_error = deactivate_disconnect_check_error (error, result, "Deactivate");
+ if (ret_error) {
+ dbus_g_method_return_error (context, ret_error);
+ g_error_free (ret_error);
+ goto done;
+ }
- if (priv->sleeping == sleep) {
- g_set_error (error,
- NM_MANAGER_ERROR, NM_MANAGER_ERROR_ALREADY_ASLEEP_OR_AWAKE,
- "Already %s", sleep ? "asleep" : "awake");
- return FALSE;
+ /* If it's a system connection, we're done */
+ active_path = nm_auth_chain_get_data (chain, "path");
+ scope = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "scope"));
+ if (scope == NM_CONNECTION_SCOPE_USER) {
+ NMAuthChain *user_chain;
+
+ /* It's a user connection, so we need to ensure the caller is
+ * authorized to manipulate user connections.
+ */
+ user_chain = nm_auth_chain_new (priv->authority, context, NULL, deactivate_user_auth_done_cb, self);
+ g_assert (user_chain);
+ priv->auth_chains = g_slist_append (priv->auth_chains, user_chain);
+
+ nm_auth_chain_set_data (user_chain, "path", g_strdup (active_path), g_free);
+ nm_auth_chain_set_data (user_chain, "scope", GUINT_TO_POINTER (scope), NULL);
+ nm_auth_chain_add_call (user_chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, TRUE);
+ } else {
+ if (!nm_manager_deactivate_connection (self,
+ active_path,
+ NM_DEVICE_STATE_REASON_USER_REQUESTED,
+ &ret_error)) {
+ dbus_g_method_return_error (context, ret_error);
+ g_clear_error (&ret_error);
+ } else
+ dbus_g_method_return (context);
}
- priv->sleeping = sleep;
+done:
+ nm_auth_chain_unref (chain);
+}
- /* Update "NetworkingEnabled" key in state file */
- if (priv->state_file) {
- GError *err = NULL;
- gboolean networking_enabled = !sleep;
+static void
+impl_manager_deactivate_connection (NMManager *self,
+ const char *active_path,
+ DBusGMethodInvocation *context)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ NMConnection *connection = NULL;
+ GError *error = NULL;
+ GSList *iter;
+ NMAuthChain *chain;
+ gulong sender_uid = G_MAXULONG;
+ NMConnectionScope scope;
+ const char *error_desc = NULL;
- if (!write_value_to_state_file (priv->state_file,
- "main", "NetworkingEnabled",
- G_TYPE_BOOLEAN, (gpointer) &networking_enabled,
- &err)) {
- nm_log_warn (LOGD_SUSPEND, "writing to state file %s failed: (%d) %s.",
- priv->state_file,
- err ? err->code : -1,
- (err && err->message) ? err->message : "unknown");
+ /* Check for device connections first */
+ for (iter = priv->devices; iter; iter = g_slist_next (iter)) {
+ NMActRequest *req;
+ const char *req_path = NULL;
+
+ req = nm_device_get_act_request (NM_DEVICE (iter->data));
+ if (req)
+ req_path = nm_act_request_get_active_connection_path (req);
+
+ if (req_path && !strcmp (active_path, req_path)) {
+ connection = nm_act_request_get_connection (req);
+ break;
}
+ }
+
+ /* Maybe it's a VPN */
+ if (!connection)
+ connection = nm_vpn_manager_get_connection_for_active (priv->vpn_manager, active_path);
+
+ if (!connection) {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_CONNECTION_NOT_ACTIVE,
+ "The connection was not active.");
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+ /* Need to check the caller's permissions and stuff before we can
+ * deactivate the connection.
+ */
+ scope = nm_connection_get_scope (connection);
+ if (!check_user_authorized (priv->dbus_mgr,
+ priv->user_proxy,
+ context,
+ scope,
+ &sender_uid,
+ &error_desc)) {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ error_desc);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
}
- if (sleep) {
- nm_log_info (LOGD_SUSPEND, "sleeping...");
+ /* Yay for root */
+ if (0 == sender_uid) {
+ if (!nm_manager_deactivate_connection (self,
+ active_path,
+ NM_DEVICE_STATE_REASON_USER_REQUESTED,
+ &error)) {
+ dbus_g_method_return_error (context, error);
+ g_clear_error (&error);
+ } else
+ dbus_g_method_return (context);
+
+ return;
+ }
+
+ /* Otherwise validate the user request */
+ chain = nm_auth_chain_new (priv->authority, context, NULL, deactivate_net_auth_done_cb, self);
+ g_assert (chain);
+ priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+
+ nm_auth_chain_set_data (chain, "path", g_strdup (active_path), g_free);
+ nm_auth_chain_set_data (chain, "scope", GUINT_TO_POINTER (scope), NULL);
+ nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, TRUE);
+}
+
+static void
+do_sleep_wake (NMManager *self)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ const GSList *unmanaged_specs;
+ GSList *iter;
+
+ if (manager_sleeping (self)) {
+ nm_log_info (LOGD_SUSPEND, "sleeping or disabling...");
/* Just deactivate and down all devices from the device list,
- * we'll remove them in 'wake' for speed's sake.
+ * to keep things fast the device list will get resynced when
+ * the manager wakes up.
*/
for (iter = priv->devices; iter; iter = iter->next)
nm_device_set_managed (NM_DEVICE (iter->data), FALSE, NM_DEVICE_STATE_REASON_SLEEPING);
- } else {
- const GSList *unmanaged_specs;
- nm_log_info (LOGD_SUSPEND, "waking up...");
+ } else {
+ nm_log_info (LOGD_SUSPEND, "waking up and re-enabling...");
unmanaged_specs = nm_sysconfig_settings_get_unmanaged_specs (priv->sys_settings);
@@ -2721,13 +3367,13 @@ impl_manager_sleep (NMManager *self, gboolean sleep, GError **error)
*/
for (i = 0; i < RFKILL_TYPE_MAX; i++) {
RadioState *rstate = &priv->radio_states[i];
- gboolean enabled = (rstate->hw_enabled && rstate->enabled);
+ gboolean enabled = radio_enabled_for_rstate (rstate);
RfKillType devtype = RFKILL_TYPE_UNKNOWN;
if (rstate->desc) {
- nm_log_dbg (LOGD_RFKILL, "%s %s devices (hw_enabled %d, enabled %d)",
+ nm_log_dbg (LOGD_RFKILL, "%s %s devices (hw_enabled %d, sw_enabled %d, user_enabled %d)",
enabled ? "enabling" : "disabling",
- rstate->desc, rstate->hw_enabled, rstate->enabled);
+ rstate->desc, rstate->hw_enabled, rstate->sw_enabled, rstate->user_enabled);
}
g_object_get (G_OBJECT (device), NM_DEVICE_INTERFACE_RFKILL_TYPE, &devtype, NULL);
@@ -2747,23 +3393,438 @@ impl_manager_sleep (NMManager *self, gboolean sleep, GError **error)
}
nm_manager_update_state (self);
+}
- g_object_notify (G_OBJECT (self), NM_MANAGER_SLEEPING);
+static gboolean
+return_no_pk_error (PolkitAuthority *authority,
+ const char *detail,
+ DBusGMethodInvocation *context)
+{
+ GError *error;
+
+ if (!authority) {
+ error = g_error_new (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ "%s request failed: PolicyKit not initialized",
+ detail);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return FALSE;
+ }
return TRUE;
}
+static void
+_internal_sleep (NMManager *self, gboolean do_sleep)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+ if (priv->sleeping == do_sleep)
+ return;
+
+ nm_log_info (LOGD_SUSPEND, "%s requested (sleeping: %s enabled: %s)",
+ do_sleep ? "sleep" : "wake",
+ priv->sleeping ? "yes" : "no",
+ priv->net_enabled ? "yes" : "no");
+
+ priv->sleeping = do_sleep;
+
+ do_sleep_wake (self);
+
+ g_object_notify (G_OBJECT (self), NM_MANAGER_SLEEPING);
+}
+
+#if 0
+static void
+sleep_auth_done_cb (NMAuthChain *chain,
+ GError *error,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ NMManager *self = NM_MANAGER (user_data);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ GError *ret_error;
+ NMAuthCallResult result;
+ gboolean do_sleep;
+
+ priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
+
+ result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_SLEEP_WAKE));
+ if (error) {
+ nm_log_dbg (LOGD_SUSPEND, "Sleep/wake request failed: %s", error->message);
+ ret_error = g_error_new (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ "Sleep/wake request failed: %s",
+ error->message);
+ dbus_g_method_return_error (context, ret_error);
+ g_error_free (ret_error);
+ } else if (result != NM_AUTH_CALL_RESULT_YES) {
+ ret_error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ "Not authorized to sleep/wake");
+ dbus_g_method_return_error (context, ret_error);
+ g_error_free (ret_error);
+ } else {
+ /* Auth success */
+ do_sleep = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "sleep"));
+ _internal_sleep (self, do_sleep);
+ dbus_g_method_return (context);
+ }
+
+ nm_auth_chain_unref (chain);
+}
+#endif
+
+static void
+impl_manager_sleep (NMManager *self,
+ gboolean do_sleep,
+ DBusGMethodInvocation *context)
+{
+ NMManagerPrivate *priv;
+ GError *error = NULL;
+#if 0
+ NMAuthChain *chain;
+ gulong sender_uid = G_MAXULONG;
+ const char *error_desc = NULL;
+#endif
+
+ g_return_if_fail (NM_IS_MANAGER (self));
+
+ priv = NM_MANAGER_GET_PRIVATE (self);
+
+ if (priv->sleeping == do_sleep) {
+ error = g_error_new (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_ALREADY_ASLEEP_OR_AWAKE,
+ "Already %s", do_sleep ? "asleep" : "awake");
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ /* Unconditionally allow the request. Previously it was polkit protected
+ * but unfortunately that doesn't work for short-lived processes like
+ * pm-utils. It uses dbus-send without --print-reply, which quits
+ * immediately after sending the request, and NM is unable to obtain the
+ * sender's UID as dbus-send has already dropped off the bus. Thus NM
+ * fails the request. Instead, don't validate the request, but rely on
+ * D-Bus permissions to restrict the call to root.
+ */
+ _internal_sleep (self, do_sleep);
+ dbus_g_method_return (context);
+ return;
+
+#if 0
+ if (!nm_auth_get_caller_uid (context, priv->dbus_mgr, &sender_uid, &error_desc)) {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ error_desc);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ /* Root doesn't need PK authentication */
+ if (0 == sender_uid) {
+ _internal_sleep (self, do_sleep);
+ dbus_g_method_return (context);
+ return;
+ }
+
+ if (!return_no_pk_error (priv->authority, "Sleep/wake", context))
+ return;
+
+ chain = nm_auth_chain_new (priv->authority, context, NULL, sleep_auth_done_cb, self);
+ g_assert (chain);
+ priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+
+ nm_auth_chain_set_data (chain, "sleep", GUINT_TO_POINTER (do_sleep), NULL);
+ nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SLEEP_WAKE, TRUE);
+#endif
+}
+
+static void
+upower_sleeping_cb (DBusGProxy *proxy, gpointer user_data)
+{
+ nm_log_dbg (LOGD_SUSPEND, "Received UPower sleeping signal");
+ _internal_sleep (NM_MANAGER (user_data), TRUE);
+}
+
+static void
+upower_resuming_cb (DBusGProxy *proxy, gpointer user_data)
+{
+ nm_log_dbg (LOGD_SUSPEND, "Received UPower resuming signal");
+ _internal_sleep (NM_MANAGER (user_data), FALSE);
+}
+
+static void
+_internal_enable (NMManager *self, gboolean enable)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ GError *err = NULL;
+
+ /* Update "NetworkingEnabled" key in state file */
+ if (priv->state_file) {
+ if (!write_value_to_state_file (priv->state_file,
+ "main", "NetworkingEnabled",
+ G_TYPE_BOOLEAN, (gpointer) &enable,
+ &err)) {
+ /* Not a hard error */
+ nm_log_warn (LOGD_SUSPEND, "writing to state file %s failed: (%d) %s.",
+ priv->state_file,
+ err ? err->code : -1,
+ (err && err->message) ? err->message : "unknown");
+ }
+ }
+
+ nm_log_info (LOGD_SUSPEND, "%s requested (sleeping: %s enabled: %s)",
+ enable ? "enable" : "disable",
+ priv->sleeping ? "yes" : "no",
+ priv->net_enabled ? "yes" : "no");
+
+ priv->net_enabled = enable;
+
+ do_sleep_wake (self);
+
+ g_object_notify (G_OBJECT (self), NM_MANAGER_NETWORKING_ENABLED);
+}
+
+static void
+enable_net_done_cb (NMAuthChain *chain,
+ GError *error,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ NMManager *self = NM_MANAGER (user_data);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ GError *ret_error;
+ NMAuthCallResult result;
+ gboolean enable;
+
+ priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
+
+ result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK));
+ if (error) {
+ nm_log_dbg (LOGD_CORE, "Enable request failed: %s", error->message);
+ ret_error = g_error_new (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ "Enable request failed: %s",
+ error->message);
+ dbus_g_method_return_error (context, ret_error);
+ g_error_free (ret_error);
+ } else if (result != NM_AUTH_CALL_RESULT_YES) {
+ ret_error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ "Not authorized to enable/disable networking");
+ dbus_g_method_return_error (context, ret_error);
+ g_error_free (ret_error);
+ } else {
+ /* Auth success */
+ enable = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "enable"));
+ _internal_enable (self, enable);
+ dbus_g_method_return (context);
+ }
+
+ nm_auth_chain_unref (chain);
+}
+
+static void
+impl_manager_enable (NMManager *self,
+ gboolean enable,
+ DBusGMethodInvocation *context)
+{
+ NMManagerPrivate *priv;
+ NMAuthChain *chain;
+ GError *error = NULL;
+ gulong sender_uid = G_MAXULONG;
+ const char *error_desc = NULL;
+
+ g_return_if_fail (NM_IS_MANAGER (self));
+
+ priv = NM_MANAGER_GET_PRIVATE (self);
+
+ if (priv->net_enabled == enable) {
+ error = g_error_new (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_ALREADY_ENABLED_OR_DISABLED,
+ "Already %s", enable ? "enabled" : "disabled");
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ if (!nm_auth_get_caller_uid (context, priv->dbus_mgr, &sender_uid, &error_desc)) {
+ error = g_error_new_literal (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ error_desc);
+ dbus_g_method_return_error (context, error);
+ g_error_free (error);
+ return;
+ }
+
+ /* Root doesn't need PK authentication */
+ if (0 == sender_uid) {
+ _internal_enable (self, enable);
+ dbus_g_method_return (context);
+ return;
+ }
+
+ if (!return_no_pk_error (priv->authority, "Enable/disable", context))
+ return;
+
+ chain = nm_auth_chain_new (priv->authority, context, NULL, enable_net_done_cb, self);
+ g_assert (chain);
+ priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+
+ nm_auth_chain_set_data (chain, "enable", GUINT_TO_POINTER (enable), NULL);
+ nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK, TRUE);
+}
+
+/* Permissions */
+
+static void
+user_proxy_permissions_changed_done (NMAuthChain *chain,
+ GError *error,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ NMManager *self = NM_MANAGER (user_data);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ gboolean authorized = FALSE;
+
+ priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
+
+ if (error) {
+ nm_log_warn (LOGD_USER_SET, "User connections unavailable: (%d) %s",
+ error->code, error->message ? error->message : "(unknown)");
+ } else
+ authorized = user_settings_authorized (self, chain);
+
+ if (authorized) {
+ /* User connections are authorized */
+ if (!priv->user_proxy)
+ user_proxy_init (self);
+ } else
+ user_proxy_cleanup (self, TRUE);
+
+ nm_auth_chain_unref (chain);
+}
+
+static void
+pk_authority_changed_cb (GObject *object, gpointer user_data)
+{
+ NMManager *self = NM_MANAGER (user_data);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ NMAuthChain *chain;
+
+ /* If the user settings service wasn't previously authorized, we wouldn't
+ * care about it. But it might be authorized now, so lets check.
+ */
+ if (!priv->user_proxy)
+ user_proxy_init (self);
+ else {
+ /* Otherwise the user settings permissions could have changed so we
+ * need to recheck them.
+ */
+ chain = nm_auth_chain_new (priv->authority,
+ NULL,
+ priv->user_proxy,
+ user_proxy_permissions_changed_done,
+ self);
+ priv->auth_chains = g_slist_prepend (priv->auth_chains, chain);
+
+ nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, FALSE);
+ nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, FALSE);
+ }
+
+ /* Let clients know they should re-check their authorization */
+ g_signal_emit (NM_MANAGER (user_data), signals[CHECK_PERMISSIONS], 0);
+}
+
+static void
+get_perm_add_result (NMAuthChain *chain, GHashTable *results, const char *permission)
+{
+ NMAuthCallResult result;
+
+ result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, permission));
+ if (result == NM_AUTH_CALL_RESULT_YES)
+ g_hash_table_insert (results, (char *) permission, "yes");
+ else if (result == NM_AUTH_CALL_RESULT_NO)
+ g_hash_table_insert (results, (char *) permission, "no");
+ else if (result == NM_AUTH_CALL_RESULT_AUTH)
+ g_hash_table_insert (results, (char *) permission, "auth");
+ else {
+ nm_log_dbg (LOGD_CORE, "unknown auth chain result %d", result);
+ }
+}
+
+static void
+get_permissions_done_cb (NMAuthChain *chain,
+ GError *error,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ NMManager *self = NM_MANAGER (user_data);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ GError *ret_error;
+ GHashTable *results;
+
+ priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
+ if (error) {
+ nm_log_dbg (LOGD_CORE, "Permissions request failed: %s", error->message);
+ ret_error = g_error_new (NM_MANAGER_ERROR,
+ NM_MANAGER_ERROR_PERMISSION_DENIED,
+ "Permissions request failed: %s",
+ error->message);
+ dbus_g_method_return_error (context, ret_error);
+ g_error_free (ret_error);
+ } else {
+ results = g_hash_table_new (g_str_hash, g_str_equal);
+ get_perm_add_result (chain, results, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK);
+ get_perm_add_result (chain, results, NM_AUTH_PERMISSION_SLEEP_WAKE);
+ get_perm_add_result (chain, results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI);
+ get_perm_add_result (chain, results, NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN);
+ get_perm_add_result (chain, results, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS);
+ get_perm_add_result (chain, results, NM_AUTH_PERMISSION_NETWORK_CONTROL);
+ dbus_g_method_return (context, results);
+ g_hash_table_destroy (results);
+ }
+
+ nm_auth_chain_unref (chain);
+}
+
+static void
+impl_manager_get_permissions (NMManager *self,
+ DBusGMethodInvocation *context)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ NMAuthChain *chain;
+
+ if (!return_no_pk_error (priv->authority, "Permissions", context))
+ return;
+
+ chain = nm_auth_chain_new (priv->authority, context, NULL, get_permissions_done_cb, self);
+ g_assert (chain);
+ priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+
+ nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_NETWORK, FALSE);
+ nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_SLEEP_WAKE, FALSE);
+ nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI, FALSE);
+ nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN, FALSE);
+ nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_USE_USER_CONNECTIONS, FALSE);
+ nm_auth_chain_add_call (chain, NM_AUTH_PERMISSION_NETWORK_CONTROL, FALSE);
+}
+
/* Legacy 0.6 compatibility interface */
-static gboolean
-impl_manager_legacy_sleep (NMManager *manager, GError **error)
+static void
+impl_manager_legacy_sleep (NMManager *manager, DBusGMethodInvocation *context)
{
- return impl_manager_sleep (manager, TRUE, error);
+ return impl_manager_sleep (manager, TRUE, context);
}
-static gboolean
-impl_manager_legacy_wake (NMManager *manager, GError **error)
+static void
+impl_manager_legacy_wake (NMManager *manager, DBusGMethodInvocation *context)
{
- return impl_manager_sleep (manager, FALSE, error);
+ return impl_manager_sleep (manager, FALSE, context);
}
static gboolean
@@ -2796,6 +3857,15 @@ impl_manager_set_logging (NMManager *manager,
/* Connections */
+gboolean
+nm_manager_auto_user_connections_allowed (NMManager *self)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+ return priv->user_net_perm == NM_AUTH_CALL_RESULT_YES
+ && priv->user_con_perm == NM_AUTH_CALL_RESULT_YES;
+}
+
static int
connection_sort (gconstpointer pa, gconstpointer pb)
{
@@ -2889,39 +3959,26 @@ nm_manager_start (NMManager *self)
/* Set initial radio enabled/disabled state */
for (i = 0; i < RFKILL_TYPE_MAX; i++) {
RadioState *rstate = &priv->radio_states[i];
- gboolean enabled = TRUE, hw_enabled = TRUE;
+ RfKillState udev_state;
if (!rstate->desc)
continue;
- switch (nm_udev_manager_get_rfkill_state (priv->udev_mgr, i)) {
- case RFKILL_UNBLOCKED:
- enabled = TRUE;
- hw_enabled = TRUE;
- break;
- case RFKILL_SOFT_BLOCKED:
- enabled = FALSE;
- hw_enabled = TRUE;
- break;
- case RFKILL_HARD_BLOCKED:
- enabled = FALSE;
- hw_enabled = FALSE;
- break;
- default:
- break;
- }
+ udev_state = nm_udev_manager_get_rfkill_state (priv->udev_mgr, i);
+ update_rstate_from_rfkill (rstate, udev_state);
- rstate->hw_enabled = hw_enabled;
- nm_log_info (LOGD_RFKILL, "%s %s by radio killswitch; %s by state file",
- rstate->desc,
- (rstate->hw_enabled && enabled) ? "enabled" : "disabled",
- (rstate->enabled) ? "enabled" : "disabled");
- manager_set_radio_enabled (self, rstate, rstate->enabled && enabled);
+ if (rstate->desc) {
+ nm_log_info (LOGD_RFKILL, "%s %s by radio killswitch; %s by state file",
+ rstate->desc,
+ (rstate->hw_enabled && rstate->sw_enabled) ? "enabled" : "disabled",
+ rstate->user_enabled ? "enabled" : "disabled");
+ }
+ manager_update_radio_enabled (self, rstate);
}
- /* Log overall networking status - asleep/running */
+ /* Log overall networking status - enabled/disabled */
nm_log_info (LOGD_CORE, "Networking is %s by state file",
- priv->sleeping ? "disabled" : "enabled");
+ priv->net_enabled ? "enabled" : "disabled");
system_unmanaged_devices_changed_cb (priv->sys_settings, NULL, self);
system_hostname_changed_cb (priv->sys_settings, NULL, self);
@@ -2931,13 +3988,226 @@ nm_manager_start (NMManager *self)
* they will be queried when the user settings service shows up on the
* bus in nm_manager_name_owner_changed().
*/
- if (nm_dbus_manager_name_has_owner (priv->dbus_mgr, NM_DBUS_SERVICE_USER_SETTINGS))
- user_query_connections (self);
+ user_proxy_init (self);
nm_udev_manager_query_devices (priv->udev_mgr);
bluez_manager_resync_devices (self);
}
+static gboolean
+handle_firmware_changed (gpointer user_data)
+{
+ NMManager *self = NM_MANAGER (user_data);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ GSList *iter;
+
+ priv->fw_changed_id = 0;
+
+ if (manager_sleeping (self))
+ return FALSE;
+
+ /* Try to re-enable devices with missing firmware */
+ for (iter = priv->devices; iter; iter = iter->next) {
+ NMDevice *candidate = NM_DEVICE (iter->data);
+ NMDeviceState state = nm_device_get_state (candidate);
+
+ if ( nm_device_get_firmware_missing (candidate)
+ && (state == NM_DEVICE_STATE_UNAVAILABLE)) {
+ nm_log_info (LOGD_CORE, "(%s): firmware may now be available",
+ nm_device_get_iface (candidate));
+
+ /* Re-set unavailable state to try bringing the device up again */
+ nm_device_state_changed (candidate,
+ NM_DEVICE_STATE_UNAVAILABLE,
+ NM_DEVICE_STATE_REASON_NONE);
+ }
+ }
+
+ return FALSE;
+}
+
+static void
+firmware_dir_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ gpointer user_data)
+{
+ NMManager *self = NM_MANAGER (user_data);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+ switch (event_type) {
+ case G_FILE_MONITOR_EVENT_CREATED:
+ case G_FILE_MONITOR_EVENT_CHANGED:
+#if GLIB_CHECK_VERSION(2,23,4)
+ case G_FILE_MONITOR_EVENT_MOVED:
+#endif
+ case G_FILE_MONITOR_EVENT_ATTRIBUTE_CHANGED:
+ case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+ if (!priv->fw_changed_id) {
+ priv->fw_changed_id = g_timeout_add_seconds (4, handle_firmware_changed, self);
+ nm_log_info (LOGD_CORE, "kernel firmware directory '%s' changed",
+ KERNEL_FIRMWARE_DIR);
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+#define PERM_DENIED_ERROR "org.freedesktop.NetworkManager.PermissionDenied"
+
+static void
+prop_set_auth_done_cb (NMAuthChain *chain,
+ GError *error,
+ DBusGMethodInvocation *context,
+ gpointer user_data)
+{
+ NMManager *self = NM_MANAGER (user_data);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ DBusGConnection *bus;
+ DBusConnection *dbus_connection;
+ NMAuthCallResult result;
+ DBusMessage *reply, *request;
+ const char *permission, *prop;
+ gboolean set_enabled = TRUE;
+
+ priv->auth_chains = g_slist_remove (priv->auth_chains, chain);
+
+ request = nm_auth_chain_get_data (chain, "message");
+ permission = nm_auth_chain_get_data (chain, "permission");
+ prop = nm_auth_chain_get_data (chain, "prop");
+ set_enabled = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, "enabled"));
+
+ if (error) {
+ reply = dbus_message_new_error (request, PERM_DENIED_ERROR,
+ "Not authorized to perform this operation");
+ } else {
+ /* Caller has had a chance to obtain authorization, so we only need to
+ * check for 'yes' here.
+ */
+ result = GPOINTER_TO_UINT (nm_auth_chain_get_data (chain, permission));
+ if (result != NM_AUTH_CALL_RESULT_YES) {
+ reply = dbus_message_new_error (request, PERM_DENIED_ERROR,
+ "Not authorized to perform this operation");
+ } else {
+ g_object_set (self, prop, set_enabled, NULL);
+ reply = dbus_message_new_method_return (request);
+ }
+ }
+
+ if (reply) {
+ bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
+ g_assert (bus);
+ dbus_connection = dbus_g_connection_get_connection (bus);
+ g_assert (dbus_connection);
+
+ dbus_connection_send (dbus_connection, reply, NULL);
+ dbus_message_unref (reply);
+ }
+ nm_auth_chain_unref (chain);
+}
+
+static DBusHandlerResult
+prop_filter (DBusConnection *connection,
+ DBusMessage *message,
+ void *user_data)
+{
+ NMManager *self = NM_MANAGER (user_data);
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ DBusMessageIter iter;
+ DBusMessageIter sub;
+ const char *propiface = NULL;
+ const char *propname = NULL;
+ const char *sender = NULL;
+ const char *glib_propname = NULL, *permission = NULL;
+ DBusError dbus_error;
+ gulong uid = G_MAXULONG;
+ DBusMessage *reply = NULL;
+ gboolean set_enabled = FALSE;
+ NMAuthChain *chain;
+
+ /* The sole purpose of this function is to validate property accesses
+ * on the NMManager object since dbus-glib doesn't yet give us this
+ * functionality.
+ */
+
+ if (!dbus_message_is_method_call (message, DBUS_INTERFACE_PROPERTIES, "Set"))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ dbus_message_iter_init (message, &iter);
+
+ /* Get the D-Bus interface of the property to set */
+ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ dbus_message_iter_get_basic (&iter, &propiface);
+ if (!propiface || strcmp (propiface, NM_DBUS_INTERFACE))
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ dbus_message_iter_next (&iter);
+
+ /* Get the property name that's going to be set */
+ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_STRING)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ dbus_message_iter_get_basic (&iter, &propname);
+ dbus_message_iter_next (&iter);
+
+ if (!strcmp (propname, "WirelessEnabled")) {
+ glib_propname = NM_MANAGER_WIRELESS_ENABLED;
+ permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WIFI;
+ } else if (!strcmp (propname, "WwanEnabled")) {
+ glib_propname = NM_MANAGER_WWAN_ENABLED;
+ permission = NM_AUTH_PERMISSION_ENABLE_DISABLE_WWAN;
+ } else
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ /* Get the new value for the property */
+ if (dbus_message_iter_get_arg_type (&iter) != DBUS_TYPE_VARIANT)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ dbus_message_iter_recurse (&iter, &sub);
+ if (dbus_message_iter_get_arg_type (&sub) != DBUS_TYPE_BOOLEAN)
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+ dbus_message_iter_get_basic (&sub, &set_enabled);
+
+ sender = dbus_message_get_sender (message);
+ if (!sender) {
+ reply = dbus_message_new_error (message, PERM_DENIED_ERROR,
+ "Could not determine D-Bus requestor");
+ goto out;
+ }
+
+ dbus_error_init (&dbus_error);
+ uid = dbus_bus_get_unix_user (connection, sender, &dbus_error);
+ if (dbus_error_is_set (&dbus_error)) {
+ reply = dbus_message_new_error (message, PERM_DENIED_ERROR,
+ "Could not determine the user ID of the requestor");
+ dbus_error_free (&dbus_error);
+ goto out;
+ }
+
+ if (uid > 0) {
+ /* Otherwise validate the user request */
+ chain = nm_auth_chain_new_raw_message (priv->authority, message, prop_set_auth_done_cb, self);
+ g_assert (chain);
+ priv->auth_chains = g_slist_append (priv->auth_chains, chain);
+ nm_auth_chain_set_data (chain, "prop", g_strdup (glib_propname), g_free);
+ nm_auth_chain_set_data (chain, "permission", g_strdup (permission), g_free);
+ nm_auth_chain_set_data (chain, "enabled", GUINT_TO_POINTER (set_enabled), NULL);
+ nm_auth_chain_set_data (chain, "message", dbus_message_ref (message), (GDestroyNotify) dbus_message_unref);
+ nm_auth_chain_add_call (chain, permission, TRUE);
+ } else {
+ /* Yay for root */
+ g_object_set (self, glib_propname, set_enabled, NULL);
+ reply = dbus_message_new_method_return (message);
+ }
+
+out:
+ if (reply) {
+ dbus_connection_send (connection, reply, NULL);
+ dbus_message_unref (reply);
+ }
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
NMManager *
nm_manager_get (const char *config_file,
const char *plugins,
@@ -2951,6 +4221,7 @@ nm_manager_get (const char *config_file,
static NMManager *singleton = NULL;
NMManagerPrivate *priv;
DBusGConnection *bus;
+ DBusConnection *dbus_connection;
if (singleton)
return g_object_ref (singleton);
@@ -2962,6 +4233,14 @@ nm_manager_get (const char *config_file,
bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
g_assert (bus);
+ dbus_connection = dbus_g_connection_get_connection (bus);
+ g_assert (dbus_connection);
+
+ if (!dbus_connection_add_filter (dbus_connection, prop_filter, singleton, NULL)) {
+ nm_log_err (LOGD_CORE, "failed to register DBus connection filter");
+ g_object_unref (singleton);
+ return NULL;
+ }
priv->sys_settings = nm_sysconfig_settings_new (config_file, plugins, bus, error);
if (!priv->sys_settings) {
@@ -2974,11 +4253,11 @@ nm_manager_get (const char *config_file,
priv->state_file = g_strdup (state_file);
- priv->sleeping = !initial_net_enabled;
+ priv->net_enabled = initial_net_enabled;
- priv->radio_states[RFKILL_TYPE_WLAN].enabled = initial_wifi_enabled;
- priv->radio_states[RFKILL_TYPE_WWAN].enabled = initial_wwan_enabled;
- priv->radio_states[RFKILL_TYPE_WIMAX].enabled = initial_wimax_enabled;
+ priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = initial_wifi_enabled;
+ priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = initial_wwan_enabled;
+ priv->radio_states[RFKILL_TYPE_WIMAX].user_enabled = initial_wimax_enabled;
g_signal_connect (priv->sys_settings, "notify::" NM_SYSCONFIG_SETTINGS_UNMANAGED_SPECS,
G_CALLBACK (system_unmanaged_devices_changed_cb), singleton);
@@ -3030,6 +4309,9 @@ dispose (GObject *object)
{
NMManager *manager = NM_MANAGER (object);
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
+ GSList *iter;
+ DBusGConnection *bus;
+ DBusConnection *dbus_connection;
if (priv->disposed) {
G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object);
@@ -3037,8 +4319,14 @@ dispose (GObject *object)
}
priv->disposed = TRUE;
- pending_connection_info_destroy (priv->pending_connection_info);
- priv->pending_connection_info = NULL;
+ for (iter = priv->pending_activations; iter; iter = g_slist_next (iter))
+ pending_activation_destroy ((PendingActivation *) iter->data, NULL, NULL);
+ g_slist_free (priv->pending_activations);
+ priv->pending_activations = NULL;
+
+ g_slist_foreach (priv->auth_chains, (GFunc) nm_auth_chain_unref, NULL);
+ g_slist_free (priv->auth_chains);
+ g_object_unref (priv->authority);
while (g_slist_length (priv->secrets_calls))
free_get_secrets_info ((GetSecretsInfo *) priv->secrets_calls->data);
@@ -3047,11 +4335,10 @@ dispose (GObject *object)
priv->devices = remove_one_device (manager,
priv->devices,
NM_DEVICE (priv->devices->data),
- TRUE,
- FALSE);
+ TRUE);
}
- user_destroy_connections (manager);
+ user_proxy_cleanup (manager, FALSE);
g_hash_table_destroy (priv->user_connections);
priv->user_connections = NULL;
@@ -3084,14 +4371,80 @@ dispose (GObject *object)
}
g_object_unref (priv->modem_manager);
+ /* Unregister property filter */
+ bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
+ if (bus) {
+ dbus_connection = dbus_g_connection_get_connection (bus);
+ g_assert (dbus_connection);
+ dbus_connection_remove_filter (dbus_connection, prop_filter, manager);
+ }
g_object_unref (priv->dbus_mgr);
+
if (priv->bluez_mgr)
g_object_unref (priv->bluez_mgr);
+ if (priv->aipd_proxy)
+ g_object_unref (priv->aipd_proxy);
+
+ if (priv->upower_proxy)
+ g_object_unref (priv->upower_proxy);
+
+ if (priv->fw_monitor) {
+ if (priv->fw_monitor_id)
+ g_signal_handler_disconnect (priv->fw_monitor, priv->fw_monitor_id);
+
+ if (priv->fw_changed_id)
+ g_source_remove (priv->fw_changed_id);
+
+ g_file_monitor_cancel (priv->fw_monitor);
+ g_object_unref (priv->fw_monitor);
+ }
+
+ if (priv->timestamp_update_id) {
+ g_source_remove (priv->timestamp_update_id);
+ priv->timestamp_update_id = 0;
+ }
+
G_OBJECT_CLASS (nm_manager_parent_class)->dispose (object);
}
static void
+manager_radio_user_toggled (NMManager *self,
+ RadioState *rstate,
+ gboolean enabled)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+ GError *error = NULL;
+ gboolean old_enabled, new_enabled;
+
+ if (rstate->desc) {
+ nm_log_dbg (LOGD_RFKILL, "(%s): setting radio %s by user",
+ rstate->desc,
+ enabled ? "enabled" : "disabled");
+ }
+
+ /* Update enabled key in state file */
+ if (priv->state_file) {
+ if (!write_value_to_state_file (priv->state_file,
+ "main", rstate->key,
+ G_TYPE_BOOLEAN, (gpointer) &enabled,
+ &error)) {
+ nm_log_warn (LOGD_CORE, "writing to state file %s failed: (%d) %s.",
+ priv->state_file,
+ error ? error->code : -1,
+ (error && error->message) ? error->message : "unknown");
+ g_clear_error (&error);
+ }
+ }
+
+ old_enabled = radio_enabled_for_rstate (rstate);
+ rstate->user_enabled = enabled;
+ new_enabled = radio_enabled_for_rstate (rstate);
+ if (new_enabled != old_enabled)
+ manager_update_radio_enabled (self, rstate);
+}
+
+static void
set_property (GObject *object, guint prop_id,
const GValue *value, GParamSpec *pspec)
{
@@ -3099,15 +4452,19 @@ set_property (GObject *object, guint prop_id,
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
switch (prop_id) {
+ case PROP_NETWORKING_ENABLED:
+ /* Construct only for now */
+ priv->net_enabled = g_value_get_boolean (value);
+ break;
case PROP_WIRELESS_ENABLED:
- manager_set_radio_enabled (NM_MANAGER (object),
- &priv->radio_states[RFKILL_TYPE_WLAN],
- g_value_get_boolean (value));
+ manager_radio_user_toggled (NM_MANAGER (object),
+ &priv->radio_states[RFKILL_TYPE_WLAN],
+ g_value_get_boolean (value));
break;
case PROP_WWAN_ENABLED:
- manager_set_radio_enabled (NM_MANAGER (object),
- &priv->radio_states[RFKILL_TYPE_WWAN],
- g_value_get_boolean (value));
+ manager_radio_user_toggled (NM_MANAGER (object),
+ &priv->radio_states[RFKILL_TYPE_WWAN],
+ g_value_get_boolean (value));
break;
case PROP_WIMAX_ENABLED:
manager_set_radio_enabled (NM_MANAGER (object),
@@ -3128,18 +4485,24 @@ get_property (GObject *object, guint prop_id,
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
switch (prop_id) {
+ case PROP_VERSION:
+ g_value_set_string (value, VERSION);
+ break;
case PROP_STATE:
nm_manager_update_state (self);
g_value_set_uint (value, priv->state);
break;
+ case PROP_NETWORKING_ENABLED:
+ g_value_set_boolean (value, priv->net_enabled);
+ break;
case PROP_WIRELESS_ENABLED:
- g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WLAN].enabled);
+ g_value_set_boolean (value, radio_enabled_for_type (self, RFKILL_TYPE_WLAN));
break;
case PROP_WIRELESS_HARDWARE_ENABLED:
g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WLAN].hw_enabled);
break;
case PROP_WWAN_ENABLED:
- g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WWAN].enabled);
+ g_value_set_boolean (value, radio_enabled_for_type (self, RFKILL_TYPE_WWAN));
break;
case PROP_WWAN_HARDWARE_ENABLED:
g_value_set_boolean (value, priv->radio_states[RFKILL_TYPE_WWAN].hw_enabled);
@@ -3165,17 +4528,41 @@ get_property (GObject *object, guint prop_id,
}
}
+static gboolean
+periodic_update_active_connection_timestamps (gpointer user_data)
+{
+ NMManager *manager = NM_MANAGER (user_data);
+ GPtrArray *active;
+ int i;
+
+ active = get_active_connections (manager, NULL);
+
+ for (i = 0; i < active->len; i++) {
+ const char *active_path = g_ptr_array_index (active, i);
+ NMActRequest *req;
+ NMDevice *device = NULL;
+
+ req = nm_manager_get_act_request_by_path (manager, active_path, &device);
+ if (device && nm_device_get_state (device) == NM_DEVICE_STATE_ACTIVATED)
+ update_active_connection_timestamp (manager, device);
+ }
+
+ return TRUE;
+}
+
static void
nm_manager_init (NMManager *manager)
{
NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (manager);
DBusGConnection *g_connection;
guint id, i;
+ GFile *file;
+ GError *error = NULL;
/* Initialize rfkill structures and states */
memset (priv->radio_states, 0, sizeof (priv->radio_states));
- priv->radio_states[RFKILL_TYPE_WLAN].enabled = TRUE;
+ priv->radio_states[RFKILL_TYPE_WLAN].user_enabled = TRUE;
priv->radio_states[RFKILL_TYPE_WLAN].key = "WirelessEnabled";
priv->radio_states[RFKILL_TYPE_WLAN].prop = NM_MANAGER_WIRELESS_ENABLED;
priv->radio_states[RFKILL_TYPE_WLAN].hw_prop = NM_MANAGER_WIRELESS_HARDWARE_ENABLED;
@@ -3183,7 +4570,7 @@ nm_manager_init (NMManager *manager)
priv->radio_states[RFKILL_TYPE_WLAN].other_enabled_func = nm_manager_get_ipw_rfkill_state;
priv->radio_states[RFKILL_TYPE_WLAN].rtype = RFKILL_TYPE_WLAN;
- priv->radio_states[RFKILL_TYPE_WWAN].enabled = TRUE;
+ priv->radio_states[RFKILL_TYPE_WWAN].user_enabled = TRUE;
priv->radio_states[RFKILL_TYPE_WWAN].key = "WWANEnabled";
priv->radio_states[RFKILL_TYPE_WWAN].prop = NM_MANAGER_WWAN_ENABLED;
priv->radio_states[RFKILL_TYPE_WWAN].hw_prop = NM_MANAGER_WWAN_HARDWARE_ENABLED;
@@ -3191,7 +4578,7 @@ nm_manager_init (NMManager *manager)
priv->radio_states[RFKILL_TYPE_WWAN].other_enabled_func = nm_manager_get_modem_enabled_state;
priv->radio_states[RFKILL_TYPE_WWAN].rtype = RFKILL_TYPE_WWAN;
- priv->radio_states[RFKILL_TYPE_WIMAX].enabled = TRUE;
+ priv->radio_states[RFKILL_TYPE_WIMAX].user_enabled = TRUE;
priv->radio_states[RFKILL_TYPE_WIMAX].key = "WiMAXEnabled";
priv->radio_states[RFKILL_TYPE_WIMAX].prop = NM_MANAGER_WIMAX_ENABLED;
priv->radio_states[RFKILL_TYPE_WIMAX].hw_prop = NM_MANAGER_WIMAX_HARDWARE_ENABLED;
@@ -3252,6 +4639,58 @@ nm_manager_init (NMManager *manager)
NULL);
} else
nm_log_warn (LOGD_AUTOIP4, "could not initialize avahi-autoipd D-Bus proxy");
+
+ /* upower sleep/wake handling */
+ priv->upower_proxy = dbus_g_proxy_new_for_name (g_connection,
+ UPOWER_DBUS_SERVICE,
+ "/org/freedesktop/UPower",
+ "org.freedesktop.UPower");
+ if (priv->upower_proxy) {
+ dbus_g_proxy_add_signal (priv->upower_proxy, "Sleeping", G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->upower_proxy, "Sleeping",
+ G_CALLBACK (upower_sleeping_cb),
+ manager, NULL);
+
+ dbus_g_proxy_add_signal (priv->upower_proxy, "Resuming", G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->upower_proxy, "Resuming",
+ G_CALLBACK (upower_resuming_cb),
+ manager, NULL);
+ } else
+ nm_log_warn (LOGD_SUSPEND, "could not initialize UPower D-Bus proxy");
+
+ priv->authority = polkit_authority_get_sync (NULL, &error);
+ if (priv->authority) {
+ priv->auth_changed_id = g_signal_connect (priv->authority,
+ "changed",
+ G_CALLBACK (pk_authority_changed_cb),
+ manager);
+ } else {
+ nm_log_warn (LOGD_CORE, "failed to create PolicyKit authority: (%d) %s",
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ }
+
+ /* Monitor the firmware directory */
+ if (strlen (KERNEL_FIRMWARE_DIR)) {
+ file = g_file_new_for_path (KERNEL_FIRMWARE_DIR "/");
+ priv->fw_monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
+ g_object_unref (file);
+ }
+
+ if (priv->fw_monitor) {
+ priv->fw_monitor_id = g_signal_connect (priv->fw_monitor, "changed",
+ G_CALLBACK (firmware_dir_changed),
+ manager);
+ nm_log_info (LOGD_CORE, "monitoring kernel firmware directory '%s'.",
+ KERNEL_FIRMWARE_DIR);
+ } else {
+ nm_log_warn (LOGD_CORE, "failed to monitor kernel firmware directory '%s'.",
+ KERNEL_FIRMWARE_DIR);
+ }
+
+ /* Update timestamps in active connections */
+ priv->timestamp_update_id = g_timeout_add_seconds (300, (GSourceFunc) periodic_update_active_connection_timestamps, manager);
}
static void
@@ -3270,6 +4709,14 @@ nm_manager_class_init (NMManagerClass *manager_class)
/* properties */
g_object_class_install_property
+ (object_class, PROP_VERSION,
+ g_param_spec_string (NM_MANAGER_VERSION,
+ "Version",
+ "NetworkManager version",
+ NULL,
+ G_PARAM_READABLE));
+
+ g_object_class_install_property
(object_class, PROP_STATE,
g_param_spec_uint (NM_MANAGER_STATE,
"State",
@@ -3278,6 +4725,14 @@ nm_manager_class_init (NMManagerClass *manager_class)
G_PARAM_READABLE));
g_object_class_install_property
+ (object_class, PROP_NETWORKING_ENABLED,
+ g_param_spec_boolean (NM_MANAGER_NETWORKING_ENABLED,
+ "NetworkingEnabled",
+ "Is networking enabled",
+ TRUE,
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+
+ g_object_class_install_property
(object_class, PROP_WIRELESS_ENABLED,
g_param_spec_boolean (NM_MANAGER_WIRELESS_ENABLED,
"WirelessEnabled",
@@ -3342,6 +4797,7 @@ nm_manager_class_init (NMManagerClass *manager_class)
NULL,
G_PARAM_READABLE | NM_PROPERTY_PARAM_NO_EXPORT));
+ /* Sleeping is not exported over D-Bus */
g_object_class_install_property
(object_class, PROP_SLEEPING,
g_param_spec_boolean (NM_MANAGER_SLEEPING,
@@ -3418,6 +4874,22 @@ nm_manager_class_init (NMManagerClass *manager_class)
_nm_marshal_VOID__OBJECT_UINT,
G_TYPE_NONE, 2, G_TYPE_OBJECT, G_TYPE_UINT);
+ signals[CHECK_PERMISSIONS] =
+ g_signal_new ("check-permissions",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[USER_PERMISSIONS_CHANGED] =
+ g_signal_new ("user-permissions-changed",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
/* StateChange is DEPRECATED */
signals[STATE_CHANGE] =
g_signal_new ("state-change",
diff --git a/src/nm-manager.h b/src/nm-manager.h
index bebd9217b7..42472407d1 100644
--- a/src/nm-manager.h
+++ b/src/nm-manager.h
@@ -16,7 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2007 - 2008 Novell, Inc.
- * Copyright (C) 2007 - 2008 Red Hat, Inc.
+ * Copyright (C) 2007 - 2010 Red Hat, Inc.
*/
#ifndef NM_MANAGER_H
@@ -35,6 +35,17 @@
#define NM_IS_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_MANAGER))
#define NM_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_MANAGER, NMManagerClass))
+#define NM_MANAGER_VERSION "version"
+#define NM_MANAGER_STATE "state"
+#define NM_MANAGER_NETWORKING_ENABLED "networking-enabled"
+#define NM_MANAGER_WIRELESS_ENABLED "wireless-enabled"
+#define NM_MANAGER_WIRELESS_HARDWARE_ENABLED "wireless-hardware-enabled"
+#define NM_MANAGER_WWAN_ENABLED "wwan-enabled"
+#define NM_MANAGER_WWAN_HARDWARE_ENABLED "wwan-hardware-enabled"
+#define NM_MANAGER_WIMAX_ENABLED "wimax-enabled"
+#define NM_MANAGER_WIMAX_HARDWARE_ENABLED "wimax-hardware-enabled"
+#define NM_MANAGER_ACTIVE_CONNECTIONS "active-connections"
+
/* Not exported */
#define NM_MANAGER_HOSTNAME "hostname"
#define NM_MANAGER_SLEEPING "sleeping"
@@ -104,6 +115,8 @@ NMState nm_manager_get_state (NMManager *manager);
GSList *nm_manager_get_connections (NMManager *manager, NMConnectionScope scope);
+gboolean nm_manager_auto_user_connections_allowed (NMManager *manager);
+
NMConnection * nm_manager_get_connection_by_object_path (NMManager *manager,
NMConnectionScope scope,
const char *path);
diff --git a/src/nm-netlink-monitor.c b/src/nm-netlink-monitor.c
index 10bf239d5d..13b3ab9850 100644
--- a/src/nm-netlink-monitor.c
+++ b/src/nm-netlink-monitor.c
@@ -282,7 +282,7 @@ event_connection_setup (NMNetlinkMonitor *self, GError **error)
g_return_val_if_fail (priv->io_channel == NULL, FALSE);
/* Set up the event listener connection */
- cb = nl_cb_alloc (NL_CB_VERBOSE);
+ cb = nl_cb_alloc (NL_CB_DEFAULT);
priv->nlh_event = nl_handle_alloc_cb (cb);
nl_cb_put (cb);
if (!priv->nlh_event) {
@@ -344,7 +344,7 @@ sync_connection_setup (NMNetlinkMonitor *self, GError **error)
#endif
/* Set up the event listener connection */
- cb = nl_cb_alloc (NL_CB_VERBOSE);
+ cb = nl_cb_alloc (NL_CB_DEFAULT);
priv->nlh_sync = nl_handle_alloc_cb (cb);
nl_cb_put (cb);
if (!priv->nlh_sync) {
diff --git a/src/nm-policy-hostname.c b/src/nm-policy-hostname.c
index a273a9202f..4fe69c5a9b 100644
--- a/src/nm-policy-hostname.c
+++ b/src/nm-policy-hostname.c
@@ -24,12 +24,12 @@
#include <errno.h>
#include <netdb.h>
#include <ctype.h>
+#include <arpa/inet.h>
#include <glib.h>
#include "nm-logging.h"
#include "nm-policy-hostname.h"
-#include "nm-policy-hosts.h"
/************************************************************************/
@@ -40,7 +40,10 @@ struct HostnameThread {
gboolean dead;
int ret;
- guint32 ip4_addr;
+ struct sockaddr_in addr4;
+ struct sockaddr_in6 addr6;
+ struct sockaddr *addr;
+ size_t addr_size;
char hostname[NI_MAXHOST + 1];
HostnameThreadCallback callback;
@@ -53,9 +56,10 @@ hostname_thread_run_cb (gpointer user_data)
HostnameThread *ht = (HostnameThread *) user_data;
const char *hostname = NULL;
- if (strlen (ht->hostname))
+ if (strlen (ht->hostname) && strcmp (ht->hostname, "."))
hostname = ht->hostname;
+ nm_log_dbg (LOGD_DNS, "(%p) calling address reverse-lookup result handler", ht);
(*ht->callback) (ht, ht->ret, hostname, ht->user_data);
return FALSE;
}
@@ -64,9 +68,10 @@ static gpointer
hostname_thread_worker (gpointer data)
{
HostnameThread *ht = (HostnameThread *) data;
- struct sockaddr_in addr;
int i;
+ nm_log_dbg (LOGD_DNS, "(%p) starting address reverse-lookup", ht);
+
g_mutex_lock (ht->lock);
if (ht->dead) {
g_mutex_unlock (ht->lock);
@@ -74,21 +79,22 @@ hostname_thread_worker (gpointer data)
}
g_mutex_unlock (ht->lock);
- addr.sin_family = AF_INET;
- addr.sin_addr.s_addr = ht->ip4_addr;
-
- ht->ret = getnameinfo ((struct sockaddr *) &addr, sizeof (struct sockaddr_in),
- ht->hostname, NI_MAXHOST, NULL, 0,
- NI_NAMEREQD);
+ ht->ret = getnameinfo (ht->addr, ht->addr_size, ht->hostname, NI_MAXHOST, NULL, 0, NI_NAMEREQD);
if (ht->ret == 0) {
+ nm_log_dbg (LOGD_DNS, "(%p) address reverse-lookup returned hostname '%s'",
+ ht, ht->hostname);
for (i = 0; i < strlen (ht->hostname); i++)
ht->hostname[i] = tolower (ht->hostname[i]);
+ } else {
+ nm_log_dbg (LOGD_DNS, "(%p) address reverse-lookup failed: (%d) %s",
+ ht, ht->ret, gai_strerror (ht->ret));
}
/* Don't track the idle handler ID because by the time the g_idle_add()
* returns the ID, the handler may already have run and freed the
* HostnameThread.
*/
+ nm_log_dbg (LOGD_DNS, "(%p) scheduling address reverse-lookup result handler", ht);
g_idle_add (hostname_thread_run_cb, ht);
return (gpointer) TRUE;
}
@@ -98,15 +104,21 @@ hostname_thread_free (HostnameThread *ht)
{
g_return_if_fail (ht != NULL);
+ nm_log_dbg (LOGD_DNS, "(%p) freeing reverse-lookup thread", ht);
+
g_mutex_free (ht->lock);
memset (ht, 0, sizeof (HostnameThread));
g_free (ht);
}
HostnameThread *
-hostname_thread_new (guint32 ip4_addr, HostnameThreadCallback callback, gpointer user_data)
+hostname4_thread_new (guint32 ip4_addr,
+ HostnameThreadCallback callback,
+ gpointer user_data)
{
HostnameThread *ht;
+ struct sockaddr_in addr4;
+ char buf[INET_ADDRSTRLEN + 1];
ht = g_malloc0 (sizeof (HostnameThread));
g_assert (ht);
@@ -114,14 +126,59 @@ hostname_thread_new (guint32 ip4_addr, HostnameThreadCallback callback, gpointer
ht->lock = g_mutex_new ();
ht->callback = callback;
ht->user_data = user_data;
- ht->ip4_addr = ip4_addr;
+
+ ht->addr4.sin_family = AF_INET;
+ ht->addr4.sin_addr.s_addr = ip4_addr;
+ ht->addr = (struct sockaddr *) &ht->addr4;
+ ht->addr_size = sizeof (ht->addr4);
ht->thread = g_thread_create (hostname_thread_worker, ht, FALSE, NULL);
if (!ht->thread) {
hostname_thread_free (ht);
- ht = NULL;
+ return NULL;
}
+ if (!inet_ntop (AF_INET, &addr4.sin_addr, buf, sizeof (buf)))
+ strcpy (buf, "(unknown)");
+
+ nm_log_dbg (LOGD_DNS, "(%p) started IPv4 reverse-lookup thread for address '%s'",
+ ht, buf);
+
+ return ht;
+}
+
+HostnameThread *
+hostname6_thread_new (const struct in6_addr *ip6_addr,
+ HostnameThreadCallback callback,
+ gpointer user_data)
+{
+ HostnameThread *ht;
+ char buf[INET6_ADDRSTRLEN + 1];
+
+ ht = g_malloc0 (sizeof (HostnameThread));
+ g_assert (ht);
+
+ ht->lock = g_mutex_new ();
+ ht->callback = callback;
+ ht->user_data = user_data;
+
+ ht->addr6.sin6_family = AF_INET6;
+ ht->addr6.sin6_addr = *ip6_addr;
+ ht->addr = (struct sockaddr *) &ht->addr6;
+ ht->addr_size = sizeof (ht->addr6);
+
+ ht->thread = g_thread_create (hostname_thread_worker, ht, FALSE, NULL);
+ if (!ht->thread) {
+ hostname_thread_free (ht);
+ return NULL;
+ }
+
+ if (!inet_ntop (AF_INET, ip6_addr, buf, sizeof (buf)))
+ strcpy (buf, "(unknown)");
+
+ nm_log_dbg (LOGD_DNS, "(%p) started IPv6 reverse-lookup thread for address '%s'",
+ ht, buf);
+
return ht;
}
@@ -130,6 +187,8 @@ hostname_thread_kill (HostnameThread *ht)
{
g_return_if_fail (ht != NULL);
+ nm_log_dbg (LOGD_DNS, "(%p) stopping reverse-lookup thread", ht);
+
g_mutex_lock (ht->lock);
ht->dead = TRUE;
g_mutex_unlock (ht->lock);
@@ -145,21 +204,18 @@ hostname_thread_is_dead (HostnameThread *ht)
/************************************************************************/
-#define FALLBACK_HOSTNAME "localhost.localdomain"
+#define FALLBACK_HOSTNAME4 "localhost.localdomain"
gboolean
nm_policy_set_system_hostname (const char *new_hostname, const char *msg)
{
char old_hostname[HOST_NAME_MAX + 1];
- int ret = 0;
const char *name;
- gboolean set_hostname = TRUE, changed = FALSE;
+ int ret;
if (new_hostname)
g_warn_if_fail (strlen (new_hostname));
- name = (new_hostname && strlen (new_hostname)) ? new_hostname : FALLBACK_HOSTNAME;
-
old_hostname[HOST_NAME_MAX] = '\0';
errno = 0;
ret = gethostname (old_hostname, HOST_NAME_MAX);
@@ -169,37 +225,19 @@ nm_policy_set_system_hostname (const char *new_hostname, const char *msg)
} else {
/* Don't set the hostname if it isn't actually changing */
if ( (new_hostname && !strcmp (old_hostname, new_hostname))
- || (!new_hostname && !strcmp (old_hostname, FALLBACK_HOSTNAME)))
- set_hostname = FALSE;
- }
-
- if (set_hostname) {
- nm_log_info (LOGD_DNS, "Setting system hostname to '%s' (%s)", name, msg);
- ret = sethostname (name, strlen (name));
- if (ret != 0) {
- nm_log_warn (LOGD_DNS, "couldn't set the system hostname to '%s': (%d) %s",
- name, errno, strerror (errno));
+ || (!new_hostname && !strcmp (old_hostname, FALLBACK_HOSTNAME4)))
return FALSE;
- }
}
- /* But even if the hostname isn't changing, always try updating /etc/hosts
- * just in case the hostname changed while NM wasn't running; we need to
- * make sure that /etc/hosts has valid mappings for '127.0.0.1' and the
- * current system hostname. If those exist,
- * nm_policy_hosts_update_etc_hosts() will just return and won't touch
- * /etc/hosts at all.
- */
- if (!nm_policy_hosts_update_etc_hosts (name, FALLBACK_HOSTNAME, &changed)) {
- /* error updating /etc/hosts; fallback to localhost.localdomain */
- nm_log_info (LOGD_DNS, "Setting system hostname to '" FALLBACK_HOSTNAME "' (error updating /etc/hosts)");
- ret = sethostname (FALLBACK_HOSTNAME, strlen (FALLBACK_HOSTNAME));
- if (ret != 0) {
- nm_log_warn (LOGD_DNS, "couldn't set the fallback system hostname (%s): (%d) %s",
- FALLBACK_HOSTNAME, errno, strerror (errno));
- }
+ name = (new_hostname && strlen (new_hostname)) ? new_hostname : FALLBACK_HOSTNAME4;
+
+ nm_log_info (LOGD_DNS, "Setting system hostname to '%s' (%s)", name, msg);
+ ret = sethostname (name, strlen (name));
+ if (ret != 0) {
+ nm_log_warn (LOGD_DNS, "couldn't set the system hostname to '%s': (%d) %s",
+ name, errno, strerror (errno));
}
- return changed;
+ return (ret == 0);
}
diff --git a/src/nm-policy-hostname.h b/src/nm-policy-hostname.h
index c59ca41078..e76713f16d 100644
--- a/src/nm-policy-hostname.h
+++ b/src/nm-policy-hostname.h
@@ -34,9 +34,13 @@ typedef void (*HostnameThreadCallback) (HostnameThread *ht,
const char *hostname,
gpointer user_data);
-HostnameThread * hostname_thread_new (guint32 ip4_addr,
- HostnameThreadCallback callback,
- gpointer user_data);
+HostnameThread * hostname4_thread_new (guint32 ip4_addr,
+ HostnameThreadCallback callback,
+ gpointer user_data);
+
+HostnameThread * hostname6_thread_new (const struct in6_addr *ip6_addr,
+ HostnameThreadCallback callback,
+ gpointer user_data);
void hostname_thread_free (HostnameThread *ht);
diff --git a/src/nm-policy-hosts.c b/src/nm-policy-hosts.c
index 7723c99759..8bbd1d3b5f 100644
--- a/src/nm-policy-hosts.c
+++ b/src/nm-policy-hosts.c
@@ -18,218 +18,76 @@
* Copyright (C) 2004 - 2010 Red Hat, Inc.
*/
+#include <config.h>
#include <string.h>
-#include <unistd.h>
-#include <errno.h>
-#include <netdb.h>
-#include <ctype.h>
#include "nm-policy-hosts.h"
#include "nm-logging.h"
-gboolean
-nm_policy_hosts_find_token (const char *line, const char *token)
-{
- const char *start = line, *p = line;
-
- g_return_val_if_fail (line != NULL, FALSE);
- g_return_val_if_fail (token != NULL, FALSE);
- g_return_val_if_fail (strlen (token) > 0, FALSE);
-
- /* Walk through the line to find the next whitespace character */
- while (p <= line + strlen (line)) {
- if (isblank (*p) || (*p == '\0')) {
- /* Token starts with 'start' and ends with 'end' */
- if ((p > start) && *start && (p - start == strlen (token)) && !strncmp (start, token, (p - start)))
- return TRUE; /* found */
-
- /* not found; advance start and continue looking */
- start = p + 1;
- }
- p++;
- }
-
- return FALSE;
-}
-
-static gboolean
-is_local_mapping (const char *str, const char *hostname)
-{
- return ( !strncmp (str, "127.0.0.1", strlen ("127.0.0.1"))
- && nm_policy_hosts_find_token (str, hostname ? hostname : "localhost"));
-}
+#define ADDED_TAG "# Added by NetworkManager"
GString *
-nm_policy_get_etc_hosts (const char **lines,
- gsize existing_len,
- const char *hostname,
- const char *fallback_hostname,
- GError **error)
+nm_policy_get_etc_hosts (const char *contents, gsize contents_len)
{
- GString *contents = NULL;
- const char **line;
- gboolean found_host_nonlocal = FALSE;
- gboolean found_host = FALSE;
- gboolean found_localhost = FALSE;
- gboolean initial_comments = TRUE;
- gboolean added = FALSE;
-
- g_return_val_if_fail (lines != NULL, FALSE);
- g_return_val_if_fail (hostname != NULL, FALSE);
-
- /* /etc/hosts needs at least two things:
- *
- * 1) current hostname mapped to any address
- * 2) 'localhost' mapped to 127.0.0.1
- *
- * If both these conditions exist in /etc/hosts, we don't need to bother
- * updating the file.
- */
-
- /* Look for the two cases from above */
- for (line = lines; lines && *line; line++) {
- if (strlen (*line) && (*line[0] != '#')) {
- if (nm_policy_hosts_find_token (*line, hostname)) {
- if (!is_local_mapping (*line, "localhost")) {
- /* hostname is not on a 127.0.0.1 line or the line does not
- * contain 'localhost'.
- */
- found_host_nonlocal = TRUE;
- }
- found_host = TRUE;
- }
-
- if (is_local_mapping (*line, "localhost")) {
- /* a 127.0.0.1 line containing 'localhost' */
- found_localhost = TRUE;
- }
- }
-
- if (found_localhost && found_host)
- return NULL; /* No update required */
- }
+ char **lines = NULL, **iter;
+ GString *new_contents = NULL;
- contents = g_string_sized_new (existing_len ? existing_len + 100 : 200);
- if (!contents) {
- g_set_error_literal (error, 0, 0, "not enough memory");
+ if (contents_len == 0 || !strstr (contents, ADDED_TAG))
return NULL;
- }
-
- /* Construct the new hosts file; replace any 127.0.0.1 entry that is at the
- * beginning of the file or right after initial comments and contains
- * the string 'localhost'. If there is no 127.0.0.1 entry at the beginning
- * or after initial comments that contains 'localhost', add one there
- * and ignore any other 127.0.0.1 entries that contain 'localhost'.
- */
- for (line = lines, initial_comments = TRUE; lines && *line; line++) {
- gboolean add_line = TRUE;
-
- /* This is the first line after the initial comments */
- if (strlen (*line) && initial_comments && (*line[0] != '#')) {
- initial_comments = FALSE;
-
- /* If some other line contained the hostname but not 'localhost',
- * make a simple localhost mapping and assume the user knows what
- * they are doing with their manual hostname entry. Otherwise if
- * the hostname wasn't found somewhere else, add it to the localhost
- * mapping line to make sure it's mapped to something.
- */
- if (found_host_nonlocal)
- g_string_append (contents, "127.0.0.1");
- else
- g_string_append_printf (contents, "127.0.0.1\t%s", hostname);
- if (strcmp (hostname, fallback_hostname)) {
- g_string_append_printf (contents, "\t%s", fallback_hostname);
- /* Don't add a standalone 'localhost.localdomain' 127 mapping */
- if (is_local_mapping (*line, fallback_hostname))
- add_line = FALSE;
- }
+ new_contents = g_string_sized_new (contents_len);
- g_string_append (contents, "\tlocalhost\n");
- added = TRUE;
-
- /* Don't add the original line if it is a 'localhost' mapping */
- if (is_local_mapping (*line, "localhost"))
- add_line = FALSE;
- }
-
- if (add_line) {
- g_string_append (contents, *line);
- /* Only append the new line if this isn't the last line in the file */
- if (*(line+1))
- g_string_append_c (contents, '\n');
+ /* Remove "# Added ..." lines */
+ lines = g_strsplit_set (contents, "\n\r", -1);
+ for (iter = lines; iter && *iter; iter++) {
+ if (!strstr (*iter, ADDED_TAG)) {
+ g_string_append (new_contents, *iter);
+ g_string_append_c (new_contents, '\n');
}
}
+ g_strfreev (lines);
- /* Hmm, /etc/hosts was empty for some reason */
- if (!added) {
- g_string_append (contents, "# Do not remove the following line, or various programs\n");
- g_string_append (contents, "# that require network functionality will fail.\n");
- g_string_append_printf (contents, "127.0.0.1\t%s\tlocalhost\n", fallback_hostname);
- }
+ /* Remove last blank line at end of file, if one exists; this is
+ * an artifact of how g_strsplit_set() works.
+ */
+ if ( (new_contents->len > 2)
+ && (new_contents->str[new_contents->len - 1] == '\n'))
+ g_string_truncate (new_contents, new_contents->len - 1);
- return contents;
+ return new_contents;
}
-gboolean
-nm_policy_hosts_update_etc_hosts (const char *hostname,
- const char *fallback_hostname,
- gboolean *out_changed)
+/* remove any leftover "# Added by NetworkManager" lines */
+void
+nm_policy_hosts_clean_etc_hosts (void)
{
char *contents = NULL;
- char **lines = NULL;
- GError *error = NULL;
- GString *new_contents = NULL;
gsize contents_len = 0;
- gboolean success = FALSE;
-
- g_return_val_if_fail (hostname != NULL, FALSE);
- g_return_val_if_fail (out_changed != NULL, FALSE);
+ GError *error = NULL;
+ GString *new;
if (!g_file_get_contents (SYSCONFDIR "/hosts", &contents, &contents_len, &error)) {
nm_log_warn (LOGD_DNS, "couldn't read " SYSCONFDIR "/hosts: (%d) %s",
error ? error->code : 0,
(error && error->message) ? error->message : "(unknown)");
g_clear_error (&error);
- return FALSE;
+ return;
}
- /* Get the new /etc/hosts contents */
- lines = g_strsplit_set (contents, "\n\r", 0);
- new_contents = nm_policy_get_etc_hosts ((const char **) lines,
- contents_len,
- hostname,
- fallback_hostname,
- &error);
- g_strfreev (lines);
- g_free (contents);
-
- if (new_contents) {
- nm_log_info (LOGD_DNS, "Updating /etc/hosts with new system hostname");
+ new = nm_policy_get_etc_hosts (contents, contents_len);
+ if (new && new->len) {
+ nm_log_info (LOGD_DNS, "Cleaning leftovers from /etc/hosts");
g_clear_error (&error);
- /* And actually update /etc/hosts */
- if (!g_file_set_contents (SYSCONFDIR "/hosts", new_contents->str, -1, &error)) {
- nm_log_warn (LOGD_DNS, "couldn't update " SYSCONFDIR "/hosts: (%d) %s",
- error ? error->code : 0,
- (error && error->message) ? error->message : "(unknown)");
+ if (!g_file_set_contents (SYSCONFDIR "/hosts", new->str, -1, &error)) {
+ nm_log_dbg (LOGD_DNS, "couldn't update " SYSCONFDIR "/hosts: (%d) %s",
+ error ? error->code : 0,
+ (error && error->message) ? error->message : "(unknown)");
g_clear_error (&error);
- } else {
- success = TRUE;
- *out_changed = TRUE;
}
-
- g_string_free (new_contents, TRUE);
- } else if (!error) {
- /* No change required */
- success = TRUE;
- } else {
- nm_log_warn (LOGD_DNS, "couldn't read " SYSCONFDIR "/hosts: (%d) %s",
- error->code, error->message ? error->message : "(unknown)");
- g_clear_error (&error);
}
- return success;
+ if (new)
+ g_string_free (new, TRUE);
}
diff --git a/src/nm-policy-hosts.h b/src/nm-policy-hosts.h
index 0a77e6678a..9f4bf9a9a9 100644
--- a/src/nm-policy-hosts.h
+++ b/src/nm-policy-hosts.h
@@ -23,18 +23,10 @@
#include <glib.h>
-gboolean nm_policy_hosts_update_etc_hosts (const char *hostname,
- const char *fallback_hostname,
- gboolean *out_changed);
+void nm_policy_hosts_clean_etc_hosts (void);
/* Only for testcases; don't use outside of nm-policy-hosts.c */
-gboolean nm_policy_hosts_find_token (const char *line, const char *token);
-
-GString *nm_policy_get_etc_hosts (const char **lines,
- gsize existing_len,
- const char *hostname,
- const char *fallback_hostname,
- GError **error);
+GString *nm_policy_get_etc_hosts (const char *contents, gsize contents_len);
#endif /* NM_POLICY_HOSTS_H */
diff --git a/src/nm-policy.c b/src/nm-policy.c
index a7bd96f1bb..53557039e5 100644
--- a/src/nm-policy.c
+++ b/src/nm-policy.c
@@ -19,6 +19,7 @@
* Copyright (C) 2007 - 2008 Novell, Inc.
*/
+#include <config.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
@@ -39,9 +40,8 @@
#include "nm-setting-ip4-config.h"
#include "nm-setting-connection.h"
#include "nm-system.h"
-#include "nm-named-manager.h"
+#include "nm-dns-manager.h"
#include "nm-vpn-manager.h"
-#include "nm-policy-hosts.h"
#include "nm-policy-hostname.h"
struct NMPolicy {
@@ -61,9 +61,12 @@ struct NMPolicy {
HostnameThread *lookup;
char *orig_hostname; /* hostname at NM start time */
+ char *cur_hostname; /* hostname we want to assign */
};
#define INVALID_TAG "invalid"
+#define RETRIES_TAG "autoconnect-retries"
+#define RETRIES_DEFAULT 4
static const char *
get_connection_id (NMConnection *connection)
@@ -137,7 +140,8 @@ get_best_ip4_device (NMManager *manager, NMActRequest **out_req)
continue;
/* 'never-default' devices can't ever be the default */
- if (s_ip4 && nm_setting_ip4_config_get_never_default (s_ip4))
+ if ( (s_ip4 && nm_setting_ip4_config_get_never_default (s_ip4))
+ || nm_ip4_config_get_never_default (ip4_config))
continue;
prio = nm_device_get_priority (dev);
@@ -225,9 +229,23 @@ get_best_ip6_device (NMManager *manager, NMActRequest **out_req)
}
static void
-_set_hostname (const char *new_hostname, const char *msg)
+_set_hostname (NMPolicy *policy,
+ gboolean change_hostname,
+ const char *new_hostname,
+ const char *msg)
{
- if (nm_policy_set_system_hostname (new_hostname, msg))
+ if (change_hostname) {
+ NMDnsManager *dns_mgr;
+
+ g_free (policy->cur_hostname);
+ policy->cur_hostname = g_strdup (new_hostname);
+
+ dns_mgr = nm_dns_manager_get (NULL);
+ nm_dns_manager_set_hostname (dns_mgr, policy->cur_hostname);
+ g_object_unref (dns_mgr);
+ }
+
+ if (nm_policy_set_system_hostname (policy->cur_hostname, msg))
nm_utils_call_dispatcher ("hostname", NULL, NULL, NULL);
}
@@ -244,24 +262,23 @@ lookup_callback (HostnameThread *thread,
if (!hostname_thread_is_dead (thread) && (thread == policy->lookup)) {
policy->lookup = NULL;
if (!hostname) {
- /* No valid IP4 config (!!); fall back to localhost.localdomain */
+ /* Fall back to localhost.localdomain */
msg = g_strdup_printf ("address lookup failed: %d", result);
- _set_hostname (NULL, msg);
+ _set_hostname (policy, TRUE, NULL, msg);
g_free (msg);
} else
- _set_hostname (hostname, "from address lookup");
+ _set_hostname (policy, TRUE, hostname, "from address lookup");
}
hostname_thread_free (thread);
}
static void
-update_system_hostname (NMPolicy *policy, NMDevice *best)
+update_system_hostname (NMPolicy *policy, NMDevice *best4, NMDevice *best6)
{
char *configured_hostname = NULL;
- NMActRequest *best_req = NULL;
- NMDHCP4Config *dhcp4_config;
- NMIP4Config *ip4_config;
- NMIP4Address *addr;
+ NMActRequest *best_req4 = NULL;
+ NMActRequest *best_req6 = NULL;
+ const char *dhcp_hostname, *p;
g_return_if_fail (policy != NULL);
@@ -282,39 +299,62 @@ update_system_hostname (NMPolicy *policy, NMDevice *best)
/* Try a persistent hostname first */
g_object_get (G_OBJECT (policy->manager), NM_MANAGER_HOSTNAME, &configured_hostname, NULL);
if (configured_hostname) {
- _set_hostname (configured_hostname, "from system configuration");
+ _set_hostname (policy, TRUE, configured_hostname, "from system configuration");
g_free (configured_hostname);
return;
}
/* Try automatically determined hostname from the best device's IP config */
- if (!best)
- best = get_best_ip4_device (policy->manager, &best_req);
+ if (!best4)
+ best4 = get_best_ip4_device (policy->manager, &best_req4);
+ if (!best6)
+ best6 = get_best_ip6_device (policy->manager, &best_req6);
- if (!best) {
+ if (!best4 && !best6) {
/* No best device; fall back to original hostname or if there wasn't
* one, 'localhost.localdomain'
*/
- _set_hostname (policy->orig_hostname, "no default device");
+ _set_hostname (policy, TRUE, policy->orig_hostname, "no default device");
return;
}
- /* Grab a hostname out of the device's DHCP4 config */
- dhcp4_config = nm_device_get_dhcp4_config (best);
- if (dhcp4_config) {
- const char *dhcp4_hostname, *p;
-
- p = dhcp4_hostname = nm_dhcp4_config_get_option (dhcp4_config, "host_name");
- if (dhcp4_hostname && strlen (dhcp4_hostname)) {
- /* Sanity check */
- while (*p) {
- if (!isblank (*p++)) {
- _set_hostname (dhcp4_hostname, "from DHCP");
- return;
+ if (best4) {
+ NMDHCP4Config *dhcp4_config;
+
+ /* Grab a hostname out of the device's DHCP4 config */
+ dhcp4_config = nm_device_get_dhcp4_config (best4);
+ if (dhcp4_config) {
+ p = dhcp_hostname = nm_dhcp4_config_get_option (dhcp4_config, "host_name");
+ if (dhcp_hostname && strlen (dhcp_hostname)) {
+ /* Sanity check; strip leading spaces */
+ while (*p) {
+ if (!isblank (*p++)) {
+ _set_hostname (policy, TRUE, dhcp_hostname, "from DHCPv4");
+ return;
+ }
}
+ nm_log_warn (LOGD_DNS, "DHCPv4-provided hostname '%s' looks invalid; ignoring it",
+ dhcp_hostname);
+ }
+ }
+ } else if (best6) {
+ NMDHCP6Config *dhcp6_config;
+
+ /* Grab a hostname out of the device's DHCP4 config */
+ dhcp6_config = nm_device_get_dhcp6_config (best6);
+ if (dhcp6_config) {
+ p = dhcp_hostname = nm_dhcp6_config_get_option (dhcp6_config, "host_name");
+ if (dhcp_hostname && strlen (dhcp_hostname)) {
+ /* Sanity check; strip leading spaces */
+ while (*p) {
+ if (!isblank (*p++)) {
+ _set_hostname (policy, TRUE, dhcp_hostname, "from DHCPv6");
+ return;
+ }
+ }
+ nm_log_warn (LOGD_DNS, "DHCPv6-provided hostname '%s' looks invalid; ignoring it",
+ dhcp_hostname);
}
- nm_log_warn (LOGD_DNS, "DHCP-provided hostname '%s' looks invalid; ignoring it",
- dhcp4_hostname);
}
}
@@ -322,40 +362,64 @@ update_system_hostname (NMPolicy *policy, NMDevice *best)
* when NM started up.
*/
if (policy->orig_hostname) {
- _set_hostname (policy->orig_hostname, "from system startup");
+ _set_hostname (policy, TRUE, policy->orig_hostname, "from system startup");
return;
}
- /* No configured hostname, no automatically determined hostname, and
- * no bootup hostname. Start reverse DNS of the current IP address.
+ /* No configured hostname, no automatically determined hostname, and no
+ * bootup hostname. Start reverse DNS of the current IPv4 or IPv6 address.
*/
- ip4_config = nm_device_get_ip4_config (best);
- if ( !ip4_config
- || (nm_ip4_config_get_num_nameservers (ip4_config) == 0)
- || (nm_ip4_config_get_num_addresses (ip4_config) == 0)) {
- /* No valid IP4 config (!!); fall back to localhost.localdomain */
- _set_hostname (NULL, "no IPv4 config");
- return;
- }
+ if (best4) {
+ NMIP4Config *ip4_config;
+ NMIP4Address *addr4;
- addr = nm_ip4_config_get_address (ip4_config, 0);
- g_assert (addr); /* checked for > 1 address above */
+ ip4_config = nm_device_get_ip4_config (best4);
+ if ( !ip4_config
+ || (nm_ip4_config_get_num_nameservers (ip4_config) == 0)
+ || (nm_ip4_config_get_num_addresses (ip4_config) == 0)) {
+ /* No valid IP4 config (!!); fall back to localhost.localdomain */
+ _set_hostname (policy, TRUE, NULL, "no IPv4 config");
+ return;
+ }
+
+ addr4 = nm_ip4_config_get_address (ip4_config, 0);
+ g_assert (addr4); /* checked for > 1 address above */
+
+ /* Start the hostname lookup thread */
+ policy->lookup = hostname4_thread_new (nm_ip4_address_get_address (addr4), lookup_callback, policy);
+ } else if (best6) {
+ NMIP6Config *ip6_config;
+ NMIP6Address *addr6;
+
+ ip6_config = nm_device_get_ip6_config (best6);
+ if ( !ip6_config
+ || (nm_ip6_config_get_num_nameservers (ip6_config) == 0)
+ || (nm_ip6_config_get_num_addresses (ip6_config) == 0)) {
+ /* No valid IP6 config (!!); fall back to localhost.localdomain */
+ _set_hostname (policy, TRUE, NULL, "no IPv6 config");
+ return;
+ }
+
+ addr6 = nm_ip6_config_get_address (ip6_config, 0);
+ g_assert (addr6); /* checked for > 1 address above */
+
+ /* Start the hostname lookup thread */
+ policy->lookup = hostname6_thread_new (nm_ip6_address_get_address (addr6), lookup_callback, policy);
+ }
- /* Start the hostname lookup thread */
- policy->lookup = hostname_thread_new (nm_ip4_address_get_address (addr), lookup_callback, policy);
if (!policy->lookup) {
/* Fall back to 'localhost.localdomain' */
- _set_hostname (NULL, "error starting hostname thread");
+ _set_hostname (policy, TRUE, NULL, "error starting hostname thread");
}
}
static void
update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update)
{
- NMNamedIPConfigType dns_type = NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE;
+ NMDnsIPConfigType dns_type = NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE;
NMDevice *best = NULL;
NMActRequest *best_req = NULL;
- NMNamedManager *named_mgr;
+ NMDnsManager *dns_mgr;
GSList *devices = NULL, *iter, *vpns;
NMIP4Config *ip4_config = NULL;
NMIP4Address *addr;
@@ -382,6 +446,13 @@ update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update)
/* If it's marked 'never-default', don't make it default */
vpn_connection = nm_vpn_connection_get_connection (candidate);
g_assert (vpn_connection);
+
+ /* Check the active IP4 config from the VPN service daemon */
+ ip4_config = nm_vpn_connection_get_ip4_config (candidate);
+ if (ip4_config && nm_ip4_config_get_never_default (ip4_config))
+ can_default = FALSE;
+
+ /* Check the user's preference from the NMConnection */
s_ip4 = (NMSettingIP4Config *) nm_connection_get_setting (vpn_connection, NM_TYPE_SETTING_IP4_CONFIG);
if (s_ip4 && nm_setting_ip4_config_get_never_default (s_ip4))
can_default = FALSE;
@@ -393,7 +464,6 @@ update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update)
ip_iface = nm_vpn_connection_get_ip_iface (candidate);
connection = nm_vpn_connection_get_connection (candidate);
- ip4_config = nm_vpn_connection_get_ip4_config (candidate);
addr = nm_ip4_config_get_address (ip4_config, 0);
parent = nm_vpn_connection_get_parent_device (candidate);
@@ -406,7 +476,7 @@ update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update)
nm_device_get_ip_iface (parent),
nm_ip4_config_get_mss (parent_ip4));
- dns_type = NM_NAMED_IP_CONFIG_TYPE_VPN;
+ dns_type = NM_DNS_IP_CONFIG_TYPE_VPN;
}
g_object_unref (candidate);
}
@@ -422,7 +492,7 @@ update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update)
nm_system_replace_default_ip4_route (ip_iface, nm_ip4_address_get_gateway (addr), nm_ip4_config_get_mss (ip4_config));
- dns_type = NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE;
+ dns_type = NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE;
}
if (!ip_iface || !ip4_config) {
@@ -446,9 +516,9 @@ update_ip4_routing_and_dns (NMPolicy *policy, gboolean force_update)
nm_act_request_set_default (req, FALSE);
}
- named_mgr = nm_named_manager_get ();
- nm_named_manager_add_ip4_config (named_mgr, ip_iface, ip4_config, dns_type);
- g_object_unref (named_mgr);
+ dns_mgr = nm_dns_manager_get (NULL);
+ nm_dns_manager_add_ip4_config (dns_mgr, ip_iface, ip4_config, dns_type);
+ g_object_unref (dns_mgr);
/* Now set new default active connection _after_ updating DNS info, so that
* if the connection is shared dnsmasq picks up the right stuff.
@@ -473,10 +543,10 @@ out:
static void
update_ip6_routing_and_dns (NMPolicy *policy, gboolean force_update)
{
- NMNamedIPConfigType dns_type = NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE;
+ NMDnsIPConfigType dns_type = NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE;
NMDevice *best = NULL;
NMActRequest *best_req = NULL;
- NMNamedManager *named_mgr;
+ NMDnsManager *dns_mgr;
GSList *devices = NULL, *iter;
#if NOT_YET
GSList *vpns;
@@ -531,7 +601,7 @@ update_ip6_routing_and_dns (NMPolicy *policy, gboolean force_update)
nm_device_get_ip_iface (parent),
nm_ip6_config_get_mss (parent_ip4));
- dns_type = NM_NAMED_IP_CONFIG_TYPE_VPN;
+ dns_type = NM_DNS_IP_CONFIG_TYPE_VPN;
}
g_object_unref (candidate);
}
@@ -548,7 +618,7 @@ update_ip6_routing_and_dns (NMPolicy *policy, gboolean force_update)
nm_system_replace_default_ip6_route (ip_iface, nm_ip6_address_get_gateway (addr));
- dns_type = NM_NAMED_IP_CONFIG_TYPE_BEST_DEVICE;
+ dns_type = NM_DNS_IP_CONFIG_TYPE_BEST_DEVICE;
}
if (!ip_iface || !ip6_config) {
@@ -572,9 +642,9 @@ update_ip6_routing_and_dns (NMPolicy *policy, gboolean force_update)
nm_act_request_set_default6 (req, FALSE);
}
- named_mgr = nm_named_manager_get ();
- nm_named_manager_add_ip6_config (named_mgr, ip_iface, ip6_config, dns_type);
- g_object_unref (named_mgr);
+ dns_mgr = nm_dns_manager_get (NULL);
+ nm_dns_manager_add_ip6_config (dns_mgr, ip_iface, ip6_config, dns_type);
+ g_object_unref (dns_mgr);
/* Now set new default active connection _after_ updating DNS info, so that
* if the connection is shared dnsmasq picks up the right stuff.
@@ -603,7 +673,19 @@ update_routing_and_dns (NMPolicy *policy, gboolean force_update)
update_ip6_routing_and_dns (policy, force_update);
/* Update the system hostname */
- update_system_hostname (policy, policy->default_device4);
+ update_system_hostname (policy, policy->default_device4, policy->default_device6);
+}
+
+static void
+set_connection_auto_retries (NMConnection *connection, guint retries)
+{
+ g_object_set_data (G_OBJECT (connection), RETRIES_TAG, GUINT_TO_POINTER (retries));
+}
+
+static guint32
+get_connection_auto_retries (NMConnection *connection)
+{
+ return GPOINTER_TO_UINT (g_object_get_data (G_OBJECT (connection), RETRIES_TAG));
}
typedef struct {
@@ -633,32 +715,40 @@ auto_activate_device (gpointer user_data)
/* System connections first, then user connections */
connections = nm_manager_get_connections (policy->manager, NM_CONNECTION_SCOPE_SYSTEM);
- connections = g_slist_concat (connections, nm_manager_get_connections (policy->manager, NM_CONNECTION_SCOPE_USER));
+ if (nm_manager_auto_user_connections_allowed (policy->manager))
+ connections = g_slist_concat (connections, nm_manager_get_connections (policy->manager, NM_CONNECTION_SCOPE_USER));
- /* Remove connections that are in the invalid list. */
+ /* Remove connections that have INVALID_TAG and shouldn't be retried any more. */
iter = connections;
while (iter) {
NMConnection *iter_connection = NM_CONNECTION (iter->data);
GSList *next = g_slist_next (iter);
if (g_object_get_data (G_OBJECT (iter_connection), INVALID_TAG)) {
- connections = g_slist_remove_link (connections, iter);
- g_object_unref (iter_connection);
- g_slist_free (iter);
+ guint retries = get_connection_auto_retries (iter_connection);
+
+ if (retries == 0) {
+ connections = g_slist_remove_link (connections, iter);
+ g_object_unref (iter_connection);
+ g_slist_free (iter);
+ } else if (retries > 0)
+ set_connection_auto_retries (iter_connection, retries - 1);
+ } else {
+ /* Set the initial # of retries for auto-connection */
+ set_connection_auto_retries (iter_connection, RETRIES_DEFAULT);
}
+
iter = next;
}
best_connection = nm_device_get_best_auto_connection (data->device, connections, &specific_object);
if (best_connection) {
GError *error = NULL;
- const char *device_path;
- device_path = nm_device_get_path (data->device);
if (!nm_manager_activate_connection (policy->manager,
best_connection,
specific_object,
- device_path,
+ nm_device_get_path (data->device),
FALSE,
&error)) {
NMSettingConnection *s_con;
@@ -712,19 +802,20 @@ global_state_changed (NMManager *manager, NMState state, gpointer user_data)
static void
hostname_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data)
{
- update_system_hostname ((NMPolicy *) user_data, NULL);
+ update_system_hostname ((NMPolicy *) user_data, NULL, NULL);
}
static void
sleeping_changed (NMManager *manager, GParamSpec *pspec, gpointer user_data)
{
- gboolean sleeping = FALSE;
+ gboolean sleeping = FALSE, enabled = FALSE;
GSList *connections, *iter;
g_object_get (G_OBJECT (manager), NM_MANAGER_SLEEPING, &sleeping, NULL);
+ g_object_get (G_OBJECT (manager), NM_MANAGER_NETWORKING_ENABLED, &enabled, NULL);
/* Clear the invalid flag on all connections so they'll get retried on wakeup */
- if (sleeping) {
+ if (sleeping || !enabled) {
connections = nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_SYSTEM);
connections = g_slist_concat (connections, nm_manager_get_connections (manager, NM_CONNECTION_SCOPE_USER));
for (iter = connections; iter; iter = g_slist_next (iter))
@@ -792,23 +883,11 @@ device_state_changed (NMDevice *device,
/* Mark the connection invalid if it failed during activation so that
* it doesn't get automatically chosen over and over and over again.
*/
- if (connection) {
- gboolean fail = FALSE;
-
- if (IS_ACTIVATING_STATE (old_state)) {
+ if (connection && IS_ACTIVATING_STATE (old_state)) {
+ g_object_set_data (G_OBJECT (connection), INVALID_TAG, GUINT_TO_POINTER (TRUE));
+ if (get_connection_auto_retries (connection) == 0)
nm_log_info (LOGD_DEVICE, "Marking connection '%s' invalid.", get_connection_id (connection));
- fail = TRUE;
- } else if ( (old_state == NM_DEVICE_STATE_ACTIVATED)
- && (reason == NM_DEVICE_STATE_REASON_IP_CONFIG_EXPIRED)) {
- nm_log_info (LOGD_DEVICE, "Marking connection '%s' invalid because IP configuration expired.",
- get_connection_id (connection));
- fail = TRUE;
- }
-
- if (fail) {
- g_object_set_data (G_OBJECT (connection), INVALID_TAG, GUINT_TO_POINTER (TRUE));
- nm_connection_clear_secrets (connection);
- }
+ nm_connection_clear_secrets (connection);
}
schedule_activate_check (policy, device, 3);
break;
@@ -817,6 +896,9 @@ device_state_changed (NMDevice *device,
/* Clear the invalid tag on the connection */
g_object_set_data (G_OBJECT (connection), INVALID_TAG, NULL);
+ /* Reset RETRIES_TAG to number from the setting */
+ set_connection_auto_retries (connection, RETRIES_DEFAULT);
+
/* And clear secrets so they will always be requested from the
* settings service when the next connection is made.
*/
@@ -1012,6 +1094,12 @@ connection_removed (NMManager *manager,
g_ptr_array_free (list, TRUE);
}
+static void
+manager_user_permissions_changed (NMManager *manager, NMPolicy *policy)
+{
+ schedule_activate_all (policy);
+}
+
NMPolicy *
nm_policy_new (NMManager *manager, NMVPNManager *vpn_manager)
{
@@ -1055,6 +1143,10 @@ nm_policy_new (NMManager *manager, NMVPNManager *vpn_manager)
G_CALLBACK (sleeping_changed), policy);
policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
+ id = g_signal_connect (manager, "notify::" NM_MANAGER_NETWORKING_ENABLED,
+ G_CALLBACK (sleeping_changed), policy);
+ policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
+
id = g_signal_connect (manager, "device-added",
G_CALLBACK (device_added), policy);
policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
@@ -1083,6 +1175,10 @@ nm_policy_new (NMManager *manager, NMVPNManager *vpn_manager)
G_CALLBACK (connection_removed), policy);
policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
+ id = g_signal_connect (manager, "user-permissions-changed",
+ G_CALLBACK (manager_user_permissions_changed), policy);
+ policy->signal_ids = g_slist_append (policy->signal_ids, (gpointer) id);
+
return policy;
}
@@ -1112,6 +1208,7 @@ nm_policy_destroy (NMPolicy *policy)
g_signal_handler_disconnect (policy->vpn_manager, policy->vpn_activated_id);
g_signal_handler_disconnect (policy->vpn_manager, policy->vpn_deactivated_id);
+ g_object_unref (policy->vpn_manager);
for (iter = policy->signal_ids; iter; iter = g_slist_next (iter))
g_signal_handler_disconnect (policy->manager, (gulong) iter->data);
@@ -1126,6 +1223,7 @@ nm_policy_destroy (NMPolicy *policy)
g_slist_free (policy->dev_signal_ids);
g_free (policy->orig_hostname);
+ g_free (policy->cur_hostname);
g_object_unref (policy->manager);
g_free (policy);
diff --git a/src/nm-system.c b/src/nm-system.c
index 35aa36a111..7921fec654 100644
--- a/src/nm-system.c
+++ b/src/nm-system.c
@@ -44,7 +44,6 @@
#include "nm-system.h"
#include "nm-device.h"
-#include "nm-named-manager.h"
#include "NetworkManagerUtils.h"
#include "nm-utils.h"
#include "nm-logging.h"
@@ -481,32 +480,34 @@ nm_system_apply_ip4_config (const char *iface,
return TRUE;
}
-static struct rtnl_route *
-nm_system_device_set_ip6_route (const char *iface,
- const struct in6_addr *ip6_dest,
- guint32 ip6_prefix,
- const struct in6_addr *ip6_gateway,
- guint32 metric,
- int mss)
+int
+nm_system_set_ip6_route (int ifindex,
+ const struct in6_addr *ip6_dest,
+ guint32 ip6_prefix,
+ const struct in6_addr *ip6_gateway,
+ guint32 metric,
+ int mss,
+ int protocol,
+ int table,
+ struct rtnl_route **out_route)
{
struct nl_handle *nlh;
struct rtnl_route *route;
struct nl_addr *dest_addr;
struct nl_addr *gw_addr = NULL;
- int err, iface_idx;
+ int err = 0;
- nlh = nm_netlink_get_default_handle ();
- g_return_val_if_fail (nlh != NULL, NULL);
+ g_return_val_if_fail (ifindex >= 0, -1);
- iface_idx = nm_netlink_iface_to_index (iface);
- g_return_val_if_fail (iface_idx >= 0, NULL);
+ nlh = nm_netlink_get_default_handle ();
+ g_return_val_if_fail (nlh != NULL, -1);
- route = create_route (iface_idx, mss);
- g_return_val_if_fail (route != NULL, NULL);
+ route = create_route (ifindex, mss);
+ g_return_val_if_fail (route != NULL, -1);
/* Destination */
dest_addr = nl_addr_build (AF_INET6, (struct in6_addr *) ip6_dest, sizeof (*ip6_dest));
- g_return_val_if_fail (dest_addr != NULL, NULL);
+ g_return_val_if_fail (dest_addr != NULL, -1);
nl_addr_set_prefixlen (dest_addr, (int) ip6_prefix);
rtnl_route_set_dst (route, dest_addr);
@@ -521,7 +522,7 @@ nm_system_device_set_ip6_route (const char *iface,
} else {
nm_log_warn (LOGD_DEVICE | LOGD_IP6, "Invalid gateway");
rtnl_route_put (route);
- return NULL;
+ return -1;
}
}
@@ -529,13 +530,19 @@ nm_system_device_set_ip6_route (const char *iface,
if (metric)
rtnl_route_set_prio (route, metric);
+ if (protocol)
+ rtnl_route_set_protocol (route, protocol);
+
+ if (table)
+ rtnl_route_set_table (route, table);
+
/* Add the route */
err = rtnl_route_add (nlh, route, 0);
if (err == -ESRCH && ip6_gateway) {
/* Gateway might be over a bridge; try adding a route to gateway first */
struct rtnl_route *route2;
- route2 = create_route (iface_idx, mss);
+ route2 = create_route (ifindex, mss);
if (route2) {
/* Add route to gateway over bridge */
rtnl_route_set_dst (route2, gw_addr);
@@ -553,15 +560,12 @@ nm_system_device_set_ip6_route (const char *iface,
if (gw_addr)
nl_addr_put (gw_addr);
- if (err) {
- nm_log_err (LOGD_DEVICE | LOGD_IP6,
- "(%s): failed to set IPv6 route: %s",
- iface, nl_geterror ());
+ if (out_route)
+ *out_route = route;
+ else
rtnl_route_put (route);
- route = NULL;
- }
- return route;
+ return err;
}
static gboolean
@@ -618,24 +622,33 @@ nm_system_apply_ip6_config (const char *iface,
}
if (flags & NM_IP6_COMPARE_FLAG_ROUTES) {
+ int ifindex = nm_netlink_iface_to_index (iface);
+
for (i = 0; i < nm_ip6_config_get_num_routes (config); i++) {
NMIP6Route *route = nm_ip6_config_get_route (config, i);
- struct rtnl_route *tmp;
+ int err;
/* Don't add the route if it doesn't have a gateway and the connection
* is never supposed to be the default connection.
*/
if ( nm_ip6_config_get_never_default (config)
- && IN6_IS_ADDR_UNSPECIFIED(nm_ip6_route_get_dest (route)))
+ && IN6_IS_ADDR_UNSPECIFIED (nm_ip6_route_get_dest (route)))
continue;
- tmp = nm_system_device_set_ip6_route (iface,
- nm_ip6_route_get_dest (route),
- nm_ip6_route_get_prefix (route),
- nm_ip6_route_get_next_hop (route),
- nm_ip6_route_get_metric (route),
- nm_ip6_config_get_mss (config));
- rtnl_route_put (tmp);
+ err = nm_system_set_ip6_route (ifindex,
+ nm_ip6_route_get_dest (route),
+ nm_ip6_route_get_prefix (route),
+ nm_ip6_route_get_next_hop (route),
+ nm_ip6_route_get_metric (route),
+ nm_ip6_config_get_mss (config),
+ RTPROT_UNSPEC,
+ RT_TABLE_UNSPEC,
+ NULL);
+ if (err) {
+ nm_log_err (LOGD_DEVICE | LOGD_IP6,
+ "(%s): failed to set IPv6 route: %s",
+ iface, nl_geterror ());
+ }
}
}
@@ -772,6 +785,48 @@ nm_system_device_set_mtu (const char *iface, guint32 mtu)
return success;
}
+gboolean
+nm_system_device_set_mac (const char *iface, const struct ether_addr *mac)
+{
+ struct rtnl_link *old;
+ struct rtnl_link *new;
+ gboolean success = FALSE;
+ struct nl_handle *nlh;
+ int iface_idx;
+ struct nl_addr *addr = NULL;
+
+ g_return_val_if_fail (iface != NULL, FALSE);
+ g_return_val_if_fail (mac != NULL, FALSE);
+
+ new = rtnl_link_alloc ();
+ if (!new)
+ return FALSE;
+
+ iface_idx = nm_netlink_iface_to_index (iface);
+ old = nm_netlink_index_to_rtnl_link (iface_idx);
+ if (old) {
+ addr = nl_addr_build (AF_LLC, (void *) mac, ETH_ALEN);
+ if (!addr) {
+ char *mac_str;
+ mac_str = g_strdup_printf ("%02X:%02X:%02X:%02X:%02X:%02X", mac->ether_addr_octet[0], mac->ether_addr_octet[1], mac->ether_addr_octet[2],
+ mac->ether_addr_octet[3], mac->ether_addr_octet[4], mac->ether_addr_octet[5]);
+ nm_log_err (LOGD_DEVICE, "(%s): could not allocate memory for MAC address (%s)", iface, mac_str);
+ g_free (mac_str);
+ return FALSE;
+ }
+ rtnl_link_set_addr (new, addr);
+ nlh = nm_netlink_get_default_handle ();
+ if (nlh) {
+ rtnl_link_change (nlh, old, new, 0);
+ success = TRUE;
+ }
+ rtnl_link_put (old);
+ }
+
+ rtnl_link_put (new);
+ return success;
+}
+
static struct rtnl_route *
add_ip4_route_to_gateway (const char *iface, guint32 gw, guint32 mss)
{
@@ -1183,6 +1238,45 @@ foreach_route (void (*callback)(struct nl_object *, gpointer),
nl_cache_free (route_cache);
}
+static void
+dump_route (struct rtnl_route *route)
+{
+ char buf6[INET6_ADDRSTRLEN];
+ char buf4[INET_ADDRSTRLEN];
+ struct nl_addr *nl;
+ struct in6_addr *addr6 = NULL;
+ struct in_addr *addr4 = NULL;
+ int prefixlen = 0;
+ const char *sf = "UNSPEC";
+ int family = rtnl_route_get_family (route);
+
+ memset (buf6, 0, sizeof (buf6));
+ memset (buf4, 0, sizeof (buf4));
+ nl = rtnl_route_get_dst (route);
+ if (nl) {
+ if (nl_addr_get_family (nl) == AF_INET) {
+ addr4 = nl_addr_get_binary_addr (nl);
+ if (addr4)
+ inet_ntop (AF_INET, addr4, &buf4[0], sizeof (buf4));
+ } else if (nl_addr_get_family (nl) == AF_INET6) {
+ addr6 = nl_addr_get_binary_addr (nl);
+ if (addr6)
+ inet_ntop (AF_INET6, addr6, &buf6[0], sizeof (buf6));
+ }
+ prefixlen = nl_addr_get_prefixlen (nl);
+ }
+
+ if (family == AF_INET)
+ sf = "INET";
+ else if (family == AF_INET6)
+ sf = "INET6";
+
+ nm_log_dbg (LOGD_IP4 | LOGD_IP6, " route idx %d family %s (%d) addr %s/%d",
+ rtnl_route_get_oif (route),
+ sf, family,
+ strlen (buf4) ? buf4 : (strlen (buf6) ? buf6 : "<unknown>"),
+ prefixlen);
+}
typedef struct {
const char *iface;
@@ -1196,6 +1290,10 @@ check_one_route (struct nl_object *object, void *user_data)
RouteCheckData *data = (RouteCheckData *) user_data;
struct rtnl_route *route = (struct rtnl_route *) object;
int err;
+ guint32 log_level = LOGD_IP4 | LOGD_IP6;
+
+ if (nm_logging_level_enabled (LOGL_DEBUG))
+ dump_route (route);
/* Delete all routes from this interface */
if (rtnl_route_get_oif (route) != data->iface_idx)
@@ -1217,22 +1315,32 @@ check_one_route (struct nl_object *object, void *user_data)
addr = nl_addr_get_binary_addr (nl);
if (addr) {
- if (IN6_IS_ADDR_LINKLOCAL (addr) || IN6_IS_ADDR_MC_LINKLOCAL (addr))
+ if ( IN6_IS_ADDR_LINKLOCAL (addr)
+ || IN6_IS_ADDR_MC_LINKLOCAL (addr)
+ || (IN6_IS_ADDR_MULTICAST (addr) && (nl_addr_get_prefixlen (nl) == 8)))
return;
}
}
+ if (data->family == AF_INET)
+ log_level = LOGD_IP4;
+ else if (data->family == AF_INET6)
+ log_level = LOGD_IP6;
+ nm_log_dbg (log_level, " deleting route");
+
err = rtnl_route_del (nm_netlink_get_default_handle (), route, 0);
- if (err < 0) {
+ if (err < 0 && (err != -ERANGE)) {
nm_log_err (LOGD_DEVICE,
"(%s): error %d returned from rtnl_route_del(): %s",
- data->iface, err, nl_geterror());
+ data->iface, err, nl_geterror ());
}
}
static void flush_routes (int ifindex, const char *iface, int family)
{
RouteCheckData check_data;
+ guint32 log_level = LOGD_IP4 | LOGD_IP6;
+ const char *sf = "UNSPEC";
g_return_if_fail (iface != NULL);
@@ -1244,6 +1352,16 @@ static void flush_routes (int ifindex, const char *iface, int family)
}
}
+ if (family == AF_INET) {
+ log_level = LOGD_IP4;
+ sf = "INET";
+ } else if (family == AF_INET6) {
+ log_level = LOGD_IP6;
+ sf = "INET6";
+ }
+ nm_log_dbg (log_level, "(%s): flushing routes ifindex %d family %s (%d)",
+ iface, ifindex, sf, family);
+
memset (&check_data, 0, sizeof (check_data));
check_data.iface = iface;
check_data.iface_idx = ifindex;
diff --git a/src/nm-system.h b/src/nm-system.h
index 2eee01417d..8cb22d747a 100644
--- a/src/nm-system.h
+++ b/src/nm-system.h
@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2004 - 2008 Red Hat, Inc.
+ * Copyright (C) 2004 - 2010 Red Hat, Inc.
* Copyright (C) 2005 - 2008 Novell, Inc.
*/
@@ -25,6 +25,8 @@
#include <netlink/route/rtnl.h>
#include <netlink/route/route.h>
+#include <net/ethernet.h>
+
#include <glib.h>
#include "nm-device.h"
#include "nm-ip4-config.h"
@@ -64,6 +66,16 @@ gboolean nm_system_apply_ip4_config (const char *iface,
int priority,
NMIP4ConfigCompareFlags flags);
+int nm_system_set_ip6_route (int ifindex,
+ const struct in6_addr *ip6_dest,
+ guint32 ip6_prefix,
+ const struct in6_addr *ip6_gateway,
+ guint32 metric,
+ int mss,
+ int protocol,
+ int table,
+ struct rtnl_route **out_route);
+
gboolean nm_system_apply_ip6_config (const char *iface,
NMIP6Config *config,
int priority,
@@ -80,5 +92,6 @@ gboolean nm_system_device_is_up (NMDevice *device);
gboolean nm_system_device_is_up_with_iface (const char *iface);
gboolean nm_system_device_set_mtu (const char *iface, guint32 mtu);
+gboolean nm_system_device_set_mac (const char *iface, const struct ether_addr *mac);
#endif
diff --git a/src/nm-wifi-ap.c b/src/nm-wifi-ap.c
index c7b5d8a51a..4438118729 100644
--- a/src/nm-wifi-ap.c
+++ b/src/nm-wifi-ap.c
@@ -15,13 +15,14 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2004 - 2008 Red Hat, Inc.
+ * Copyright (C) 2004 - 2010 Red Hat, Inc.
* Copyright (C) 2006 - 2008 Novell, Inc.
*/
#include "wireless-helper.h"
#include <string.h>
+#include <stdlib.h>
#include "nm-wifi-ap.h"
#include "NetworkManagerUtils.h"
@@ -153,6 +154,8 @@ set_property (GObject *object, guint prop_id,
case PROP_STRENGTH:
nm_ap_set_strength (ap, g_value_get_char (value));
break;
+ case PROP_HW_ADDRESS:
+ break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
@@ -242,7 +245,7 @@ nm_ap_class_init (NMAccessPointClass *ap_class)
NM_802_11_AP_FLAGS_NONE,
NM_802_11_AP_FLAGS_PRIVACY,
NM_802_11_AP_FLAGS_NONE,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class, PROP_WPA_FLAGS,
@@ -252,7 +255,7 @@ nm_ap_class_init (NMAccessPointClass *ap_class)
NM_802_11_AP_SEC_NONE,
all_sec_flags,
NM_802_11_AP_SEC_NONE,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class, PROP_RSN_FLAGS,
@@ -262,7 +265,7 @@ nm_ap_class_init (NMAccessPointClass *ap_class)
NM_802_11_AP_SEC_NONE,
all_sec_flags,
NM_802_11_AP_SEC_NONE,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class, PROP_SSID,
@@ -270,7 +273,7 @@ nm_ap_class_init (NMAccessPointClass *ap_class)
"SSID",
"SSID",
DBUS_TYPE_G_UCHAR_ARRAY,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class, PROP_FREQUENCY,
@@ -278,7 +281,7 @@ nm_ap_class_init (NMAccessPointClass *ap_class)
"Frequency",
"Frequency",
0, 10000, 0,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class, PROP_HW_ADDRESS,
@@ -286,7 +289,7 @@ nm_ap_class_init (NMAccessPointClass *ap_class)
"MAC Address",
"Hardware MAC address",
NULL,
- G_PARAM_READABLE));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class, PROP_MODE,
@@ -294,7 +297,7 @@ nm_ap_class_init (NMAccessPointClass *ap_class)
"Mode",
"Mode",
NM_802_11_MODE_ADHOC, NM_802_11_MODE_INFRA, NM_802_11_MODE_INFRA,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class, PROP_MAX_BITRATE,
@@ -302,7 +305,7 @@ nm_ap_class_init (NMAccessPointClass *ap_class)
"Max Bitrate",
"Max Bitrate",
0, G_MAXUINT16, 0,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
g_object_class_install_property
(object_class, PROP_STRENGTH,
@@ -310,7 +313,7 @@ nm_ap_class_init (NMAccessPointClass *ap_class)
"Strength",
"Strength",
G_MININT8, G_MAXINT8, 0,
- G_PARAM_READWRITE));
+ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
/* Signals */
signals[PROPERTIES_CHANGED] =
@@ -367,10 +370,69 @@ NMAccessPoint *nm_ap_new (void)
return (NMAccessPoint *) object;
}
+static guint32
+pair_to_flags (const char *str)
+{
+ g_return_val_if_fail (str != NULL, NM_802_11_AP_SEC_NONE);
+
+ if (strcmp (str, "wep40") == 0)
+ return NM_802_11_AP_SEC_PAIR_WEP40;
+ if (strcmp (str, "wep104") == 0)
+ return NM_802_11_AP_SEC_PAIR_WEP104;
+ if (strcmp (str, "tkip") == 0)
+ return NM_802_11_AP_SEC_PAIR_TKIP;
+ if (strcmp (str, "ccmp") == 0)
+ return NM_802_11_AP_SEC_PAIR_CCMP;
+ return NM_802_11_AP_SEC_NONE;
+}
-#define IEEE80211_CAP_ESS 0x0001
-#define IEEE80211_CAP_IBSS 0x0002
-#define IEEE80211_CAP_PRIVACY 0x0010
+static guint32
+group_to_flags (const char *str)
+{
+ g_return_val_if_fail (str != NULL, NM_802_11_AP_SEC_NONE);
+
+ if (strcmp (str, "wep40") == 0)
+ return NM_802_11_AP_SEC_GROUP_WEP40;
+ if (strcmp (str, "wep104") == 0)
+ return NM_802_11_AP_SEC_GROUP_WEP104;
+ if (strcmp (str, "tkip") == 0)
+ return NM_802_11_AP_SEC_GROUP_TKIP;
+ if (strcmp (str, "ccmp") == 0)
+ return NM_802_11_AP_SEC_GROUP_CCMP;
+ return NM_802_11_AP_SEC_NONE;
+}
+
+static guint32
+security_from_dict (GHashTable *security)
+{
+ GValue *value;
+ guint32 flags = NM_802_11_AP_SEC_NONE;
+ const char **items, **iter;
+
+ value = g_hash_table_lookup (security, "KeyMgmt");
+ if (value) {
+ items = g_value_get_boxed (value);
+ for (iter = items; iter && *iter; iter++) {
+ if (strcmp (*iter, "wpa-psk") == 0)
+ flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK;
+ else if (strcmp (*iter, "wpa-eap") == 0)
+ flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X;
+ }
+ }
+
+ value = g_hash_table_lookup (security, "Pairwise");
+ if (value) {
+ items = g_value_get_boxed (value);
+ for (iter = items; iter && *iter; iter++)
+ flags |= pair_to_flags (*iter);
+ }
+
+ value = g_hash_table_lookup (security, "Group");
+ if (value)
+ flags |= group_to_flags (g_value_get_string (value));
+
+ return flags;
+}
static void
foreach_property_cb (gpointer key, gpointer value, gpointer user_data)
@@ -381,9 +443,9 @@ foreach_property_cb (gpointer key, gpointer value, gpointer user_data)
if (G_VALUE_HOLDS_BOXED (variant)) {
GArray *array = g_value_get_boxed (variant);
- if (!strcmp (key, "ssid")) {
+ if (!strcmp (key, "SSID")) {
guint32 len = MIN (IW_ESSID_MAX_SIZE, array->len);
- GByteArray * ssid;
+ GByteArray *ssid;
/* Stupid ieee80211 layer uses <hidden> */
if (((len == 8) || (len == 9))
@@ -397,7 +459,7 @@ foreach_property_cb (gpointer key, gpointer value, gpointer user_data)
g_byte_array_append (ssid, (const guint8 *) array->data, len);
nm_ap_set_ssid (ap, ssid);
g_byte_array_free (ssid, TRUE);
- } else if (!strcmp (key, "bssid")) {
+ } else if (!strcmp (key, "BSSID")) {
struct ether_addr addr;
if (array->len != ETH_ALEN)
@@ -405,43 +467,62 @@ foreach_property_cb (gpointer key, gpointer value, gpointer user_data)
memset (&addr, 0, sizeof (struct ether_addr));
memcpy (&addr, array->data, ETH_ALEN);
nm_ap_set_address (ap, &addr);
- } else if (!strcmp (key, "wpaie")) {
- guint8 * ie = (guint8 *) array->data;
+ } else if (!strcmp (key, "Rates")) {
+ guint32 maxrate = 0;
+ int i;
+
+ /* Find the max AP rate */
+ for (i = 0; i < array->len; i++) {
+ guint32 r = g_array_index (array, guint32, i);
+
+ if (r > maxrate) {
+ maxrate = r;
+ nm_ap_set_max_bitrate (ap, r / 1000);
+ }
+ }
+ } else if (!strcmp (key, "WPA")) {
guint32 flags = nm_ap_get_wpa_flags (ap);
- if (array->len <= 0 || array->len > WPA_MAX_IE_LEN)
- return;
- flags = nm_ap_add_security_from_ie (flags, ie, array->len);
+ flags |= security_from_dict (g_value_get_boxed (variant));
nm_ap_set_wpa_flags (ap, flags);
- } else if (!strcmp (key, "rsnie")) {
- guint8 * ie = (guint8 *) array->data;
+ } else if (!strcmp (key, "RSN")) {
guint32 flags = nm_ap_get_rsn_flags (ap);
- if (array->len <= 0 || array->len > WPA_MAX_IE_LEN)
- return;
- flags = nm_ap_add_security_from_ie (flags, ie, array->len);
+ flags |= security_from_dict (g_value_get_boxed (variant));
nm_ap_set_rsn_flags (ap, flags);
}
+ } else if (G_VALUE_HOLDS_UINT (variant)) {
+ guint32 val = g_value_get_uint (variant);
+
+ if (!strcmp (key, "Frequency"))
+ nm_ap_set_freq (ap, val);
} else if (G_VALUE_HOLDS_INT (variant)) {
- gint32 int_val = g_value_get_int (variant);
+ gint val = g_value_get_int (variant);
+
+ if (!strcmp (key, "Signal")) {
+ if (val < 0) {
+ /* Rough conversion: best = -40, worst = -100 */
+ val = abs (CLAMP (val, -100, -40) + 40);
+ val = 100 - (int) ((100.0 * (double) val) / 60.0);
+ } else
+ val /= 100;
- if (!strcmp (key, "frequency")) {
- nm_ap_set_freq (ap, (guint32) int_val);
- } else if (!strcmp (key, "maxrate")) {
- /* Supplicant reports as b/s, we use Kb/s internally */
- nm_ap_set_max_bitrate (ap, int_val / 1000);
+ nm_ap_set_strength (ap, val);
}
- } else if (G_VALUE_HOLDS_UINT (variant)) {
- guint32 val = g_value_get_uint (variant);
+ } else if (G_VALUE_HOLDS_STRING (variant)) {
+ const char *val = g_value_get_string (variant);
- if (!strcmp (key, "capabilities")) {
- if (val & IEEE80211_CAP_ESS) {
+ if (val && !strcmp (key, "Mode")) {
+ if (strcmp (val, "infrastructure") == 0)
nm_ap_set_mode (ap, NM_802_11_MODE_INFRA);
- } else if (val & IEEE80211_CAP_IBSS) {
+ else if (strcmp (val, "ad-hoc") == 0)
nm_ap_set_mode (ap, NM_802_11_MODE_ADHOC);
- }
+ }
+ } else if (G_VALUE_HOLDS_BOOLEAN (variant)) {
+ gboolean val = g_value_get_boolean (variant);
- if (val & IEEE80211_CAP_PRIVACY) {
+ if (strcmp (key, "Privacy") == 0) {
+ if (val) {
guint32 flags = nm_ap_get_flags (ap);
nm_ap_set_flags (ap, flags | NM_802_11_AP_FLAGS_PRIVACY);
}
@@ -449,7 +530,6 @@ foreach_property_cb (gpointer key, gpointer value, gpointer user_data)
}
}
-
NMAccessPoint *
nm_ap_new_from_properties (GHashTable *properties)
{
@@ -604,7 +684,7 @@ nm_ap_new_fake_from_connection (NMConnection *connection)
channel = nm_setting_wireless_get_channel (s_wireless);
if (band && channel) {
- guint32 freq = channel_to_freq (channel, band);
+ guint32 freq = nm_utils_wifi_channel_to_freq (channel, band);
if (freq == 0)
goto error;
@@ -1168,45 +1248,6 @@ void nm_ap_set_user_addresses (NMAccessPoint *ap, GSList *list)
}
-guint32
-nm_ap_add_security_from_ie (guint32 flags,
- const guint8 *wpa_ie,
- guint32 length)
-{
- wpa_ie_data * cap_data;
-
- if (!(cap_data = wpa_parse_wpa_ie (wpa_ie, length)))
- return NM_802_11_AP_SEC_NONE;
-
- /* Pairwise cipher flags */
- if (cap_data->pairwise_cipher & IW_AUTH_CIPHER_WEP40)
- flags |= NM_802_11_AP_SEC_PAIR_WEP40;
- if (cap_data->pairwise_cipher & IW_AUTH_CIPHER_WEP104)
- flags |= NM_802_11_AP_SEC_PAIR_WEP104;
- if (cap_data->pairwise_cipher & IW_AUTH_CIPHER_TKIP)
- flags |= NM_802_11_AP_SEC_PAIR_TKIP;
- if (cap_data->pairwise_cipher & IW_AUTH_CIPHER_CCMP)
- flags |= NM_802_11_AP_SEC_PAIR_CCMP;
-
- /* Group cipher flags */
- if (cap_data->group_cipher & IW_AUTH_CIPHER_WEP40)
- flags |= NM_802_11_AP_SEC_GROUP_WEP40;
- if (cap_data->group_cipher & IW_AUTH_CIPHER_WEP104)
- flags |= NM_802_11_AP_SEC_GROUP_WEP104;
- if (cap_data->group_cipher & IW_AUTH_CIPHER_TKIP)
- flags |= NM_802_11_AP_SEC_GROUP_TKIP;
- if (cap_data->group_cipher & IW_AUTH_CIPHER_CCMP)
- flags |= NM_802_11_AP_SEC_GROUP_CCMP;
-
- if (cap_data->key_mgmt & IW_AUTH_KEY_MGMT_802_1X)
- flags |= NM_802_11_AP_SEC_KEY_MGMT_802_1X;
- if (cap_data->key_mgmt & IW_AUTH_KEY_MGMT_PSK)
- flags |= NM_802_11_AP_SEC_KEY_MGMT_PSK;
-
- g_slice_free (wpa_ie_data, cap_data);
- return flags;
-}
-
gboolean
nm_ap_check_compatible (NMAccessPoint *self,
NMConnection *connection)
@@ -1256,21 +1297,21 @@ nm_ap_check_compatible (NMAccessPoint *self,
channel = nm_setting_wireless_get_channel (s_wireless);
if (channel) {
- guint32 ap_chan = freq_to_channel (priv->freq);
+ guint32 ap_chan = nm_utils_wifi_freq_to_channel (priv->freq);
if (channel != ap_chan)
return FALSE;
}
s_wireless_sec = (NMSettingWirelessSecurity *) nm_connection_get_setting (connection,
- NM_TYPE_SETTING_WIRELESS_SECURITY);
+ NM_TYPE_SETTING_WIRELESS_SECURITY);
return nm_setting_wireless_ap_security_compatible (s_wireless,
- s_wireless_sec,
- nm_ap_get_flags (self),
- nm_ap_get_wpa_flags (self),
- nm_ap_get_rsn_flags (self),
- nm_ap_get_mode (self));
+ s_wireless_sec,
+ nm_ap_get_flags (self),
+ nm_ap_get_wpa_flags (self),
+ nm_ap_get_rsn_flags (self),
+ nm_ap_get_mode (self));
}
static gboolean
@@ -1364,114 +1405,3 @@ nm_ap_match_in_list (NMAccessPoint *find_ap,
return NULL;
}
-
-struct cf_pair {
- guint32 chan;
- guint32 freq;
-};
-
-static struct cf_pair a_table[] = {
- /* A band */
- { 7, 5035 },
- { 8, 5040 },
- { 9, 5045 },
- { 11, 5055 },
- { 12, 5060 },
- { 16, 5080 },
- { 34, 5170 },
- { 36, 5180 },
- { 38, 5190 },
- { 40, 5200 },
- { 42, 5210 },
- { 44, 5220 },
- { 46, 5230 },
- { 48, 5240 },
- { 50, 5250 },
- { 52, 5260 },
- { 56, 5280 },
- { 58, 5290 },
- { 60, 5300 },
- { 64, 5320 },
- { 100, 5500 },
- { 104, 5520 },
- { 108, 5540 },
- { 112, 5560 },
- { 116, 5580 },
- { 120, 5600 },
- { 124, 5620 },
- { 128, 5640 },
- { 132, 5660 },
- { 136, 5680 },
- { 140, 5700 },
- { 149, 5745 },
- { 152, 5760 },
- { 153, 5765 },
- { 157, 5785 },
- { 160, 5800 },
- { 161, 5805 },
- { 165, 5825 },
- { 183, 4915 },
- { 184, 4920 },
- { 185, 4925 },
- { 187, 4935 },
- { 188, 4945 },
- { 192, 4960 },
- { 196, 4980 },
- { 0, -1 }
-};
-
-static struct cf_pair bg_table[] = {
- /* B/G band */
- { 1, 2412 },
- { 2, 2417 },
- { 3, 2422 },
- { 4, 2427 },
- { 5, 2432 },
- { 6, 2437 },
- { 7, 2442 },
- { 8, 2447 },
- { 9, 2452 },
- { 10, 2457 },
- { 11, 2462 },
- { 12, 2467 },
- { 13, 2472 },
- { 14, 2484 },
- { 0, -1 }
-};
-
-guint32
-freq_to_channel (guint32 freq)
-{
- int i = 0;
-
- if (freq > 4900) {
- while (a_table[i].chan && (a_table[i].freq != freq))
- i++;
- return a_table[i].chan;
- } else {
- while (bg_table[i].chan && (bg_table[i].freq != freq))
- i++;
- return bg_table[i].chan;
- }
-
- return 0;
-}
-
-guint32
-channel_to_freq (guint32 channel, const char *band)
-{
- int i = 0;
-
- if (!strcmp (band, "a")) {
- while (a_table[i].chan && (a_table[i].chan != channel))
- i++;
- return a_table[i].freq;
- } else if (!strcmp (band, "bg")) {
- while (bg_table[i].chan && (bg_table[i].chan != channel))
- i++;
- return bg_table[i].freq;
- }
-
- return 0;
-}
-
diff --git a/src/nm-wifi-ap.h b/src/nm-wifi-ap.h
index edc9e56bfb..09c87d970a 100644
--- a/src/nm-wifi-ap.h
+++ b/src/nm-wifi-ap.h
@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2004 - 2008 Red Hat, Inc.
+ * Copyright (C) 2004 - 2010 Red Hat, Inc.
* Copyright (C) 2006 - 2008 Novell, Inc.
*/
@@ -110,10 +110,6 @@ void nm_ap_set_user_created (NMAccessPoint *ap, gboolean user_created);
GSList * nm_ap_get_user_addresses (const NMAccessPoint *ap);
void nm_ap_set_user_addresses (NMAccessPoint *ap, GSList *list);
-guint32 nm_ap_add_security_from_ie (guint32 flags,
- const guint8 *wpa_ie,
- guint32 length);
-
gboolean nm_ap_check_compatible (NMAccessPoint *self,
NMConnection *connection);
@@ -123,7 +119,4 @@ NMAccessPoint * nm_ap_match_in_list (NMAccessPoint *find_ap,
void nm_ap_print_self (NMAccessPoint *ap, const char * prefix);
-guint32 freq_to_channel (guint32 freq);
-guint32 channel_to_freq (guint32 channel, const char *band);
-
#endif /* NM_ACCESS_POINT_H */
diff --git a/src/ppp-manager/Makefile.am b/src/ppp-manager/Makefile.am
index 2dc7ad94c6..f213dd2ecf 100644
--- a/src/ppp-manager/Makefile.am
+++ b/src/ppp-manager/Makefile.am
@@ -14,7 +14,7 @@ libppp_manager_la_SOURCES = \
nm-ppp-status.h
nm-ppp-manager-glue.h: $(top_srcdir)/introspection/nm-ppp-manager.xml
- dbus-binding-tool --prefix=nm_ppp_manager --mode=glib-server --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_ppp_manager --mode=glib-server --output=$@ $<
built_sources = nm-ppp-manager-glue.h
diff --git a/src/ppp-manager/nm-ppp-manager.c b/src/ppp-manager/nm-ppp-manager.c
index de905b49db..efa660b03f 100644
--- a/src/ppp-manager/nm-ppp-manager.c
+++ b/src/ppp-manager/nm-ppp-manager.c
@@ -16,9 +16,10 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Copyright (C) 2008 Novell, Inc.
- * Copyright (C) 2008 Red Hat, Inc.
+ * Copyright (C) 2008 - 2010 Red Hat, Inc.
*/
+#include <config.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <signal.h>
@@ -883,7 +884,8 @@ nm_ppp_manager_start (NMPPPManager *manager,
{
NMPPPManagerPrivate *priv;
NMConnection *connection;
- NMSettingPPP *ppp_setting;
+ NMSettingPPP *s_ppp;
+ gboolean s_ppp_created = FALSE;
NMSettingPPPOE *pppoe_setting;
NMCmdLine *ppp_cmd;
char *cmd_str;
@@ -893,26 +895,36 @@ nm_ppp_manager_start (NMPPPManager *manager,
g_return_val_if_fail (NM_IS_PPP_MANAGER (manager), FALSE);
g_return_val_if_fail (NM_IS_ACT_REQUEST (req), FALSE);
+ priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
+
+ priv->pid = 0;
+
/* Make sure /dev/ppp exists (bgo #533064) */
if (stat ("/dev/ppp", &st) || !S_ISCHR (st.st_mode))
ignored = system ("/sbin/modprobe ppp_generic");
connection = nm_act_request_get_connection (req);
- ppp_setting = NM_SETTING_PPP (nm_connection_get_setting (connection, NM_TYPE_SETTING_PPP));
- g_return_val_if_fail (ppp_setting != NULL, FALSE);
+ g_assert (connection);
+
+ s_ppp = (NMSettingPPP *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPP);
+ if (!s_ppp) {
+ /* If the PPP settings are all default we may not have a PPP setting yet,
+ * so just make a default one here.
+ */
+ s_ppp = NM_SETTING_PPP (nm_setting_ppp_new ());
+ s_ppp_created = TRUE;
+ }
pppoe_setting = (NMSettingPPPOE *) nm_connection_get_setting (connection, NM_TYPE_SETTING_PPPOE);
if (pppoe_setting)
- pppoe_fill_defaults (ppp_setting);
+ pppoe_fill_defaults (s_ppp);
- ppp_cmd = create_pppd_cmd_line (manager, ppp_setting, pppoe_setting, ppp_name, err);
+ ppp_cmd = create_pppd_cmd_line (manager, s_ppp, pppoe_setting, ppp_name, err);
if (!ppp_cmd)
- return FALSE;
+ goto out;
g_ptr_array_add (ppp_cmd->array, NULL);
- priv = NM_PPP_MANAGER_GET_PRIVATE (manager);
-
nm_log_info (LOGD_PPP, "starting PPP connection");
cmd_str = nm_cmd_line_to_str (ppp_cmd);
@@ -934,6 +946,9 @@ nm_ppp_manager_start (NMPPPManager *manager,
priv->act_req = g_object_ref (req);
out:
+ if (s_ppp_created)
+ g_object_unref (s_ppp);
+
if (ppp_cmd)
nm_cmd_line_destroy (ppp_cmd);
diff --git a/src/supplicant-manager/Makefile.am b/src/supplicant-manager/Makefile.am
index e59ae84319..359fc70ae5 100644
--- a/src/supplicant-manager/Makefile.am
+++ b/src/supplicant-manager/Makefile.am
@@ -5,8 +5,7 @@ INCLUDES = \
-I${top_srcdir}/src/logging \
-I${top_srcdir}/include \
-I${top_srcdir}/libnm-util \
- -I${top_builddir}/marshallers \
- -I${top_srcdir}/src/named-manager
+ -I${top_builddir}/marshallers
noinst_LTLIBRARIES = libsupplicant-manager.la
diff --git a/src/supplicant-manager/nm-supplicant-config.c b/src/supplicant-manager/nm-supplicant-config.c
index b516de520b..e33f67abf5 100644
--- a/src/supplicant-manager/nm-supplicant-config.c
+++ b/src/supplicant-manager/nm-supplicant-config.c
@@ -711,6 +711,8 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
gboolean success, added;
GString *phase1, *phase2;
const GByteArray *array;
+ gboolean peap = FALSE;
+ guint32 i, num_eap;
g_return_val_if_fail (NM_IS_SUPPLICANT_CONFIG (self), FALSE);
g_return_val_if_fail (setting != NULL, FALSE);
@@ -733,6 +735,28 @@ nm_supplicant_config_add_setting_8021x (NMSupplicantConfig *self,
ADD_STRING_LIST_VAL (setting, 802_1x, eap_method, eap_methods, "eap", TRUE, FALSE);
+ /* Check for PEAP + GTC */
+ num_eap = nm_setting_802_1x_get_num_eap_methods (setting);
+ for (i = 0; i < num_eap; i++) {
+ const char *method = nm_setting_802_1x_get_eap_method (setting, i);
+
+ if (method && (strcasecmp (method, "peap") == 0)) {
+ peap = TRUE;
+ break;
+ }
+ }
+
+ /* When using PEAP-GTC, we're likely using Cisco kit, so we want to turn
+ * on PMKSA caching so that roaming between access points actually works
+ * without a full reauth (which requires a new token code). We may want
+ * to extend this to all PEAP phase2 methods at some point.
+ */
+ value = nm_setting_802_1x_get_phase2_auth (setting);
+ if (peap && value && (strcasecmp (value, "gtc") == 0)) {
+ if (!nm_supplicant_config_add_option (self, "proactive_key_caching", "1", -1, FALSE))
+ return FALSE;
+ }
+
/* Drop the fragment size a bit for better compatibility */
if (!nm_supplicant_config_add_option (self, "fragment_size", "1300", -1, FALSE))
return FALSE;
diff --git a/src/supplicant-manager/nm-supplicant-interface.c b/src/supplicant-manager/nm-supplicant-interface.c
index a65a458f5b..93807b9360 100644
--- a/src/supplicant-manager/nm-supplicant-interface.c
+++ b/src/supplicant-manager/nm-supplicant-interface.c
@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2006 - 2008 Red Hat, Inc.
+ * Copyright (C) 2006 - 2010 Red Hat, Inc.
* Copyright (C) 2006 - 2008 Novell, Inc.
*/
@@ -34,73 +34,48 @@
#include "nm-glib-compat.h"
#define WPAS_DBUS_IFACE_INTERFACE WPAS_DBUS_INTERFACE ".Interface"
-#define WPAS_DBUS_IFACE_BSSID WPAS_DBUS_INTERFACE ".BSSID"
+#define WPAS_DBUS_IFACE_BSS WPAS_DBUS_INTERFACE ".BSS"
#define WPAS_DBUS_IFACE_NETWORK WPAS_DBUS_INTERFACE ".Network"
#define WPAS_ERROR_INVALID_IFACE WPAS_DBUS_INTERFACE ".InvalidInterface"
-#define WPAS_ERROR_EXISTS_ERROR WPAS_DBUS_INTERFACE ".ExistsError"
-
+#define WPAS_ERROR_EXISTS_ERROR WPAS_DBUS_INTERFACE ".InterfaceExists"
G_DEFINE_TYPE (NMSupplicantInterface, nm_supplicant_interface, G_TYPE_OBJECT)
+static void wpas_iface_properties_changed (DBusGProxy *proxy,
+ GHashTable *props,
+ gpointer user_data);
+
+static void wpas_iface_scan_done (DBusGProxy *proxy,
+ gboolean success,
+ gpointer user_data);
#define NM_SUPPLICANT_INTERFACE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
NM_TYPE_SUPPLICANT_INTERFACE, \
NMSupplicantInterfacePrivate))
-static void nm_supplicant_interface_set_property (GObject * object,
- guint prop_id,
- const GValue * value,
- GParamSpec * pspec);
-
-static void nm_supplicant_interface_get_property (GObject * object,
- guint prop_id,
- GValue * value,
- GParamSpec * pspec);
-
-static void nm_supplicant_interface_start (NMSupplicantInterface *self);
-
-static void nm_supplicant_interface_add_to_supplicant (NMSupplicantInterface *self,
- gboolean get_only);
-
-static void nm_supplicant_interface_smgr_state_changed (NMSupplicantManager *smgr,
- guint32 new_state,
- guint32 old_state,
- gpointer user_data);
-
-static void nm_supplicant_interface_set_state (NMSupplicantInterface *self,
- guint32 new_state);
-
-
/* Signals */
enum {
STATE, /* change in the interface's state */
REMOVED, /* interface was removed by the supplicant */
- SCANNED_AP, /* interface saw a new access point from a scan */
- SCAN_REQ_RESULT, /* result of a wireless scan request */
- SCAN_RESULTS, /* scan results returned from supplicant */
- CONNECTION_STATE, /* link state of the device's connection */
+ NEW_BSS, /* interface saw a new access point from a scan */
+ SCAN_DONE, /* wifi scan is complete */
CONNECTION_ERROR, /* an error occurred during a connection request */
LAST_SIGNAL
};
-static guint nm_supplicant_interface_signals[LAST_SIGNAL] = { 0 };
+static guint signals[LAST_SIGNAL] = { 0 };
/* Properties */
enum {
PROP_0 = 0,
- PROP_SUPPLICANT_MANAGER,
- PROP_DEVICE,
- PROP_STATE,
- PROP_CONNECTION_STATE,
PROP_SCANNING,
LAST_PROP
};
-typedef struct
-{
+typedef struct {
NMSupplicantManager * smgr;
- gulong smgr_state_sig_handler;
+ gulong smgr_avail_id;
NMDBusManager * dbus_mgr;
char * dev;
gboolean is_wireless;
@@ -110,18 +85,19 @@ typedef struct
NMCallStore * assoc_pcalls;
NMCallStore * other_pcalls;
- guint32 con_state;
gboolean scanning;
+ DBusGProxy * wpas_proxy;
DBusGProxy * iface_proxy;
- DBusGProxy * net_proxy;
+ DBusGProxy * props_proxy;
+ char * net_path;
+ guint32 blobs_left;
- guint scan_results_timeout;
guint32 last_scan;
NMSupplicantConfig * cfg;
- gboolean dispose_has_run;
+ gboolean disposed;
} NMSupplicantInterfacePrivate;
static gboolean
@@ -203,827 +179,485 @@ nm_supplicant_info_destroy (gpointer user_data)
}
}
-
-NMSupplicantInterface *
-nm_supplicant_interface_new (NMSupplicantManager * smgr, const char *ifname, gboolean is_wireless)
-{
- NMSupplicantInterface * iface;
-
- g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (smgr), NULL);
- g_return_val_if_fail (ifname != NULL, NULL);
-
- iface = g_object_new (NM_TYPE_SUPPLICANT_INTERFACE,
- "supplicant-manager", smgr,
- "device", ifname,
- NULL);
- if (iface) {
- NM_SUPPLICANT_INTERFACE_GET_PRIVATE (iface)->is_wireless = is_wireless;
- nm_supplicant_interface_start (iface);
- }
-
- return iface;
-}
-
static void
-nm_supplicant_interface_init (NMSupplicantInterface * self)
+emit_error_helper (NMSupplicantInterface *self,
+ GError *err)
{
- NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
-
- priv->state = NM_SUPPLICANT_INTERFACE_STATE_INIT;
- priv->con_state = NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED;
- priv->assoc_pcalls = nm_call_store_new ();
- priv->other_pcalls = nm_call_store_new ();
+ const char *name = NULL;
- priv->dispose_has_run = FALSE;
+ if (err->domain == DBUS_GERROR && err->code == DBUS_GERROR_REMOTE_EXCEPTION)
+ name = dbus_g_error_get_name (err);
- priv->dbus_mgr = nm_dbus_manager_get ();
+ g_signal_emit (self, signals[CONNECTION_ERROR], 0, name, err->message);
}
-
static void
-nm_supplicant_interface_set_property (GObject * object,
- guint prop_id,
- const GValue * value,
- GParamSpec * pspec)
+bssid_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
{
- NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object);
- gulong id;
-
- switch (prop_id) {
- case PROP_SUPPLICANT_MANAGER:
- priv->smgr = NM_SUPPLICANT_MANAGER (g_value_get_object (value));
- g_object_ref (G_OBJECT (priv->smgr));
-
- id = g_signal_connect (priv->smgr,
- "state",
- G_CALLBACK (nm_supplicant_interface_smgr_state_changed),
- object);
- priv->smgr_state_sig_handler = id;
- break;
- case PROP_DEVICE:
- /* Construct-only */
- priv->dev = g_strdup (g_value_get_string (value));
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
+ NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
+ GError *error = NULL;
+ GHashTable *props = NULL;
+
+ if (dbus_g_proxy_end_call (proxy, call_id, &error,
+ DBUS_TYPE_G_MAP_OF_VARIANT, &props,
+ G_TYPE_INVALID)) {
+ g_signal_emit (info->interface, signals[NEW_BSS], 0, props);
+ g_hash_table_destroy (props);
+ } else {
+ if (!strstr (error->message, "The BSSID requested was invalid")) {
+ nm_log_warn (LOGD_SUPPLICANT, "Couldn't retrieve BSSID properties: %s.",
+ error->message);
+ }
+ g_error_free (error);
}
}
static void
-nm_supplicant_interface_get_property (GObject * object,
- guint prop_id,
- GValue * value,
- GParamSpec * pspec)
+request_bss_properties (NMSupplicantInterface *self,
+ GPtrArray *paths)
{
- NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object);
+ NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+ int i;
- switch (prop_id) {
- case PROP_SUPPLICANT_MANAGER:
- g_value_set_object (value, G_OBJECT (priv->smgr));
- break;
- case PROP_DEVICE:
- g_value_set_string (value, priv->dev);
- break;
- case PROP_STATE:
- g_value_set_uint (value, priv->state);
- break;
- case PROP_CONNECTION_STATE:
- g_value_set_uint (value, priv->con_state);
- break;
- case PROP_SCANNING:
- g_value_set_boolean (value, priv->scanning);
- break;
- default:
- G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
- break;
+ /* Fire off a "properties" call for each returned BSSID */
+ for (i = 0; i < paths->len; i++) {
+ NMSupplicantInfo *info;
+ DBusGProxy *proxy;
+ DBusGProxyCall *call;
+
+ proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
+ WPAS_DBUS_SERVICE,
+ g_ptr_array_index (paths, i),
+ DBUS_INTERFACE_PROPERTIES);
+ info = nm_supplicant_info_new (self, proxy, priv->other_pcalls);
+ call = dbus_g_proxy_begin_call (proxy, "GetAll",
+ bssid_properties_cb,
+ info,
+ nm_supplicant_info_destroy,
+ G_TYPE_STRING, WPAS_DBUS_IFACE_BSS,
+ G_TYPE_INVALID);
+ nm_supplicant_info_set_call (info, call);
+ g_object_unref (proxy);
}
}
static void
-try_remove_iface (DBusGConnection *g_connection,
- const char *path)
+wpas_iface_bss_added (DBusGProxy *proxy,
+ const char *object_path,
+ GHashTable *props,
+ gpointer user_data)
{
- DBusGProxy *proxy;
-
- g_return_if_fail (g_connection != NULL);
- g_return_if_fail (path != NULL);
-
- proxy = dbus_g_proxy_new_for_name (g_connection,
- WPAS_DBUS_SERVICE,
- WPAS_DBUS_PATH,
- WPAS_DBUS_INTERFACE);
- if (!proxy)
- return;
-
- dbus_g_proxy_call_no_reply (proxy, "removeInterface",
- DBUS_TYPE_G_OBJECT_PATH, path,
- G_TYPE_INVALID);
- g_object_unref (proxy);
+ g_signal_emit (NM_SUPPLICANT_INTERFACE (user_data), signals[NEW_BSS], 0, props);
}
-static void
-nm_supplicant_interface_dispose (GObject *object)
+static int
+wpas_state_string_to_enum (const char *str_state)
{
- NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object);
- guint32 sm_state;
-
- if (priv->dispose_has_run) {
- G_OBJECT_CLASS (nm_supplicant_interface_parent_class)->dispose (object);
- return;
- }
-
- priv->dispose_has_run = TRUE;
-
- /* Ask wpa_supplicant to remove this interface */
- sm_state = nm_supplicant_manager_get_state (priv->smgr);
- if (sm_state == NM_SUPPLICANT_MANAGER_STATE_IDLE) {
- if (priv->object_path) {
- try_remove_iface (nm_dbus_manager_get_connection (priv->dbus_mgr),
- priv->object_path);
- }
- }
-
- if (priv->iface_proxy)
- g_object_unref (priv->iface_proxy);
-
- if (priv->net_proxy)
- g_object_unref (priv->net_proxy);
-
- if (priv->scan_results_timeout)
- g_source_remove (priv->scan_results_timeout);
-
- if (priv->smgr) {
- g_signal_handler_disconnect (priv->smgr,
- priv->smgr_state_sig_handler);
- g_object_unref (priv->smgr);
- }
-
- g_free (priv->dev);
-
- /* Cancel pending calls before unrefing the dbus manager */
- cancel_all_callbacks (priv->other_pcalls);
- nm_call_store_destroy (priv->other_pcalls);
-
- cancel_all_callbacks (priv->assoc_pcalls);
- nm_call_store_destroy (priv->assoc_pcalls);
-
- if (priv->dbus_mgr)
- g_object_unref (priv->dbus_mgr);
-
- if (priv->cfg)
- g_object_unref (priv->cfg);
-
- g_free (priv->object_path);
-
- /* Chain up to the parent class */
- G_OBJECT_CLASS (nm_supplicant_interface_parent_class)->dispose (object);
+ if (!strcmp (str_state, "disconnected"))
+ return NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED;
+ else if (!strcmp (str_state, "inactive"))
+ return NM_SUPPLICANT_INTERFACE_STATE_INACTIVE;
+ else if (!strcmp (str_state, "scanning"))
+ return NM_SUPPLICANT_INTERFACE_STATE_SCANNING;
+ else if (!strcmp (str_state, "associating"))
+ return NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING;
+ else if (!strcmp (str_state, "associated"))
+ return NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED;
+ else if (!strcmp (str_state, "4way_handshake"))
+ return NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE;
+ else if (!strcmp (str_state, "group_handshake"))
+ return NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE;
+ else if (!strcmp (str_state, "completed"))
+ return NM_SUPPLICANT_INTERFACE_STATE_COMPLETED;
+
+ return -1;
}
static void
-nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
+set_state (NMSupplicantInterface *self, guint32 new_state)
{
- GObjectClass *object_class = G_OBJECT_CLASS (klass);
-
- g_type_class_add_private (object_class, sizeof (NMSupplicantInterfacePrivate));
+ NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+ guint32 old_state = priv->state;
- object_class->dispose = nm_supplicant_interface_dispose;
- object_class->set_property = nm_supplicant_interface_set_property;
- object_class->get_property = nm_supplicant_interface_get_property;
+ g_return_if_fail (new_state < NM_SUPPLICANT_INTERFACE_STATE_LAST);
- /* Properties */
- g_object_class_install_property (object_class,
- PROP_SUPPLICANT_MANAGER,
- g_param_spec_object ("supplicant-manager",
- "Supplicant Manager",
- "Supplicant manager to which this interface belongs",
- NM_TYPE_SUPPLICANT_MANAGER,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class_install_property (object_class,
- PROP_DEVICE,
- g_param_spec_string ("device",
- "Device",
- "Device which this interface represents to the supplicant",
- NULL,
- G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
-
- g_object_class_install_property (object_class,
- PROP_STATE,
- g_param_spec_uint ("state",
- "State",
- "State of the supplicant interface; INIT, READY, or DOWN",
- NM_SUPPLICANT_INTERFACE_STATE_INIT,
- NM_SUPPLICANT_INTERFACE_STATE_LAST - 1,
- NM_SUPPLICANT_INTERFACE_STATE_INIT,
- G_PARAM_READABLE));
-
- g_object_class_install_property (object_class,
- PROP_SCANNING,
- g_param_spec_boolean ("scanning",
- "Scanning",
- "Scanning",
- FALSE,
- G_PARAM_READABLE));
+ if (new_state == priv->state)
+ return;
- /* Signals */
- nm_supplicant_interface_signals[STATE] =
- g_signal_new ("state",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (NMSupplicantInterfaceClass, state),
- NULL, NULL,
- _nm_marshal_VOID__UINT_UINT,
- G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+ /* DOWN is a terminal state */
+ g_return_if_fail (priv->state != NM_SUPPLICANT_INTERFACE_STATE_DOWN);
- nm_supplicant_interface_signals[REMOVED] =
- g_signal_new ("removed",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (NMSupplicantInterfaceClass, removed),
- NULL, NULL,
- g_cclosure_marshal_VOID__VOID,
- G_TYPE_NONE, 0);
+ /* Cannot regress to READY, STARTING, or INIT from higher states */
+ if (priv->state >= NM_SUPPLICANT_INTERFACE_STATE_READY)
+ g_return_if_fail (new_state > NM_SUPPLICANT_INTERFACE_STATE_READY);
- nm_supplicant_interface_signals[SCANNED_AP] =
- g_signal_new ("scanned-ap",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (NMSupplicantInterfaceClass, scanned_ap),
- NULL, NULL,
- g_cclosure_marshal_VOID__POINTER,
- G_TYPE_NONE, 1, G_TYPE_POINTER);
-
- nm_supplicant_interface_signals[SCAN_REQ_RESULT] =
- g_signal_new ("scan-req-result",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (NMSupplicantInterfaceClass, scan_req_result),
- NULL, NULL,
- g_cclosure_marshal_VOID__BOOLEAN,
- G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+ if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) {
+ /* Cancel all pending calls when going down */
+ cancel_all_callbacks (priv->other_pcalls);
+ cancel_all_callbacks (priv->assoc_pcalls);
- nm_supplicant_interface_signals[SCAN_RESULTS] =
- g_signal_new ("scan-results",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (NMSupplicantInterfaceClass, scan_results),
- NULL, NULL,
- g_cclosure_marshal_VOID__UINT,
- G_TYPE_NONE, 1, G_TYPE_UINT);
+ /* Disconnect supplicant manager state listeners since we're done */
+ if (priv->smgr_avail_id) {
+ g_signal_handler_disconnect (priv->smgr, priv->smgr_avail_id);
+ priv->smgr_avail_id = 0;
+ }
- nm_supplicant_interface_signals[CONNECTION_STATE] =
- g_signal_new ("connection-state",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (NMSupplicantInterfaceClass, connection_state),
- NULL, NULL,
- _nm_marshal_VOID__UINT_UINT,
- G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+ if (priv->iface_proxy) {
+ dbus_g_proxy_disconnect_signal (priv->iface_proxy,
+ "PropertiesChanged",
+ G_CALLBACK (wpas_iface_properties_changed),
+ self);
+ dbus_g_proxy_disconnect_signal (priv->iface_proxy,
+ "ScanDone",
+ G_CALLBACK (wpas_iface_scan_done),
+ self);
+ dbus_g_proxy_disconnect_signal (priv->iface_proxy,
+ "BSSAdded",
+ G_CALLBACK (wpas_iface_bss_added),
+ self);
+ }
+ }
- nm_supplicant_interface_signals[CONNECTION_ERROR] =
- g_signal_new ("connection-error",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (NMSupplicantInterfaceClass, connection_error),
- NULL, NULL,
- _nm_marshal_VOID__STRING_STRING,
- G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
+ priv->state = new_state;
+ g_signal_emit (self, signals[STATE], 0, priv->state, old_state);
}
static void
-emit_error_helper (NMSupplicantInterface *self,
- GError *err)
+set_state_from_string (NMSupplicantInterface *self, const char *new_state)
{
- const char *name = NULL;
+ int state;
- if (err->domain == DBUS_GERROR && err->code == DBUS_GERROR_REMOTE_EXCEPTION)
- name = dbus_g_error_get_name (err);
-
- g_signal_emit (self,
- nm_supplicant_interface_signals[CONNECTION_ERROR],
- 0,
- name,
- err->message);
+ state = wpas_state_string_to_enum (new_state);
+ g_warn_if_fail (state > 0);
+ if (state > 0)
+ set_state (self, (guint32) state);
}
static void
-bssid_properties_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+set_scanning (NMSupplicantInterface *self, gboolean new_scanning)
{
- NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
- GError *err = NULL;
- GHashTable *hash = NULL;
+ NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+ GTimeVal cur_time;
- if (!dbus_g_proxy_end_call (proxy, call_id, &err,
- DBUS_TYPE_G_MAP_OF_VARIANT, &hash,
- G_TYPE_INVALID)) {
- if (!strstr (err->message, "The BSSID requested was invalid")) {
- nm_log_warn (LOGD_SUPPLICANT, "Couldn't retrieve BSSID properties: %s.",
- err->message);
+ if (priv->scanning != new_scanning) {
+ priv->scanning = new_scanning;
+
+ /* Cache time of last scan completion */
+ if (priv->scanning == FALSE) {
+ g_get_current_time (&cur_time);
+ priv->last_scan = cur_time.tv_sec;
}
- g_error_free (err);
- } else {
- g_signal_emit (info->interface,
- nm_supplicant_interface_signals[SCANNED_AP],
- 0,
- hash);
- g_hash_table_destroy (hash);
+ g_object_notify (G_OBJECT (self), "scanning");
}
}
-static void
-request_bssid_properties (NMSupplicantInterface * self,
- const char * op)
-{
- NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
- NMSupplicantInfo *info;
- DBusGProxy *proxy;
- DBusGProxyCall *call;
-
- proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
- WPAS_DBUS_SERVICE,
- op,
- WPAS_DBUS_IFACE_BSSID);
- info = nm_supplicant_info_new (self, proxy, priv->other_pcalls);
- call = dbus_g_proxy_begin_call (proxy, "properties",
- bssid_properties_cb,
- info,
- nm_supplicant_info_destroy,
- G_TYPE_INVALID);
- nm_supplicant_info_set_call (info, call);
- g_object_unref (proxy);
-}
-
-static void
-scan_results_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+gboolean
+nm_supplicant_interface_get_scanning (NMSupplicantInterface *self)
{
- GError *err = NULL;
- GPtrArray *array = NULL;
-
- if (!dbus_g_proxy_end_call (proxy, call_id, &err,
- DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH, &array,
- G_TYPE_INVALID)) {
- nm_log_warn (LOGD_SUPPLICANT, "could not get scan results: %s.", err->message);
- g_error_free (err);
- } else {
- int i;
- NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
-
- /* Notify listeners of the result of the scan */
- g_signal_emit (info->interface,
- nm_supplicant_interface_signals[SCAN_RESULTS],
- 0,
- array->len);
-
- /* Fire off a "properties" call for each returned BSSID */
- for (i = 0; i < array->len; i++) {
- char *op = g_ptr_array_index (array, i);
+ NMSupplicantInterfacePrivate *priv;
- request_bssid_properties (info->interface, op);
- g_free (op);
- }
+ g_return_val_if_fail (self != NULL, FALSE);
- g_ptr_array_free (array, TRUE);
- }
+ priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+ if (priv->scanning)
+ return TRUE;
+ if (priv->state == NM_SUPPLICANT_INTERFACE_STATE_SCANNING)
+ return TRUE;
+ return FALSE;
}
-static gboolean
-request_scan_results (gpointer user_data)
+static void
+wpas_iface_scan_done (DBusGProxy *proxy,
+ gboolean success,
+ gpointer user_data)
{
NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
- NMSupplicantInfo *info;
- DBusGProxyCall *call;
GTimeVal cur_time;
- priv->scan_results_timeout = 0;
-
- g_return_val_if_fail (priv->iface_proxy != NULL, FALSE);
-
- info = nm_supplicant_info_new (self, priv->iface_proxy, priv->other_pcalls);
- call = dbus_g_proxy_begin_call (priv->iface_proxy, "scanResults",
- scan_results_cb,
- info,
- nm_supplicant_info_destroy,
- G_TYPE_INVALID);
- nm_supplicant_info_set_call (info, call);
-
+ /* Cache last scan completed time */
g_get_current_time (&cur_time);
priv->last_scan = cur_time.tv_sec;
- return FALSE;
+
+ g_signal_emit (self, signals[SCAN_DONE], 0, success);
}
static void
-wpas_iface_query_scan_results (DBusGProxy *proxy, gpointer user_data)
+wpas_iface_properties_changed (DBusGProxy *proxy,
+ GHashTable *props,
+ gpointer user_data)
{
- NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (user_data);
- GTimeVal cur_time;
+ NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
+ GValue *value;
- /* Only query scan results if a query is not queued */
- if (priv->scan_results_timeout)
- return;
+ value = g_hash_table_lookup (props, "Scanning");
+ if (value && G_VALUE_HOLDS_BOOLEAN (value))
+ set_scanning (self, g_value_get_boolean (value));
- g_get_current_time (&cur_time);
-
- /* Only fetch scan results every 4s max, but initially do it right away */
- if (priv->last_scan + 4 < cur_time.tv_sec) {
- priv->scan_results_timeout = g_idle_add (request_scan_results,
- user_data);
- } else {
- priv->scan_results_timeout =
- g_timeout_add_seconds ((4 - (cur_time.tv_sec - priv->last_scan)),
- request_scan_results, user_data);
- }
-}
+ value = g_hash_table_lookup (props, "State");
+ if (value && G_VALUE_HOLDS_STRING (value))
+ set_state_from_string (self, g_value_get_string (value));
-static guint32
-wpas_state_string_to_enum (const char * str_state)
-{
- guint32 enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED;
-
- if (!strcmp (str_state, "DISCONNECTED"))
- enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED;
- else if (!strcmp (str_state, "INACTIVE"))
- enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_INACTIVE;
- else if (!strcmp (str_state, "SCANNING"))
- enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_SCANNING;
- else if (!strcmp (str_state, "ASSOCIATING"))
- enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATING;
- else if (!strcmp (str_state, "ASSOCIATED"))
- enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATED;
- else if (!strcmp (str_state, "4WAY_HANDSHAKE"))
- enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_4WAY_HANDSHAKE;
- else if (!strcmp (str_state, "GROUP_HANDSHAKE"))
- enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_GROUP_HANDSHAKE;
- else if (!strcmp (str_state, "COMPLETED"))
- enum_state = NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED;
-
- return enum_state;
+ value = g_hash_table_lookup (props, "BSSs");
+ if (value && G_VALUE_HOLDS (value, DBUS_TYPE_G_ARRAY_OF_OBJECT_PATH))
+ request_bss_properties (self, g_value_get_boxed (value));
}
static void
-wpas_iface_handle_state_change (DBusGProxy *proxy,
- const char *str_new_state,
- const char *str_old_state,
- gpointer user_data)
+iface_get_props_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
{
- NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (user_data);
- guint32 old_state, enum_new_state;
-
- enum_new_state = wpas_state_string_to_enum (str_new_state);
- old_state = priv->con_state;
- priv->con_state = enum_new_state;
- if (priv->con_state != old_state) {
- g_signal_emit (user_data,
- nm_supplicant_interface_signals[CONNECTION_STATE],
- 0,
- priv->con_state,
- old_state);
- }
-}
-
-
-static void
-iface_state_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
-{
- GError *err = NULL;
- char *state_str = NULL;
-
- if (!dbus_g_proxy_end_call (proxy, call_id, &err,
- G_TYPE_STRING, &state_str,
- G_TYPE_INVALID)) {
- nm_log_warn (LOGD_SUPPLICANT, "could not get interface state: %s.", err->message);
- g_error_free (err);
+ NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
+ GHashTable *props = NULL;
+ GError *error = NULL;
+
+ if (dbus_g_proxy_end_call (proxy, call_id, &error,
+ DBUS_TYPE_G_MAP_OF_VARIANT, &props,
+ G_TYPE_INVALID)) {
+ wpas_iface_properties_changed (NULL, props, info->interface);
+ g_hash_table_destroy (props);
} else {
- NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
- NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
-
- priv->con_state = wpas_state_string_to_enum (state_str);
- g_free (state_str);
- nm_supplicant_interface_set_state (info->interface, NM_SUPPLICANT_INTERFACE_STATE_READY);
+ nm_log_warn (LOGD_SUPPLICANT, "could not get interface properties: %s.",
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
}
}
static void
-wpas_iface_get_state (NMSupplicantInterface *self)
+wpas_iface_get_props (NMSupplicantInterface *self)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
NMSupplicantInfo *info;
DBusGProxyCall *call;
- info = nm_supplicant_info_new (self, priv->iface_proxy, priv->other_pcalls);
- call = dbus_g_proxy_begin_call (priv->iface_proxy, "state",
- iface_state_cb,
+ info = nm_supplicant_info_new (self, priv->props_proxy, priv->other_pcalls);
+ call = dbus_g_proxy_begin_call (priv->props_proxy, "GetAll",
+ iface_get_props_cb,
info,
nm_supplicant_info_destroy,
+ G_TYPE_STRING, WPAS_DBUS_IFACE_INTERFACE,
G_TYPE_INVALID);
nm_supplicant_info_set_call (info, call);
}
static void
-iface_scanning_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+interface_add_done (NMSupplicantInterface *self, char *path)
+{
+ NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+
+ nm_log_dbg (LOGD_SUPPLICANT, "(%s): interface added to supplicant", priv->dev);
+
+ priv->object_path = path;
+
+ priv->iface_proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
+ WPAS_DBUS_SERVICE,
+ path,
+ WPAS_DBUS_IFACE_INTERFACE);
+
+ dbus_g_object_register_marshaller (g_cclosure_marshal_VOID__BOXED,
+ G_TYPE_NONE,
+ DBUS_TYPE_G_MAP_OF_VARIANT,
+ G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (priv->iface_proxy, "PropertiesChanged",
+ DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->iface_proxy, "PropertiesChanged",
+ G_CALLBACK (wpas_iface_properties_changed),
+ self, NULL);
+
+ dbus_g_proxy_add_signal (priv->iface_proxy, "ScanDone", G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->iface_proxy, "ScanDone",
+ G_CALLBACK (wpas_iface_scan_done),
+ self,
+ NULL);
+
+ dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_BOXED,
+ G_TYPE_NONE,
+ G_TYPE_STRING, DBUS_TYPE_G_MAP_OF_VARIANT,
+ G_TYPE_INVALID);
+ dbus_g_proxy_add_signal (priv->iface_proxy, "BSSAdded", G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->iface_proxy, "BSSAdded",
+ G_CALLBACK (wpas_iface_bss_added),
+ self,
+ NULL);
+
+ priv->props_proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
+ WPAS_DBUS_SERVICE,
+ path,
+ DBUS_INTERFACE_PROPERTIES);
+ /* Get initial properties */
+ wpas_iface_get_props (self);
+
+ set_state (self, NM_SUPPLICANT_INTERFACE_STATE_READY);
+}
+
+static void
+interface_get_cb (DBusGProxy *proxy,
+ DBusGProxyCall *call_id,
+ gpointer user_data)
{
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
- gboolean scanning = FALSE;
+ GError *error = NULL;
+ char *path = NULL;
- if (dbus_g_proxy_end_call (proxy, call_id, NULL,
- G_TYPE_BOOLEAN, &scanning,
- G_TYPE_INVALID)) {
- if (scanning != priv->scanning) {
- priv->scanning = scanning;
- g_object_notify (G_OBJECT (info->interface), "scanning");
- }
+ if (dbus_g_proxy_end_call (proxy, call_id, &error,
+ DBUS_TYPE_G_OBJECT_PATH, &path,
+ G_TYPE_INVALID)) {
+ interface_add_done (info->interface, path);
+ } else {
+ nm_log_err (LOGD_SUPPLICANT, "(%s): error getting interface: %s",
+ priv->dev, error->message);
+ g_clear_error (&error);
+ set_state (info->interface, NM_SUPPLICANT_INTERFACE_STATE_DOWN);
}
}
static void
-wpas_iface_get_scanning (NMSupplicantInterface *self)
+interface_get (NMSupplicantInterface *self)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
NMSupplicantInfo *info;
DBusGProxyCall *call;
- info = nm_supplicant_info_new (self, priv->iface_proxy, priv->other_pcalls);
- call = dbus_g_proxy_begin_call (priv->iface_proxy, "scanning",
- iface_scanning_cb,
+ info = nm_supplicant_info_new (self, priv->wpas_proxy, priv->other_pcalls);
+ call = dbus_g_proxy_begin_call (priv->wpas_proxy, "GetInterface",
+ interface_get_cb,
info,
nm_supplicant_info_destroy,
+ G_TYPE_STRING, priv->dev,
G_TYPE_INVALID);
nm_supplicant_info_set_call (info, call);
}
static void
-wpas_iface_handle_scanning (DBusGProxy *proxy,
- gboolean scanning,
- gpointer user_data)
-{
- NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
- NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
-
- if (scanning != priv->scanning) {
- priv->scanning = scanning;
- g_object_notify (G_OBJECT (self), "scanning");
- }
-}
-
-gboolean
-nm_supplicant_interface_get_scanning (NMSupplicantInterface *self)
-{
- NMSupplicantInterfacePrivate *priv;
-
- g_return_val_if_fail (self != NULL, FALSE);
-
- priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
- if (priv->scanning)
- return TRUE;
- if (priv->con_state == NM_SUPPLICANT_INTERFACE_CON_STATE_SCANNING)
- return TRUE;
- return FALSE;
-}
-
-static void
-nm_supplicant_interface_add_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+interface_add_cb (DBusGProxy *proxy,
+ DBusGProxyCall *call_id,
+ gpointer user_data)
{
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
- GError *err = NULL;
+ GError *error = NULL;
char *path = NULL;
- if (!dbus_g_proxy_end_call (proxy, call_id, &err,
- DBUS_TYPE_G_OBJECT_PATH, &path,
- G_TYPE_INVALID)) {
-
- if (dbus_g_error_has_name (err, WPAS_ERROR_INVALID_IFACE)) {
- /* Interface not added, try to add it */
- nm_supplicant_interface_add_to_supplicant (info->interface, FALSE);
- } else if (dbus_g_error_has_name (err, WPAS_ERROR_EXISTS_ERROR)) {
- /* Interface already added, just try to get the interface */
- nm_supplicant_interface_add_to_supplicant (info->interface, TRUE);
+ if (dbus_g_proxy_end_call (proxy, call_id, &error,
+ DBUS_TYPE_G_OBJECT_PATH, &path,
+ G_TYPE_INVALID)) {
+ interface_add_done (info->interface, path);
+ } else {
+ if (dbus_g_error_has_name (error, WPAS_ERROR_EXISTS_ERROR)) {
+ /* Interface already added, just get its object path */
+ interface_get (info->interface);
+ } else if ( g_error_matches (error, DBUS_GERROR, DBUS_GERROR_SERVICE_UNKNOWN)
+ || dbus_g_error_has_name (error, DBUS_ERROR_SPAWN_SERVICE_NOT_FOUND)) {
+ /* Supplicant wasn't running and could be launched via service
+ * activation. Wait for it to start by moving back to the INIT
+ * state.
+ */
+ nm_log_dbg (LOGD_SUPPLICANT, "(%s): failed to activate supplicant: %s",
+ priv->dev, error->message);
+ set_state (info->interface, NM_SUPPLICANT_INTERFACE_STATE_INIT);
} else {
- nm_log_err (LOGD_SUPPLICANT, "(%s): error getting interface: %s",
- priv->dev, err->message);
+ nm_log_err (LOGD_SUPPLICANT, "(%s): error adding interface: %s",
+ priv->dev, error->message);
+ set_state (info->interface, NM_SUPPLICANT_INTERFACE_STATE_DOWN);
}
-
- g_error_free (err);
- } else {
- nm_log_dbg (LOGD_SUPPLICANT, "(%s): interface added to supplicant", priv->dev);
-
- priv->object_path = path;
-
- priv->iface_proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
- WPAS_DBUS_SERVICE,
- path,
- WPAS_DBUS_IFACE_INTERFACE);
-
- dbus_g_proxy_add_signal (priv->iface_proxy, "ScanResultsAvailable", G_TYPE_INVALID);
-
- dbus_g_object_register_marshaller (_nm_marshal_VOID__STRING_STRING,
- G_TYPE_NONE,
- G_TYPE_STRING, G_TYPE_STRING,
- G_TYPE_INVALID);
-
- dbus_g_proxy_add_signal (priv->iface_proxy, "StateChange", G_TYPE_STRING, G_TYPE_STRING, G_TYPE_INVALID);
-
- dbus_g_proxy_connect_signal (priv->iface_proxy, "ScanResultsAvailable",
- G_CALLBACK (wpas_iface_query_scan_results),
- info->interface,
- NULL);
-
- dbus_g_proxy_connect_signal (priv->iface_proxy, "StateChange",
- G_CALLBACK (wpas_iface_handle_state_change),
- info->interface,
- NULL);
-
- dbus_g_proxy_add_signal (priv->iface_proxy, "Scanning", G_TYPE_BOOLEAN, G_TYPE_INVALID);
-
- dbus_g_proxy_connect_signal (priv->iface_proxy, "Scanning",
- G_CALLBACK (wpas_iface_handle_scanning),
- info->interface,
- NULL);
-
- /* Interface added to the supplicant; get its initial state. */
- wpas_iface_get_state (info->interface);
- wpas_iface_get_scanning (info->interface);
+ g_clear_error (&error);
}
}
static void
-nm_supplicant_interface_add_to_supplicant (NMSupplicantInterface * self,
- gboolean get_only)
+interface_add (NMSupplicantInterface *self, gboolean is_wireless)
{
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
- NMSupplicantInfo *info;
- DBusGProxy *proxy;
DBusGProxyCall *call;
-
- proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
- WPAS_DBUS_SERVICE,
- WPAS_DBUS_PATH,
- WPAS_DBUS_INTERFACE);
- info = nm_supplicant_info_new (self, proxy, priv->other_pcalls);
-
- if (get_only) {
- call = dbus_g_proxy_begin_call (proxy, "getInterface",
- nm_supplicant_interface_add_cb,
- info,
- nm_supplicant_info_destroy,
- G_TYPE_STRING, priv->dev,
- G_TYPE_INVALID);
- } else {
- GHashTable *hash = g_hash_table_new (g_str_hash, g_str_equal);
- GValue *driver;
-
- driver = g_new0 (GValue, 1);
- g_value_init (driver, G_TYPE_STRING);
- g_value_set_string (driver, priv->is_wireless ? "wext" : "wired");
- g_hash_table_insert (hash, "driver", driver);
-
- call = dbus_g_proxy_begin_call (proxy, "addInterface",
- nm_supplicant_interface_add_cb,
- info,
- nm_supplicant_info_destroy,
- G_TYPE_STRING, priv->dev,
- DBUS_TYPE_G_MAP_OF_VARIANT, hash,
- G_TYPE_INVALID);
-
- g_value_unset (driver);
- g_free (driver);
- g_hash_table_destroy (hash);
- }
-
- g_object_unref (proxy);
-
- nm_supplicant_info_set_call (info, call);
-}
-
-static void
-nm_supplicant_interface_start (NMSupplicantInterface * self)
-{
- NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
- guint32 state;
+ NMSupplicantInfo *info;
+ GHashTable *hash;
+ GValue *driver, *ifname;
/* Can only start the interface from INIT state */
g_return_if_fail (priv->state == NM_SUPPLICANT_INTERFACE_STATE_INIT);
nm_log_dbg (LOGD_SUPPLICANT, "(%s): adding interface to supplicant", priv->dev);
- state = nm_supplicant_manager_get_state (priv->smgr);
- if (state == NM_SUPPLICANT_MANAGER_STATE_IDLE) {
- nm_supplicant_interface_set_state (self, NM_SUPPLICANT_INTERFACE_STATE_STARTING);
- nm_supplicant_interface_add_to_supplicant (self, FALSE);
- } else if (state == NM_SUPPLICANT_MANAGER_STATE_DOWN) {
- /* Don't do anything; wait for signal from supplicant manager
- * that its state has changed.
- */
- } else
- nm_log_warn (LOGD_SUPPLICANT, "Unknown supplicant manager state!");
-}
+ /* Move to starting to prevent double-calls of interface_add() */
+ set_state (self, NM_SUPPLICANT_INTERFACE_STATE_STARTING);
-static void
-nm_supplicant_interface_handle_supplicant_manager_idle_state (NMSupplicantInterface * self)
-{
- switch (NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->state) {
- case NM_SUPPLICANT_INTERFACE_STATE_INIT:
- /* Move to STARTING state when supplicant is ready */
- nm_supplicant_interface_start (self);
- break;
- case NM_SUPPLICANT_INTERFACE_STATE_STARTING:
- /* Don't do anything here, though we should never hit this */
- break;
- case NM_SUPPLICANT_INTERFACE_STATE_READY:
- /* Don't do anything here, though we should never hit this */
- break;
- case NM_SUPPLICANT_INTERFACE_STATE_DOWN:
- /* Don't do anything here; interface can't get out of DOWN state */
- break;
- default:
- nm_log_warn (LOGD_SUPPLICANT, "Unknown supplicant interface state!");
- break;
- }
-}
+ /* Try to add the interface to the supplicant. If the supplicant isn't
+ * running, this will start it via D-Bus activation and return the response
+ * when the supplicant has started.
+ */
+ info = nm_supplicant_info_new (self, priv->wpas_proxy, priv->other_pcalls);
-static void
-nm_supplicant_interface_set_state (NMSupplicantInterface * self,
- guint32 new_state)
-{
- NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
- guint32 old_state;
+ hash = g_hash_table_new (g_str_hash, g_str_equal);
- g_return_if_fail (new_state < NM_SUPPLICANT_INTERFACE_STATE_LAST);
+ driver = g_new0 (GValue, 1);
+ g_value_init (driver, G_TYPE_STRING);
+ g_value_set_string (driver, is_wireless ? "nl80211,wext" : "wired");
+ g_hash_table_insert (hash, "Driver", driver);
- if (new_state == priv->state)
- return;
+ ifname = g_new0 (GValue, 1);
+ g_value_init (ifname, G_TYPE_STRING);
+ g_value_set_string (ifname, priv->dev);
+ g_hash_table_insert (hash, "Ifname", ifname);
- old_state = priv->state;
- if (new_state == NM_SUPPLICANT_INTERFACE_STATE_DOWN) {
- /* If the interface is transitioning to DOWN and there's are
- * in-progress pending calls, cancel them.
- */
- cancel_all_callbacks (priv->other_pcalls);
- cancel_all_callbacks (priv->assoc_pcalls);
- }
+ call = dbus_g_proxy_begin_call (priv->wpas_proxy, "CreateInterface",
+ interface_add_cb,
+ info,
+ nm_supplicant_info_destroy,
+ DBUS_TYPE_G_MAP_OF_VARIANT, hash,
+ G_TYPE_INVALID);
- priv->state = new_state;
- g_signal_emit (self,
- nm_supplicant_interface_signals[STATE],
- 0,
- priv->state,
- old_state);
+ g_hash_table_destroy (hash);
+ g_value_unset (driver);
+ g_free (driver);
+ g_value_unset (ifname);
+ g_free (ifname);
+
+ nm_supplicant_info_set_call (info, call);
}
static void
-nm_supplicant_interface_smgr_state_changed (NMSupplicantManager * smgr,
- guint32 new_state,
- guint32 old_state,
- gpointer user_data)
+smgr_avail_cb (NMSupplicantManager *smgr,
+ GParamSpec *pspec,
+ gpointer user_data)
{
- NMSupplicantInterface * self = NM_SUPPLICANT_INTERFACE (user_data);
+ NMSupplicantInterface *self = NM_SUPPLICANT_INTERFACE (user_data);
+ NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (user_data);
- switch (new_state) {
- case NM_SUPPLICANT_MANAGER_STATE_DOWN:
- /* The supplicant went away, likely the connection to it is also
- * gone. Therefore, this interface must move to the DOWN state
- * and be disposed of.
- */
- nm_supplicant_interface_set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN);
- break;
- case NM_SUPPLICANT_MANAGER_STATE_IDLE:
- /* Handle the supplicant now being available. */
- nm_supplicant_interface_handle_supplicant_manager_idle_state (self);
- break;
- default:
- nm_log_warn (LOGD_SUPPLICANT, "Unknown supplicant manager state!");
- break;
+ if (nm_supplicant_manager_available (smgr)) {
+ /* This can happen if the supplicant couldn't be activated but
+ * for some reason was started after the activation failure.
+ */
+ if (priv->state == NM_SUPPLICANT_INTERFACE_STATE_INIT)
+ interface_add (self, priv->is_wireless);
+ } else {
+ /* The supplicant stopped; so we must tear down the interface */
+ set_state (self, NM_SUPPLICANT_INTERFACE_STATE_DOWN);
}
}
-
static void
remove_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
{
- GError *err = NULL;
- guint tmp;
+ GError *error = NULL;
- if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) {
+ if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) {
nm_log_dbg (LOGD_SUPPLICANT, "Couldn't remove network from supplicant interface: %s.",
- err->message);
- g_error_free (err);
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
}
}
static void
disconnect_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
{
- GError *err = NULL;
- guint tmp;
+ GError *error = NULL;
- if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) {
+ if (!dbus_g_proxy_end_call (proxy, call_id, &error, G_TYPE_INVALID)) {
nm_log_warn (LOGD_SUPPLICANT, "Couldn't disconnect supplicant interface: %s.",
- err->message);
- g_error_free (err);
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
}
}
@@ -1045,32 +679,26 @@ nm_supplicant_interface_disconnect (NMSupplicantInterface * self)
if (!priv->iface_proxy)
return;
- /* Don't try to disconnect if the supplicant interface is already
- * disconnected.
- */
- if (priv->con_state == NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED
- || priv->con_state == NM_SUPPLICANT_INTERFACE_CON_STATE_INACTIVE) {
- if (priv->net_proxy) {
- g_object_unref (priv->net_proxy);
- priv->net_proxy = NULL;
- }
-
+ /* Don't try to disconnect if the supplicant interface is already disconnected */
+ if ( priv->state == NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED
+ || priv->state == NM_SUPPLICANT_INTERFACE_STATE_INACTIVE) {
+ g_free (priv->net_path);
+ priv->net_path = NULL;
return;
}
/* Remove any network that was added by NetworkManager */
- if (priv->net_proxy) {
- dbus_g_proxy_begin_call (priv->iface_proxy, "removeNetwork",
+ if (priv->net_path) {
+ dbus_g_proxy_begin_call (priv->iface_proxy, "RemoveNetwork",
remove_network_cb,
NULL, NULL,
- DBUS_TYPE_G_OBJECT_PATH, dbus_g_proxy_get_path (priv->net_proxy),
+ DBUS_TYPE_G_OBJECT_PATH, priv->net_path,
G_TYPE_INVALID);
-
- g_object_unref (priv->net_proxy);
- priv->net_proxy = NULL;
+ g_free (priv->net_path);
+ priv->net_path = NULL;
}
- dbus_g_proxy_begin_call (priv->iface_proxy, "disconnect",
+ dbus_g_proxy_begin_call (priv->iface_proxy, "Disconnect",
disconnect_cb,
NULL, NULL,
G_TYPE_INVALID);
@@ -1081,9 +709,8 @@ select_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_dat
{
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
GError *err = NULL;
- guint tmp;
- if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) {
+ if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_INVALID)) {
nm_log_warn (LOGD_SUPPLICANT, "Couldn't select network config: %s.", err->message);
emit_error_helper (info->interface, err);
g_error_free (err);
@@ -1091,163 +718,86 @@ select_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_dat
}
static void
-set_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
-{
- NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
- NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
- GError *err = NULL;
- guint tmp;
-
- if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) {
- nm_log_warn (LOGD_SUPPLICANT, "Couldn't set network config: %s.", err->message);
- emit_error_helper (info->interface, err);
- g_error_free (err);
- } else {
- DBusGProxyCall *call;
-
- info = nm_supplicant_info_new (info->interface, priv->iface_proxy, priv->assoc_pcalls);
- call = dbus_g_proxy_begin_call (priv->iface_proxy, "selectNetwork",
- select_network_cb,
- info,
- nm_supplicant_info_destroy,
- DBUS_TYPE_G_OBJECT_PATH, dbus_g_proxy_get_path (proxy),
- G_TYPE_INVALID);
- nm_supplicant_info_set_call (info, call);
- }
-}
-
-static void
-call_set_network (NMSupplicantInfo *info)
+call_select_network (NMSupplicantInterface *self)
{
- NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
- GHashTable *config_hash;
+ NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
DBusGProxyCall *call;
+ NMSupplicantInfo *info;
- config_hash = nm_supplicant_config_get_hash (priv->cfg);
- call = dbus_g_proxy_begin_call (priv->net_proxy, "set",
- set_network_cb,
+ /* We only select the network after all blobs (if any) have been set */
+ if (priv->blobs_left > 0)
+ return;
+
+ info = nm_supplicant_info_new (self, priv->iface_proxy, priv->assoc_pcalls);
+ call = dbus_g_proxy_begin_call (priv->iface_proxy, "SelectNetwork",
+ select_network_cb,
info,
nm_supplicant_info_destroy,
- DBUS_TYPE_G_MAP_OF_VARIANT, config_hash,
+ DBUS_TYPE_G_OBJECT_PATH, priv->net_path,
G_TYPE_INVALID);
nm_supplicant_info_set_call (info, call);
- g_hash_table_destroy (config_hash);
}
static void
-set_blobs_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
+add_blob_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
{
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
GError *err = NULL;
guint tmp;
+ priv->blobs_left--;
+
if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) {
nm_log_warn (LOGD_SUPPLICANT, "Couldn't set network certificates: %s.", err->message);
emit_error_helper (info->interface, err);
g_error_free (err);
- } else {
- info = nm_supplicant_info_new (info->interface, priv->iface_proxy, priv->assoc_pcalls);
- call_set_network (info);
- }
-}
-
-static GValue *
-byte_array_to_gvalue (const GByteArray *array)
-{
- GValue *val;
-
- val = g_slice_new0 (GValue);
- g_value_init (val, DBUS_TYPE_G_UCHAR_ARRAY);
- g_value_set_boxed (val, array);
-
- return val;
-}
-
-static void
-blob_free (GValue *val)
-{
- g_value_unset (val);
- g_slice_free (GValue, val);
-}
-
-static void
-convert_blob (const char *key, const GByteArray *value, GHashTable *hash)
-{
- GValue *val;
-
- val = byte_array_to_gvalue (value);
- g_hash_table_insert (hash, g_strdup (key), val);
-}
-
-static void
-call_set_blobs (NMSupplicantInfo *info, GHashTable *orig_blobs)
-{
- NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
- DBusGProxyCall *call;
- GHashTable *blobs;
-
- blobs = g_hash_table_new_full (g_str_hash, g_str_equal,
- (GDestroyNotify) g_free,
- (GDestroyNotify) blob_free);
- if (!blobs) {
- const char *msg = "Not enough memory to create blob table.";
-
- nm_log_warn (LOGD_SUPPLICANT, "%s", msg);
- g_signal_emit (info->interface,
- nm_supplicant_interface_signals[CONNECTION_ERROR],
- 0, "SendBlobError", msg);
- return;
- }
-
- g_hash_table_foreach (orig_blobs, (GHFunc) convert_blob, blobs);
-
- call = dbus_g_proxy_begin_call (priv->iface_proxy, "setBlobs",
- set_blobs_cb,
- info,
- nm_supplicant_info_destroy,
- DBUS_TYPE_G_MAP_OF_VARIANT, blobs,
- G_TYPE_INVALID);
- nm_supplicant_info_set_call (info, call);
- g_hash_table_destroy (blobs);
+ } else
+ call_select_network (info->interface);
}
static void
add_network_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
{
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
+ NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
GError *err = NULL;
- char *path = NULL;
+ GHashTable *blobs;
+ GHashTableIter iter;
+ gpointer name, data;
+ DBusGProxyCall *call;
+ NMSupplicantInfo *blob_info;
+
+ g_free (priv->net_path);
+ priv->net_path = NULL;
if (!dbus_g_proxy_end_call (proxy, call_id, &err,
- DBUS_TYPE_G_OBJECT_PATH, &path,
+ DBUS_TYPE_G_OBJECT_PATH, &priv->net_path,
G_TYPE_INVALID)) {
nm_log_warn (LOGD_SUPPLICANT, "Couldn't add a network to the supplicant interface: %s.",
err->message);
emit_error_helper (info->interface, err);
g_error_free (err);
- } else {
- NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
- GHashTable *blobs;
-
- priv->net_proxy = dbus_g_proxy_new_for_name (nm_dbus_manager_get_connection (priv->dbus_mgr),
- WPAS_DBUS_SERVICE,
- path,
- WPAS_DBUS_IFACE_NETWORK);
- g_free (path);
-
- info = nm_supplicant_info_new (info->interface,
- priv->net_proxy,
- priv->assoc_pcalls);
- /* Send any blobs first; if there aren't any jump to sending the
- * config settings.
- */
- blobs = nm_supplicant_config_get_blobs (priv->cfg);
- if (g_hash_table_size (blobs) > 0)
- call_set_blobs (info, blobs);
- else
- call_set_network (info);
+ return;
+ }
+
+ /* Send blobs first; otherwise jump to sending the config settings */
+ blobs = nm_supplicant_config_get_blobs (priv->cfg);
+ priv->blobs_left = g_hash_table_size (blobs);
+ g_hash_table_iter_init (&iter, blobs);
+ while (g_hash_table_iter_next (&iter, &name, &data)) {
+ blob_info = nm_supplicant_info_new (info->interface, priv->iface_proxy, priv->assoc_pcalls);
+ call = dbus_g_proxy_begin_call (priv->iface_proxy, "AddBlob",
+ add_blob_cb,
+ blob_info,
+ nm_supplicant_info_destroy,
+ DBUS_TYPE_STRING, name,
+ DBUS_TYPE_G_UCHAR_ARRAY, blobs,
+ G_TYPE_INVALID);
+ nm_supplicant_info_set_call (blob_info, call);
}
+
+ call_select_network (info->interface);
}
static void
@@ -1256,10 +806,10 @@ set_ap_scan_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (info->interface);
GError *err = NULL;
- guint32 tmp;
DBusGProxyCall *call;
+ GHashTable *config_hash;
- if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_UINT, &tmp, G_TYPE_INVALID)) {
+ if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_INVALID)) {
nm_log_warn (LOGD_SUPPLICANT, "Couldn't send AP scan mode to the supplicant interface: %s.",
err->message);
emit_error_helper (info->interface, err);
@@ -1270,12 +820,15 @@ set_ap_scan_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
nm_log_info (LOGD_SUPPLICANT, "Config: set interface ap_scan to %d",
nm_supplicant_config_get_ap_scan (priv->cfg));
- info = nm_supplicant_info_new (info->interface, proxy, info->store);
- call = dbus_g_proxy_begin_call (proxy, "addNetwork",
+ info = nm_supplicant_info_new (info->interface, priv->iface_proxy, info->store);
+ config_hash = nm_supplicant_config_get_hash (priv->cfg);
+ call = dbus_g_proxy_begin_call (priv->iface_proxy, "AddNetwork",
add_network_cb,
info,
nm_supplicant_info_destroy,
+ DBUS_TYPE_G_MAP_OF_VARIANT, config_hash,
G_TYPE_INVALID);
+ g_hash_table_destroy (config_hash);
nm_supplicant_info_set_call (info, call);
}
@@ -1286,7 +839,7 @@ nm_supplicant_interface_set_config (NMSupplicantInterface * self,
NMSupplicantInterfacePrivate *priv;
NMSupplicantInfo *info;
DBusGProxyCall *call;
- guint32 ap_scan;
+ GValue value = { 0, };
g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE);
@@ -1303,46 +856,54 @@ nm_supplicant_interface_set_config (NMSupplicantInterface * self,
g_object_ref (priv->cfg);
- info = nm_supplicant_info_new (self, priv->iface_proxy, priv->other_pcalls);
- ap_scan = nm_supplicant_config_get_ap_scan (priv->cfg);
- call = dbus_g_proxy_begin_call (priv->iface_proxy, "setAPScan",
+ g_value_init (&value, G_TYPE_UINT);
+ g_value_set_uint (&value, nm_supplicant_config_get_ap_scan (priv->cfg));
+
+ info = nm_supplicant_info_new (self, priv->props_proxy, priv->other_pcalls);
+ call = dbus_g_proxy_begin_call (priv->props_proxy, "Set",
set_ap_scan_cb,
info,
nm_supplicant_info_destroy,
- G_TYPE_UINT, ap_scan,
+ G_TYPE_STRING, WPAS_DBUS_IFACE_INTERFACE,
+ G_TYPE_STRING, "ApScan",
+ G_TYPE_VALUE, &value,
G_TYPE_INVALID);
nm_supplicant_info_set_call (info, call);
+ g_value_unset (&value);
return call != NULL;
}
-const char *
-nm_supplicant_interface_get_device (NMSupplicantInterface * self)
-{
- g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NULL);
-
- return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->dev;
-}
-
static void
scan_request_cb (DBusGProxy *proxy, DBusGProxyCall *call_id, gpointer user_data)
{
NMSupplicantInfo *info = (NMSupplicantInfo *) user_data;
GError *err = NULL;
- guint32 success = 0;
- if (!dbus_g_proxy_end_call (proxy, call_id, &err,
- G_TYPE_UINT, &success,
- G_TYPE_INVALID)) {
+ if (!dbus_g_proxy_end_call (proxy, call_id, &err, G_TYPE_INVALID)) {
nm_log_warn (LOGD_SUPPLICANT, "Could not get scan request result: %s", err->message);
- g_error_free (err);
}
+ g_signal_emit (info->interface, signals[SCAN_DONE], 0, err ? FALSE : TRUE);
+ g_clear_error (&err);
+}
+
+static void
+destroy_gvalue (gpointer data)
+{
+ GValue *value = (GValue *) data;
- /* Notify listeners of the result of the scan */
- g_signal_emit (info->interface,
- nm_supplicant_interface_signals[SCAN_REQ_RESULT],
- 0,
- success ? TRUE : FALSE);
+ g_value_unset (value);
+ g_slice_free (GValue, value);
+}
+
+static GValue *
+string_to_gvalue (const char *str)
+{
+ GValue *val = g_slice_new0 (GValue);
+
+ g_value_init (val, G_TYPE_STRING);
+ g_value_set_string (val, str);
+ return val;
}
gboolean
@@ -1351,17 +912,24 @@ nm_supplicant_interface_request_scan (NMSupplicantInterface * self)
NMSupplicantInterfacePrivate *priv;
NMSupplicantInfo *info;
DBusGProxyCall *call;
+ GHashTable *hash;
g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE);
priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+ /* Scan parameters */
+ hash = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, destroy_gvalue);
+ g_hash_table_insert (hash, "Type", string_to_gvalue ("active"));
+
info = nm_supplicant_info_new (self, priv->iface_proxy, priv->other_pcalls);
- call = dbus_g_proxy_begin_call (priv->iface_proxy, "scan",
+ call = dbus_g_proxy_begin_call (priv->iface_proxy, "Scan",
scan_request_cb,
info,
nm_supplicant_info_destroy,
+ DBUS_TYPE_G_MAP_OF_VARIANT, hash,
G_TYPE_INVALID);
+ g_hash_table_destroy (hash);
nm_supplicant_info_set_call (info, call);
return call != NULL;
@@ -1375,14 +943,6 @@ nm_supplicant_interface_get_state (NMSupplicantInterface * self)
return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->state;
}
-guint32
-nm_supplicant_interface_get_connection_state (NMSupplicantInterface * self)
-{
- g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED);
-
- return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->con_state;
-}
-
const char *
nm_supplicant_interface_state_to_string (guint32 state)
{
@@ -1393,6 +953,22 @@ nm_supplicant_interface_state_to_string (guint32 state)
return "starting";
case NM_SUPPLICANT_INTERFACE_STATE_READY:
return "ready";
+ case NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED:
+ return "disconnected";
+ case NM_SUPPLICANT_INTERFACE_STATE_INACTIVE:
+ return "inactive";
+ case NM_SUPPLICANT_INTERFACE_STATE_SCANNING:
+ return "scanning";
+ case NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING:
+ return "associating";
+ case NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED:
+ return "associated";
+ case NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE:
+ return "4-way handshake";
+ case NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE:
+ return "group handshake";
+ case NM_SUPPLICANT_INTERFACE_STATE_COMPLETED:
+ return "completed";
case NM_SUPPLICANT_INTERFACE_STATE_DOWN:
return "down";
default:
@@ -1402,28 +978,227 @@ nm_supplicant_interface_state_to_string (guint32 state)
}
const char *
-nm_supplicant_interface_connection_state_to_string (guint32 state)
+nm_supplicant_interface_get_device (NMSupplicantInterface * self)
{
- switch (state) {
- case NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED:
- return "disconnected";
- case NM_SUPPLICANT_INTERFACE_CON_STATE_INACTIVE:
- return "inactive";
- case NM_SUPPLICANT_INTERFACE_CON_STATE_SCANNING:
- return "scanning";
- case NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATING:
- return "associating";
- case NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATED:
- return "associated";
- case NM_SUPPLICANT_INTERFACE_CON_STATE_4WAY_HANDSHAKE:
- return "4-way handshake";
- case NM_SUPPLICANT_INTERFACE_CON_STATE_GROUP_HANDSHAKE:
- return "group handshake";
- case NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED:
- return "completed";
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NULL);
+
+ return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->dev;
+}
+
+const char *
+nm_supplicant_interface_get_object_path (NMSupplicantInterface *self)
+{
+ g_return_val_if_fail (self != NULL, NULL);
+ g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), NULL);
+
+ return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->object_path;
+}
+
+const char *
+nm_supplicant_interface_get_ifname (NMSupplicantInterface *self)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (NM_IS_SUPPLICANT_INTERFACE (self), FALSE);
+
+ return NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self)->dev;
+}
+
+/*******************************************************************/
+
+NMSupplicantInterface *
+nm_supplicant_interface_new (NMSupplicantManager *smgr,
+ const char *ifname,
+ gboolean is_wireless,
+ gboolean start_now)
+{
+ NMSupplicantInterface *self;
+ NMSupplicantInterfacePrivate *priv;
+ guint id;
+
+ g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (smgr), NULL);
+ g_return_val_if_fail (ifname != NULL, NULL);
+
+ self = g_object_new (NM_TYPE_SUPPLICANT_INTERFACE, NULL);
+ if (self) {
+ priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+
+ priv->smgr = g_object_ref (smgr);
+ id = g_signal_connect (priv->smgr,
+ "notify::" NM_SUPPLICANT_MANAGER_AVAILABLE,
+ G_CALLBACK (smgr_avail_cb),
+ self);
+ priv->smgr_avail_id = id;
+
+ priv->dev = g_strdup (ifname);
+ priv->is_wireless = is_wireless;
+
+ if (start_now)
+ interface_add (self, priv->is_wireless);
+ }
+
+ return self;
+}
+
+static void
+nm_supplicant_interface_init (NMSupplicantInterface * self)
+{
+ NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (self);
+ DBusGConnection *bus;
+
+ priv->state = NM_SUPPLICANT_INTERFACE_STATE_INIT;
+ priv->assoc_pcalls = nm_call_store_new ();
+ priv->other_pcalls = nm_call_store_new ();
+ priv->dbus_mgr = nm_dbus_manager_get ();
+
+ bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
+ priv->wpas_proxy = dbus_g_proxy_new_for_name (bus,
+ WPAS_DBUS_SERVICE,
+ WPAS_DBUS_PATH,
+ WPAS_DBUS_INTERFACE);
+}
+
+static void
+set_property (GObject *object,
+ guint prop_id,
+ const GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break;
}
- return "unknown";
+}
+
+static void
+get_property (GObject *object,
+ guint prop_id,
+ GValue *value,
+ GParamSpec *pspec)
+{
+ switch (prop_id) {
+ case PROP_SCANNING:
+ g_value_set_boolean (value, NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object)->scanning);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+dispose (GObject *object)
+{
+ NMSupplicantInterfacePrivate *priv = NM_SUPPLICANT_INTERFACE_GET_PRIVATE (object);
+
+ if (priv->disposed) {
+ G_OBJECT_CLASS (nm_supplicant_interface_parent_class)->dispose (object);
+ return;
+ }
+ priv->disposed = TRUE;
+
+ /* Cancel pending calls before unrefing the dbus manager */
+ cancel_all_callbacks (priv->other_pcalls);
+ nm_call_store_destroy (priv->other_pcalls);
+
+ cancel_all_callbacks (priv->assoc_pcalls);
+ nm_call_store_destroy (priv->assoc_pcalls);
+
+ if (priv->props_proxy)
+ g_object_unref (priv->props_proxy);
+
+ if (priv->iface_proxy)
+ g_object_unref (priv->iface_proxy);
+
+ g_free (priv->net_path);
+
+ if (priv->wpas_proxy)
+ g_object_unref (priv->wpas_proxy);
+
+ if (priv->smgr) {
+ if (priv->smgr_avail_id)
+ g_signal_handler_disconnect (priv->smgr, priv->smgr_avail_id);
+ g_object_unref (priv->smgr);
+ }
+
+ g_free (priv->dev);
+
+ if (priv->dbus_mgr)
+ g_object_unref (priv->dbus_mgr);
+
+ if (priv->cfg)
+ g_object_unref (priv->cfg);
+
+ g_free (priv->object_path);
+
+ /* Chain up to the parent class */
+ G_OBJECT_CLASS (nm_supplicant_interface_parent_class)->dispose (object);
+}
+
+static void
+nm_supplicant_interface_class_init (NMSupplicantInterfaceClass *klass)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+ g_type_class_add_private (object_class, sizeof (NMSupplicantInterfacePrivate));
+
+ object_class->dispose = dispose;
+ object_class->set_property = set_property;
+ object_class->get_property = get_property;
+
+ /* Properties */
+ g_object_class_install_property (object_class, PROP_SCANNING,
+ g_param_spec_boolean ("scanning",
+ "Scanning",
+ "Scanning",
+ FALSE,
+ G_PARAM_READABLE));
+
+ /* Signals */
+ signals[STATE] =
+ g_signal_new (NM_SUPPLICANT_INTERFACE_STATE,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NMSupplicantInterfaceClass, state),
+ NULL, NULL,
+ _nm_marshal_VOID__UINT_UINT,
+ G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+
+ signals[REMOVED] =
+ g_signal_new (NM_SUPPLICANT_INTERFACE_REMOVED,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NMSupplicantInterfaceClass, removed),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__VOID,
+ G_TYPE_NONE, 0);
+
+ signals[NEW_BSS] =
+ g_signal_new (NM_SUPPLICANT_INTERFACE_NEW_BSS,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NMSupplicantInterfaceClass, new_bss),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__POINTER,
+ G_TYPE_NONE, 1, G_TYPE_POINTER);
+
+ signals[SCAN_DONE] =
+ g_signal_new (NM_SUPPLICANT_INTERFACE_SCAN_DONE,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NMSupplicantInterfaceClass, scan_done),
+ NULL, NULL,
+ g_cclosure_marshal_VOID__BOOLEAN,
+ G_TYPE_NONE, 1, G_TYPE_BOOLEAN);
+
+ signals[CONNECTION_ERROR] =
+ g_signal_new (NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ G_STRUCT_OFFSET (NMSupplicantInterfaceClass, connection_error),
+ NULL, NULL,
+ _nm_marshal_VOID__STRING_STRING,
+ G_TYPE_NONE, 2, G_TYPE_STRING, G_TYPE_STRING);
}
diff --git a/src/supplicant-manager/nm-supplicant-interface.h b/src/supplicant-manager/nm-supplicant-interface.h
index bee5436f5a..44c92f19c6 100644
--- a/src/supplicant-manager/nm-supplicant-interface.h
+++ b/src/supplicant-manager/nm-supplicant-interface.h
@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2006 - 2008 Red Hat, Inc.
+ * Copyright (C) 2006 - 2010 Red Hat, Inc.
* Copyright (C) 2007 - 2008 Novell, Inc.
*/
@@ -26,47 +26,26 @@
#include <dbus/dbus.h>
#include "nm-supplicant-types.h"
-G_BEGIN_DECLS
-
/*
* Supplicant interface states
- * The states are linear, ie INIT -> READY -> DOWN and state may only be
- * changed in one direction. If an interface reaches the DOWN state, it
- * cannot be re-initialized; it must be torn down and a new one created.
- *
- * INIT: interface has been created, but cannot be used yet; it is waiting
- * for pending requests of the supplicant to complete.
- * READY: interface is ready for use
- * DOWN: interface has been removed or has otherwise been made invalid; it
- * must be torn down.
- *
- * Note: LAST is an invalid state and only used for boundary checking.
+ * A mix of wpa_supplicant interface states and internal states.
*/
enum {
NM_SUPPLICANT_INTERFACE_STATE_INIT = 0,
NM_SUPPLICANT_INTERFACE_STATE_STARTING,
NM_SUPPLICANT_INTERFACE_STATE_READY,
+ NM_SUPPLICANT_INTERFACE_STATE_DISCONNECTED,
+ NM_SUPPLICANT_INTERFACE_STATE_INACTIVE,
+ NM_SUPPLICANT_INTERFACE_STATE_SCANNING,
+ NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATING,
+ NM_SUPPLICANT_INTERFACE_STATE_ASSOCIATED,
+ NM_SUPPLICANT_INTERFACE_STATE_4WAY_HANDSHAKE,
+ NM_SUPPLICANT_INTERFACE_STATE_GROUP_HANDSHAKE,
+ NM_SUPPLICANT_INTERFACE_STATE_COMPLETED,
NM_SUPPLICANT_INTERFACE_STATE_DOWN,
NM_SUPPLICANT_INTERFACE_STATE_LAST
};
-
-/*
- * Supplicant interface connection states
- * The wpa_supplicant state for the connection.
- */
-enum {
- NM_SUPPLICANT_INTERFACE_CON_STATE_DISCONNECTED = 0,
- NM_SUPPLICANT_INTERFACE_CON_STATE_INACTIVE,
- NM_SUPPLICANT_INTERFACE_CON_STATE_SCANNING,
- NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATING,
- NM_SUPPLICANT_INTERFACE_CON_STATE_ASSOCIATED,
- NM_SUPPLICANT_INTERFACE_CON_STATE_4WAY_HANDSHAKE,
- NM_SUPPLICANT_INTERFACE_CON_STATE_GROUP_HANDSHAKE,
- NM_SUPPLICANT_INTERFACE_CON_STATE_COMPLETED,
- NM_SUPPLICANT_INTERFACE_CON_STATE_LAST
-};
-
#define NM_TYPE_SUPPLICANT_INTERFACE (nm_supplicant_interface_get_type ())
#define NM_SUPPLICANT_INTERFACE(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SUPPLICANT_INTERFACE, NMSupplicantInterface))
#define NM_SUPPLICANT_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SUPPLICANT_INTERFACE, NMSupplicantInterfaceClass))
@@ -74,6 +53,12 @@ enum {
#define NM_IS_SUPPLICANT_INTERFACE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SUPPLICANT_INTERFACE))
#define NM_SUPPLICANT_INTERFACE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SUPPLICANT_INTERFACE, NMSupplicantInterfaceClass))
+#define NM_SUPPLICANT_INTERFACE_STATE "state"
+#define NM_SUPPLICANT_INTERFACE_REMOVED "removed"
+#define NM_SUPPLICANT_INTERFACE_NEW_BSS "new-bss"
+#define NM_SUPPLICANT_INTERFACE_SCAN_DONE "scan-done"
+#define NM_SUPPLICANT_INTERFACE_CONNECTION_ERROR "connection-error"
+
struct _NMSupplicantInterface {
GObject parent;
};
@@ -91,23 +76,14 @@ typedef struct {
/* interface was removed by the supplicant */
void (*removed) (NMSupplicantInterface * iface);
- /* interface saw a new access point from a scan */
- void (*scanned_ap) (NMSupplicantInterface * iface,
- DBusMessage * message);
+ /* interface saw a new BSS */
+ void (*new_bss) (NMSupplicantInterface *iface,
+ GHashTable *props);
- /* result of a wireless scan request */
- void (*scan_req_result) (NMSupplicantInterface * iface,
+ /* wireless scan is done */
+ void (*scan_done) (NMSupplicantInterface *iface,
gboolean success);
- /* scan results returned from supplicant */
- void (*scan_results) (NMSupplicantInterface * iface,
- guint num_bssids);
-
- /* link state of the device's connection */
- void (*connection_state) (NMSupplicantInterface * iface,
- guint32 new_state,
- guint32 old_state);
-
/* an error occurred during a connection request */
void (*connection_error) (NMSupplicantInterface * iface,
const char * name,
@@ -119,7 +95,8 @@ GType nm_supplicant_interface_get_type (void);
NMSupplicantInterface * nm_supplicant_interface_new (NMSupplicantManager * smgr,
const char *ifname,
- gboolean is_wireless);
+ gboolean is_wireless,
+ gboolean start_now);
gboolean nm_supplicant_interface_set_config (NMSupplicantInterface * iface,
NMSupplicantConfig * cfg);
@@ -128,18 +105,16 @@ void nm_supplicant_interface_disconnect (NMSupplicantInterface * iface);
const char * nm_supplicant_interface_get_device (NMSupplicantInterface * iface);
+const char *nm_supplicant_interface_get_object_path (NMSupplicantInterface * iface);
+
gboolean nm_supplicant_interface_request_scan (NMSupplicantInterface * self);
guint32 nm_supplicant_interface_get_state (NMSupplicantInterface * self);
-guint32 nm_supplicant_interface_get_connection_state (NMSupplicantInterface * self);
-
const char *nm_supplicant_interface_state_to_string (guint32 state);
-const char *nm_supplicant_interface_connection_state_to_string (guint32 state);
-
gboolean nm_supplicant_interface_get_scanning (NMSupplicantInterface *self);
-G_END_DECLS
+const char *nm_supplicant_interface_get_ifname (NMSupplicantInterface *self);
#endif /* NM_SUPPLICANT_INTERFACE_H */
diff --git a/src/supplicant-manager/nm-supplicant-manager.c b/src/supplicant-manager/nm-supplicant-manager.c
index a2cf58eb8f..19ee5730f9 100644
--- a/src/supplicant-manager/nm-supplicant-manager.c
+++ b/src/supplicant-manager/nm-supplicant-manager.c
@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2006 - 2008 Red Hat, Inc.
+ * Copyright (C) 2006 - 2010 Red Hat, Inc.
* Copyright (C) 2007 - 2008 Novell, Inc.
*/
@@ -26,19 +26,7 @@
#include "nm-supplicant-manager.h"
#include "nm-supplicant-interface.h"
#include "nm-dbus-manager.h"
-#include "nm-marshal.h"
#include "nm-logging.h"
-#include "nm-glib-compat.h"
-
-#define SUPPLICANT_POKE_INTERVAL 120
-
-typedef struct {
- NMDBusManager * dbus_mgr;
- guint32 state;
- GSList * ifaces;
- gboolean dispose_has_run;
- guint poke_id;
-} NMSupplicantManagerPrivate;
#define NM_SUPPLICANT_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), \
NM_TYPE_SUPPLICANT_MANAGER, \
@@ -46,298 +34,283 @@ typedef struct {
G_DEFINE_TYPE (NMSupplicantManager, nm_supplicant_manager, G_TYPE_OBJECT)
+/* Properties */
+enum {
+ PROP_0 = 0,
+ PROP_AVAILABLE,
+ LAST_PROP
+};
-static void nm_supplicant_manager_name_owner_changed (NMDBusManager *dbus_mgr,
- const char *name,
- const char *old,
- const char *new,
- gpointer user_data);
+typedef struct {
+ NMDBusManager * dbus_mgr;
+ guint name_owner_id;
+ DBusGProxy * proxy;
+ gboolean running;
+ GHashTable * ifaces;
+ guint die_count_reset_id;
+ guint die_count;
+ gboolean disposed;
+} NMSupplicantManagerPrivate;
-static void nm_supplicant_manager_set_state (NMSupplicantManager * self,
- guint32 new_state);
+/********************************************************************/
-static gboolean nm_supplicant_manager_startup (NMSupplicantManager * self);
+static inline gboolean
+die_count_exceeded (guint32 count)
+{
+ return count > 2;
+}
+NMSupplicantInterface *
+nm_supplicant_manager_iface_get (NMSupplicantManager * self,
+ const char *ifname,
+ gboolean is_wireless)
+{
+ NMSupplicantManagerPrivate *priv;
+ NMSupplicantInterface *iface = NULL;
+ gboolean start_now;
-/* Signals */
-enum {
- STATE, /* change in the manager's state */
- LAST_SIGNAL
-};
-static guint nm_supplicant_manager_signals[LAST_SIGNAL] = { 0 };
+ g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL);
+ g_return_val_if_fail (ifname != NULL, NULL);
+ priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
-NMSupplicantManager *
-nm_supplicant_manager_get (void)
-{
- static NMSupplicantManager * singleton = NULL;
+ iface = g_hash_table_lookup (priv->ifaces, ifname);
+ if (!iface) {
+ /* If we're making the supplicant take a time out for a bit, don't
+ * let the supplicant interface start immediately, just let it hang
+ * around in INIT state until we're ready to talk to the supplicant
+ * again.
+ */
+ start_now = !die_count_exceeded (priv->die_count);
- if (!singleton) {
- singleton = NM_SUPPLICANT_MANAGER (g_object_new (NM_TYPE_SUPPLICANT_MANAGER, NULL));
+ nm_log_dbg (LOGD_SUPPLICANT, "(%s): creating new supplicant interface", ifname);
+ iface = nm_supplicant_interface_new (self, ifname, is_wireless, start_now);
+ if (iface)
+ g_hash_table_insert (priv->ifaces, g_strdup (ifname), iface);
} else {
- g_object_ref (singleton);
+ nm_log_dbg (LOGD_SUPPLICANT, "(%s): returning existing supplicant interface", ifname);
}
- g_assert (singleton);
- return singleton;
+ return iface;
}
-static gboolean
-poke_supplicant_cb (gpointer user_data)
+void
+nm_supplicant_manager_iface_release (NMSupplicantManager *self,
+ NMSupplicantInterface *iface)
{
- NMSupplicantManager *self = NM_SUPPLICANT_MANAGER (user_data);
- NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
- DBusGConnection *g_connection;
- DBusGProxy *proxy;
- const char *tmp = "ignoreme";
-
- g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
- proxy = dbus_g_proxy_new_for_name (g_connection,
- WPAS_DBUS_SERVICE,
- WPAS_DBUS_PATH,
- WPAS_DBUS_INTERFACE);
- if (!proxy) {
- nm_log_warn (LOGD_SUPPLICANT, "Error: could not init wpa_supplicant proxy");
- goto out;
- }
+ NMSupplicantManagerPrivate *priv;
+ const char *ifname, *op;
- nm_log_info (LOGD_SUPPLICANT, "Trying to start the supplicant...");
- dbus_g_proxy_call_no_reply (proxy, "getInterface", G_TYPE_STRING, tmp, G_TYPE_INVALID);
- g_object_unref (proxy);
+ g_return_if_fail (NM_IS_SUPPLICANT_MANAGER (self));
+ g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (iface));
-out:
- /* Reschedule the poke */
- priv->poke_id = g_timeout_add_seconds (SUPPLICANT_POKE_INTERVAL,
- poke_supplicant_cb,
- (gpointer) self);
+ ifname = nm_supplicant_interface_get_ifname (iface);
+ g_assert (ifname);
- return FALSE;
-}
+ priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
-static void
-nm_supplicant_manager_init (NMSupplicantManager * self)
-{
- NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
- gboolean running;
+ g_return_if_fail (g_hash_table_lookup (priv->ifaces, ifname) == iface);
- priv->dispose_has_run = FALSE;
- priv->state = NM_SUPPLICANT_MANAGER_STATE_DOWN;
- priv->dbus_mgr = nm_dbus_manager_get ();
- priv->poke_id = 0;
+ /* Ask wpa_supplicant to remove this interface */
+ op = nm_supplicant_interface_get_object_path (iface);
+ if (priv->running && priv->proxy && op) {
+ dbus_g_proxy_call_no_reply (priv->proxy, "RemoveInterface",
+ DBUS_TYPE_G_OBJECT_PATH, op,
+ G_TYPE_INVALID);
+ }
- running = nm_supplicant_manager_startup (self);
+ g_hash_table_remove (priv->ifaces, ifname);
+}
- g_signal_connect (priv->dbus_mgr,
- "name-owner-changed",
- G_CALLBACK (nm_supplicant_manager_name_owner_changed),
- self);
+gboolean
+nm_supplicant_manager_available (NMSupplicantManager *self)
+{
+ g_return_val_if_fail (self != NULL, FALSE);
+ g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), FALSE);
- if (!running) {
- /* Try to activate the supplicant */
- priv->poke_id = g_idle_add (poke_supplicant_cb, (gpointer) self);
- }
+ if (die_count_exceeded (NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->die_count))
+ return FALSE;
+ return NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->running;
}
static void
-nm_supplicant_manager_dispose (GObject *object)
+set_running (NMSupplicantManager *self, gboolean now_running)
{
- NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (object);
-
- if (priv->dispose_has_run) {
- G_OBJECT_CLASS (nm_supplicant_manager_parent_class)->dispose (object);
- return;
- }
-
- priv->dispose_has_run = TRUE;
-
- if (priv->poke_id) {
- g_source_remove (priv->poke_id);
- priv->poke_id = 0;
- }
-
- if (priv->dbus_mgr) {
- g_object_unref (G_OBJECT (priv->dbus_mgr));
- priv->dbus_mgr = NULL;
- }
+ NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+ gboolean old_available = nm_supplicant_manager_available (self);
- /* Chain up to the parent class */
- G_OBJECT_CLASS (nm_supplicant_manager_parent_class)->dispose (object);
+ priv->running = now_running;
+ if (old_available != nm_supplicant_manager_available (self))
+ g_object_notify (G_OBJECT (self), NM_SUPPLICANT_MANAGER_AVAILABLE);
}
static void
-nm_supplicant_manager_class_init (NMSupplicantManagerClass *klass)
+set_die_count (NMSupplicantManager *self, guint new_die_count)
{
- GObjectClass * object_class = G_OBJECT_CLASS (klass);
+ NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+ gboolean old_available = nm_supplicant_manager_available (self);
- g_type_class_add_private (object_class, sizeof (NMSupplicantManagerPrivate));
+ priv->die_count = new_die_count;
+ if (old_available != nm_supplicant_manager_available (self))
+ g_object_notify (G_OBJECT (self), NM_SUPPLICANT_MANAGER_AVAILABLE);
+}
- object_class->dispose = nm_supplicant_manager_dispose;
-
- /* Signals */
- nm_supplicant_manager_signals[STATE] =
- g_signal_new ("state",
- G_OBJECT_CLASS_TYPE (object_class),
- G_SIGNAL_RUN_LAST,
- G_STRUCT_OFFSET (NMSupplicantManagerClass, state),
- NULL, NULL,
- _nm_marshal_VOID__UINT_UINT,
- G_TYPE_NONE, 2, G_TYPE_UINT, G_TYPE_UINT);
+static gboolean
+wpas_die_count_reset_cb (gpointer user_data)
+{
+ NMSupplicantManager *self = NM_SUPPLICANT_MANAGER (user_data);
+ NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+
+ /* Reset the die count back to zero, which allows use of the supplicant again */
+ priv->die_count_reset_id = 0;
+ set_die_count (self, 0);
+ nm_log_info (LOGD_SUPPLICANT, "wpa_supplicant die count reset");
+ return FALSE;
}
static void
-nm_supplicant_manager_name_owner_changed (NMDBusManager *dbus_mgr,
- const char *name,
- const char *old_owner,
- const char *new_owner,
- gpointer user_data)
+name_owner_changed (NMDBusManager *dbus_mgr,
+ const char *name,
+ const char *old_owner,
+ const char *new_owner,
+ gpointer user_data)
{
- NMSupplicantManager * self = (NMSupplicantManager *) user_data;
+ NMSupplicantManager *self = NM_SUPPLICANT_MANAGER (user_data);
NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
gboolean old_owner_good = (old_owner && strlen (old_owner));
gboolean new_owner_good = (new_owner && strlen (new_owner));
- /* Can't handle the signal if its not from the supplicant service */
+ /* We only care about the supplicant here */
if (strcmp (WPAS_DBUS_SERVICE, name) != 0)
return;
if (!old_owner_good && new_owner_good) {
- gboolean running;
-
- running = nm_supplicant_manager_startup (self);
-
- if (running && priv->poke_id) {
- g_source_remove (priv->poke_id);
- priv->poke_id = 0;
- }
+ nm_log_info (LOGD_SUPPLICANT, "wpa_supplicant started");
+ set_running (self, TRUE);
} else if (old_owner_good && !new_owner_good) {
- nm_supplicant_manager_set_state (self, NM_SUPPLICANT_MANAGER_STATE_DOWN);
+ nm_log_info (LOGD_SUPPLICANT, "wpa_supplicant stopped");
- if (priv->poke_id)
- g_source_remove (priv->poke_id);
-
- /* Poke the supplicant so that it gets activated by dbus system bus
- * activation.
+ /* Reschedule the die count reset timeout. Every time the supplicant
+ * dies we wait 10 seconds before resetting the counter. If the
+ * supplicant died more than twice before the timer is reset, then
+ * we don't try to talk to the supplicant for a while.
*/
- priv->poke_id = g_idle_add (poke_supplicant_cb, (gpointer) self);
+ if (priv->die_count_reset_id)
+ g_source_remove (priv->die_count_reset_id);
+ priv->die_count_reset_id = g_timeout_add_seconds (10, wpas_die_count_reset_cb, self);
+ set_die_count (self, priv->die_count + 1);
+
+ if (die_count_exceeded (priv->die_count)) {
+ nm_log_info (LOGD_SUPPLICANT,
+ "wpa_supplicant die count %d; ignoring for 10 seconds",
+ priv->die_count);
+ }
+
+ set_running (self, FALSE);
}
}
+/*******************************************************************/
-guint32
-nm_supplicant_manager_get_state (NMSupplicantManager * self)
+NMSupplicantManager *
+nm_supplicant_manager_get (void)
{
- g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), FALSE);
+ static NMSupplicantManager *singleton = NULL;
- return NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->state;
+ if (!singleton)
+ singleton = NM_SUPPLICANT_MANAGER (g_object_new (NM_TYPE_SUPPLICANT_MANAGER, NULL));
+ else
+ g_object_ref (singleton);
+
+ g_assert (singleton);
+ return singleton;
}
static void
-nm_supplicant_manager_set_state (NMSupplicantManager * self, guint32 new_state)
+nm_supplicant_manager_init (NMSupplicantManager * self)
{
NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
- guint32 old_state;
-
- if (new_state == priv->state)
- return;
+ DBusGConnection *bus;
- old_state = priv->state;
- priv->state = new_state;
- g_signal_emit (self,
- nm_supplicant_manager_signals[STATE],
- 0,
- priv->state,
- old_state);
+ priv->dbus_mgr = nm_dbus_manager_get ();
+ priv->name_owner_id = g_signal_connect (priv->dbus_mgr,
+ "name-owner-changed",
+ G_CALLBACK (name_owner_changed),
+ self);
+ priv->running = nm_dbus_manager_name_has_owner (priv->dbus_mgr, WPAS_DBUS_SERVICE);
+
+ bus = nm_dbus_manager_get_connection (priv->dbus_mgr);
+ priv->proxy = dbus_g_proxy_new_for_name (bus,
+ WPAS_DBUS_SERVICE,
+ WPAS_DBUS_PATH,
+ WPAS_DBUS_INTERFACE);
+
+ priv->ifaces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_object_unref);
}
-static gboolean
-nm_supplicant_manager_startup (NMSupplicantManager * self)
+static void
+set_property (GObject *object, guint prop_id, const GValue *value, GParamSpec *pspec)
{
- gboolean running;
-
- /* FIXME: convert to pending call */
- running = nm_dbus_manager_name_has_owner (NM_SUPPLICANT_MANAGER_GET_PRIVATE (self)->dbus_mgr,
- WPAS_DBUS_SERVICE);
- if (running)
- nm_supplicant_manager_set_state (self, NM_SUPPLICANT_MANAGER_STATE_IDLE);
-
- return running;
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
}
-NMSupplicantInterface *
-nm_supplicant_manager_get_iface (NMSupplicantManager * self,
- const char *ifname,
- gboolean is_wireless)
+static void
+get_property (GObject *object, guint prop_id, GValue *value, GParamSpec *pspec)
{
- NMSupplicantManagerPrivate *priv;
- NMSupplicantInterface * iface = NULL;
- GSList * elt;
+ switch (prop_id) {
+ case PROP_AVAILABLE:
+ g_value_set_boolean (value, nm_supplicant_manager_available (NM_SUPPLICANT_MANAGER (object)));
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
- g_return_val_if_fail (NM_IS_SUPPLICANT_MANAGER (self), NULL);
- g_return_val_if_fail (ifname != NULL, NULL);
+static void
+dispose (GObject *object)
+{
+ NMSupplicantManagerPrivate *priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (object);
- priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+ if (priv->disposed)
+ goto out;
+ priv->disposed = TRUE;
- /* Ensure we don't already have this interface */
- for (elt = priv->ifaces; elt; elt = g_slist_next (elt)) {
- NMSupplicantInterface * if_tmp = (NMSupplicantInterface *) elt->data;
+ if (priv->die_count_reset_id)
+ g_source_remove (priv->die_count_reset_id);
- if (!strcmp (ifname, nm_supplicant_interface_get_device (if_tmp))) {
- iface = if_tmp;
- break;
- }
+ if (priv->dbus_mgr) {
+ if (priv->name_owner_id)
+ g_signal_handler_disconnect (priv->dbus_mgr, priv->name_owner_id);
+ g_object_unref (G_OBJECT (priv->dbus_mgr));
}
- if (!iface) {
- nm_log_dbg (LOGD_SUPPLICANT, "(%s): creating new supplicant interface", ifname);
- iface = nm_supplicant_interface_new (self, ifname, is_wireless);
- if (iface)
- priv->ifaces = g_slist_append (priv->ifaces, iface);
- } else {
- nm_log_dbg (LOGD_SUPPLICANT, "(%s): returning existing supplicant interface", ifname);
- }
+ g_hash_table_destroy (priv->ifaces);
- return iface;
+ if (priv->proxy)
+ g_object_unref (priv->proxy);
+
+out:
+ /* Chain up to the parent class */
+ G_OBJECT_CLASS (nm_supplicant_manager_parent_class)->dispose (object);
}
-void
-nm_supplicant_manager_release_iface (NMSupplicantManager * self,
- NMSupplicantInterface * iface)
+static void
+nm_supplicant_manager_class_init (NMSupplicantManagerClass *klass)
{
- NMSupplicantManagerPrivate *priv;
- GSList * elt;
+ GObjectClass *object_class = G_OBJECT_CLASS (klass);
- g_return_if_fail (NM_IS_SUPPLICANT_MANAGER (self));
- g_return_if_fail (NM_IS_SUPPLICANT_INTERFACE (iface));
-
- priv = NM_SUPPLICANT_MANAGER_GET_PRIVATE (self);
+ g_type_class_add_private (object_class, sizeof (NMSupplicantManagerPrivate));
- for (elt = priv->ifaces; elt; elt = g_slist_next (elt)) {
- NMSupplicantInterface * if_tmp = (NMSupplicantInterface *) elt->data;
-
- if (if_tmp == iface) {
- /* Remove the iface from the supplicant manager's list and
- * dereference to match additional reference in get_iface.
- */
- priv->ifaces = g_slist_remove_link (priv->ifaces, elt);
- g_slist_free_1 (elt);
- g_object_unref (iface);
- break;
- }
- }
-}
+ object_class->get_property = get_property;
+ object_class->set_property = set_property;
+ object_class->dispose = dispose;
-const char *
-nm_supplicant_manager_state_to_string (guint32 state)
-{
- switch (state) {
- case NM_SUPPLICANT_MANAGER_STATE_DOWN:
- return "down";
- case NM_SUPPLICANT_MANAGER_STATE_IDLE:
- return "idle";
- default:
- break;
- }
- return "unknown";
+ g_object_class_install_property (object_class, PROP_AVAILABLE,
+ g_param_spec_boolean (NM_SUPPLICANT_MANAGER_AVAILABLE,
+ "Available",
+ "Available",
+ FALSE,
+ G_PARAM_READABLE));
}
-
diff --git a/src/supplicant-manager/nm-supplicant-manager.h b/src/supplicant-manager/nm-supplicant-manager.h
index fef2a77444..9e2f3b21b1 100644
--- a/src/supplicant-manager/nm-supplicant-manager.h
+++ b/src/supplicant-manager/nm-supplicant-manager.h
@@ -26,30 +26,13 @@
#include "nm-supplicant-types.h"
#include "nm-device.h"
-#define WPAS_DBUS_SERVICE "fi.epitest.hostap.WPASupplicant"
-#define WPAS_DBUS_PATH "/fi/epitest/hostap/WPASupplicant"
-#define WPAS_DBUS_INTERFACE "fi.epitest.hostap.WPASupplicant"
+#define WPAS_DBUS_SERVICE "fi.w1.wpa_supplicant1"
+#define WPAS_DBUS_PATH "/fi/w1/wpa_supplicant1"
+#define WPAS_DBUS_INTERFACE "fi.w1.wpa_supplicant1"
G_BEGIN_DECLS
-/*
- * Supplicant manager states
- * Either state may transition to the other state at any time.
- *
- * DOWN: supplicant manager has been created, but cannot be used; the supplicant
- * is either not running or has not yet been fully initialized.
- * IDLE: supplicant manager is ready for use
- *
- * Note: LAST is an invalid state and only used for boundary checking.
- */
-enum {
- NM_SUPPLICANT_MANAGER_STATE_DOWN = 0,
- NM_SUPPLICANT_MANAGER_STATE_IDLE,
- NM_SUPPLICANT_MANAGER_STATE_LAST
-};
-
-
#define NM_TYPE_SUPPLICANT_MANAGER (nm_supplicant_manager_get_type ())
#define NM_SUPPLICANT_MANAGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_SUPPLICANT_MANAGER, NMSupplicantManager))
#define NM_SUPPLICANT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), NM_TYPE_SUPPLICANT_MANAGER, NMSupplicantManagerClass))
@@ -57,6 +40,8 @@ enum {
#define NM_IS_SUPPLICANT_MANAGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), NM_TYPE_SUPPLICANT_MANAGER))
#define NM_SUPPLICANT_MANAGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_SUPPLICANT_MANAGER, NMSupplicantManagerClass))
+#define NM_SUPPLICANT_MANAGER_AVAILABLE "available"
+
struct _NMSupplicantManager
{
GObject parent;
@@ -65,24 +50,19 @@ struct _NMSupplicantManager
typedef struct
{
GObjectClass parent;
-
- /* class members */
- void (* state) (NMSupplicantManager * mgr, guint32 new_state, guint32 old_state);
} NMSupplicantManagerClass;
GType nm_supplicant_manager_get_type (void);
-NMSupplicantManager * nm_supplicant_manager_get (void);
-
-guint32 nm_supplicant_manager_get_state (NMSupplicantManager * mgr);
+NMSupplicantManager *nm_supplicant_manager_get (void);
-NMSupplicantInterface * nm_supplicant_manager_get_iface (NMSupplicantManager * mgr,
- const char *ifname,
- gboolean is_wireless);
+NMSupplicantInterface *nm_supplicant_manager_iface_get (NMSupplicantManager *mgr,
+ const char *ifname,
+ gboolean is_wireless);
-void nm_supplicant_manager_release_iface (NMSupplicantManager * mgr,
- NMSupplicantInterface * iface);
+void nm_supplicant_manager_iface_release (NMSupplicantManager *mgr,
+ NMSupplicantInterface *iface);
-const char *nm_supplicant_manager_state_to_string (guint32 state);
+gboolean nm_supplicant_manager_available (NMSupplicantManager *mgr);
#endif /* NM_SUPPLICANT_MANAGER_H */
diff --git a/src/supplicant-manager/nm-supplicant-settings-verify.c b/src/supplicant-manager/nm-supplicant-settings-verify.c
index 283346559b..c65af4dee5 100644
--- a/src/supplicant-manager/nm-supplicant-settings-verify.c
+++ b/src/supplicant-manager/nm-supplicant-settings-verify.c
@@ -123,6 +123,7 @@ static const struct Opt opt_table[] = {
{ "engine_id", TYPE_BYTES, 0, 0, FALSE, NULL },
{ "key_id", TYPE_BYTES, 0, 0, FALSE, NULL },
{ "fragment_size", TYPE_INT, 1, 2000, FALSE, NULL },
+ { "proactive_key_caching", TYPE_INT, 0, 1, FALSE, NULL },
};
diff --git a/src/system-settings/Makefile.am b/src/system-settings/Makefile.am
index 3b616b9084..0b92228f57 100644
--- a/src/system-settings/Makefile.am
+++ b/src/system-settings/Makefile.am
@@ -54,7 +54,7 @@ libsystem_settings_la_LIBADD = \
libsystem_settings_la_LDFLAGS = -rdynamic
nm-settings-system-glue.h: $(top_srcdir)/introspection/nm-settings-system.xml
- dbus-binding-tool --prefix=nm_settings_system --mode=glib-server --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_settings_system --mode=glib-server --output=$@ $<
CLEANFILES = \
$(BUILT_SOURCES)
diff --git a/src/system-settings/nm-default-wired-connection.c b/src/system-settings/nm-default-wired-connection.c
index 54e00d6273..0d19dea014 100644
--- a/src/system-settings/nm-default-wired-connection.c
+++ b/src/system-settings/nm-default-wired-connection.c
@@ -163,6 +163,7 @@ constructor (GType type,
NM_SETTING_CONNECTION_AUTOCONNECT, TRUE,
NM_SETTING_CONNECTION_UUID, uuid,
NM_SETTING_CONNECTION_READ_ONLY, priv->read_only,
+ NM_SETTING_CONNECTION_TIMESTAMP, (guint64) time (NULL),
NULL);
g_free (id);
diff --git a/src/system-settings/nm-polkit-helpers.h b/src/system-settings/nm-polkit-helpers.h
index c26fcc2c9b..a37c2eebaa 100644
--- a/src/system-settings/nm-polkit-helpers.h
+++ b/src/system-settings/nm-polkit-helpers.h
@@ -16,12 +16,13 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* (C) Copyright 2008 Novell, Inc.
- * (C) Copyright 2008 Red Hat, Inc.
+ * (C) Copyright 2008 - 2010 Red Hat, Inc.
*/
#ifndef NM_POLKIT_HELPERS_H
#define NM_POLKIT_HELPERS_H
+#include <config.h>
#include <polkit/polkit.h>
#define NM_SYSCONFIG_POLICY_ACTION_CONNECTION_MODIFY "org.freedesktop.network-manager-settings.system.modify"
@@ -29,4 +30,18 @@
#define NM_SYSCONFIG_POLICY_ACTION_WIFI_SHARE_OPEN "org.freedesktop.network-manager-settings.system.wifi.share.open"
#define NM_SYSCONFIG_POLICY_ACTION_HOSTNAME_MODIFY "org.freedesktop.network-manager-settings.system.hostname.modify"
+/* Fix for polkit 0.97 and later */
+#if !HAVE_POLKIT_AUTHORITY_GET_SYNC
+static inline PolkitAuthority *
+polkit_authority_get_sync (GCancellable *cancellable, GError **error)
+{
+ PolkitAuthority *authority;
+
+ authority = polkit_authority_get ();
+ if (!authority)
+ g_set_error (error, 0, 0, "failed to get the PolicyKit authority");
+ return authority;
+}
+#endif
+
#endif /* NM_POLKIT_HELPERS_H */
diff --git a/src/system-settings/nm-sysconfig-connection.c b/src/system-settings/nm-sysconfig-connection.c
index 09cec4000f..73906d20a7 100644
--- a/src/system-settings/nm-sysconfig-connection.c
+++ b/src/system-settings/nm-sysconfig-connection.c
@@ -16,7 +16,7 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* (C) Copyright 2008 Novell, Inc.
- * (C) Copyright 2008 - 2009 Red Hat, Inc.
+ * (C) Copyright 2008 - 2010 Red Hat, Inc.
*/
#include <NetworkManager.h>
@@ -209,7 +209,7 @@ get_secrets (NMSettingsConnectionInterface *connection,
setting = nm_connection_get_setting_by_name (priv->secrets, setting_name);
if (!setting) {
error = g_error_new (NM_SETTINGS_INTERFACE_ERROR,
- NM_SETTINGS_INTERFACE_ERROR_INVALID_CONNECTION,
+ NM_SETTINGS_INTERFACE_ERROR_INVALID_SETTING,
"%s.%d - Connection didn't have requested setting '%s'.",
__FILE__, __LINE__, setting_name);
(*callback) (connection, NULL, error, user_data);
@@ -612,10 +612,14 @@ static void
nm_sysconfig_connection_init (NMSysconfigConnection *self)
{
NMSysconfigConnectionPrivate *priv = NM_SYSCONFIG_CONNECTION_GET_PRIVATE (self);
+ GError *error = NULL;
- priv->authority = polkit_authority_get ();
+ priv->authority = polkit_authority_get_sync (NULL, NULL);
if (!priv->authority) {
- nm_log_err (LOGD_SYS_SET, "%s: error creating PolicyKit authority");
+ nm_log_warn (LOGD_SYS_SET, "failed to create PolicyKit authority: (%d) %s",
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
}
}
diff --git a/src/system-settings/nm-sysconfig-settings.c b/src/system-settings/nm-sysconfig-settings.c
index 4bcfb04bc7..d929813f1b 100644
--- a/src/system-settings/nm-sysconfig-settings.c
+++ b/src/system-settings/nm-sysconfig-settings.c
@@ -259,6 +259,7 @@ get_plugin (NMSysconfigSettings *self, guint32 capability)
return NULL;
}
+/* Returns an allocated string which the caller owns and must eventually free */
char *
nm_sysconfig_settings_get_hostname (NMSysconfigSettings *self)
{
@@ -281,7 +282,7 @@ nm_sysconfig_settings_get_hostname (NMSysconfigSettings *self)
}
}
- return hostname;
+ return NULL;
}
static void
@@ -414,7 +415,7 @@ load_plugins (NMSysconfigSettings *self, const char *plugins, GError **error)
for (iter = plist; *iter; iter++) {
GModule *plugin;
char *full_name, *path;
- const char *pname = *iter;
+ const char *pname = g_strstrip (*iter);
GObject *obj;
GObject * (*factory_func) (void);
@@ -1077,6 +1078,11 @@ is_mac_auto_wired_blacklisted (NMSysconfigSettings *self, const GByteArray *mac)
for (iter = list; iter && *iter; iter++) {
struct ether_addr *candidate;
+ if (strcmp(g_strstrip(*iter), "*") == 0) {
+ found = TRUE;
+ break;
+ }
+
candidate = ether_aton (*iter);
if (candidate && !memcmp (mac->data, candidate->ether_addr_octet, ETH_ALEN)) {
found = TRUE;
@@ -1136,13 +1142,19 @@ default_wired_deleted (NMDefaultWiredConnection *wired,
g_key_file_load_from_file (config, priv->config_file, G_KEY_FILE_KEEP_COMMENTS, NULL);
list = g_key_file_get_string_list (config, "main", CONFIG_KEY_NO_AUTO_DEFAULT, &len, NULL);
- /* Traverse entire list to get count of # items */
for (iter = list; iter && *iter; iter++) {
struct ether_addr *candidate;
+ if (strcmp(g_strstrip(*iter), "*") == 0) {
+ found = TRUE;
+ break;
+ }
+
candidate = ether_aton (*iter);
- if (candidate && !memcmp (mac->data, candidate->ether_addr_octet, ETH_ALEN))
+ if (candidate && !memcmp (mac->data, candidate->ether_addr_octet, ETH_ALEN)) {
found = TRUE;
+ break;
+ }
}
/* Add this device's MAC to the list */
@@ -1505,16 +1517,21 @@ static void
nm_sysconfig_settings_init (NMSysconfigSettings *self)
{
NMSysconfigSettingsPrivate *priv = NM_SYSCONFIG_SETTINGS_GET_PRIVATE (self);
+ GError *error = NULL;
priv->connections = g_hash_table_new_full (g_direct_hash, g_direct_equal, g_object_unref, NULL);
- priv->authority = polkit_authority_get ();
+ priv->authority = polkit_authority_get_sync (NULL, &error);
if (priv->authority) {
priv->auth_changed_id = g_signal_connect (priv->authority,
"changed",
G_CALLBACK (pk_authority_changed_cb),
self);
- } else
- nm_log_warn (LOGD_SYS_SET, "failed to create PolicyKit authority.");
+ } else {
+ nm_log_warn (LOGD_SYS_SET, "failed to create PolicyKit authority: (%d) %s",
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ }
}
diff --git a/src/system-settings/nm-system-config-interface.h b/src/system-settings/nm-system-config-interface.h
index c5bbaaa917..3daceb89bd 100644
--- a/src/system-settings/nm-system-config-interface.h
+++ b/src/system-settings/nm-system-config-interface.h
@@ -15,7 +15,7 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2007 - 2008 Red Hat, Inc.
+ * Copyright (C) 2007 - 2010 Red Hat, Inc.
* Copyright (C) 2008 Novell, Inc.
*/
@@ -107,6 +107,12 @@ struct _NMSystemConfigInterface {
*
* Method: mac Data: device MAC address formatted with leading zeros and
* lowercase letters, like 00:0a:0b:0c:0d:0e
+ *
+ * Method: s390-subchannels Data: string of 2 or 3 s390 subchannels
+ * separated by commas (,) that identify the
+ * device, like "0.0.09a0,0.0.09a1,0.0.09a2".
+ * The string may contain only the following
+ * characters: [a-fA-F0-9,.]
*/
GSList * (*get_unmanaged_specs) (NMSystemConfigInterface *config);
diff --git a/src/tests/test-dhcp-options.c b/src/tests/test-dhcp-options.c
index dd1f914959..aa85f87e70 100644
--- a/src/tests/test-dhcp-options.c
+++ b/src/tests/test-dhcp-options.c
@@ -250,300 +250,406 @@ test_wins_options (const char *client)
g_hash_table_destroy (options);
}
-static Option classless_routes_options[] = {
- /* For dhclient */
- { "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 8 10 10 17 66 41" },
- /* For dhcpcd */
- { "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.0.0/8 10.17.66.41" },
- { NULL, NULL }
-};
+static void
+ip4_test_route (const char *test,
+ NMIP4Config *ip4_config,
+ guint route_num,
+ const char *expected_dest,
+ const char *expected_gw,
+ guint expected_prefix)
+{
+ NMIP4Route *route;
+ struct in_addr tmp;
+
+ route = nm_ip4_config_get_route (ip4_config, route_num);
+ ASSERT (inet_pton (AF_INET, expected_dest, &tmp) > 0,
+ test, "couldn't convert expected route destination #1");
+ ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr,
+ test, "unexpected route %d destination", route_num + 1);
+
+ ASSERT (inet_pton (AF_INET, expected_gw, &tmp) > 0,
+ test, "couldn't convert expected route next hop %d",
+ route_num + 1);
+ ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr,
+ test, "unexpected route %d next hop", route_num + 1);
+
+ ASSERT (nm_ip4_route_get_prefix (route) == expected_prefix,
+ test, "unexpected route %d prefix", route_num + 1);
+ ASSERT (nm_ip4_route_get_metric (route) == 0,
+ test, "unexpected route %d metric", route_num + 1);
+}
static void
-test_classless_static_routes (const char *client)
+ip4_test_gateway (const char *test,
+ NMIP4Config *ip4_config,
+ const char *expected_gw)
+{
+ NMIP4Address *addr;
+ struct in_addr tmp;
+
+ ASSERT (nm_ip4_config_get_num_addresses (ip4_config) == 1,
+ test, "unexpected number of IP addresses");
+ addr = nm_ip4_config_get_address (ip4_config, 0);
+ ASSERT (inet_pton (AF_INET, expected_gw, &tmp) > 0,
+ test, "couldn't convert expected IP gateway");
+ ASSERT (nm_ip4_address_get_gateway (addr) == tmp.s_addr,
+ test, "unexpected IP gateway");
+}
+
+static void
+test_classless_static_routes_1 (const char *client)
{
GHashTable *options;
NMIP4Config *ip4_config;
- NMIP4Route *route;
- struct in_addr tmp;
const char *expected_route1_dest = "192.168.10.0";
const char *expected_route1_gw = "192.168.1.1";
const char *expected_route2_dest = "10.0.0.0";
const char *expected_route2_gw = "10.17.66.41";
+ static Option data[] = {
+ /* dhclient custom format */
+ { "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 8 10 10 17 66 41" },
+ { NULL, NULL }
+ };
options = fill_table (generic_options, NULL);
- options = fill_table (classless_routes_options, options);
+ options = fill_table (data, options);
ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
ASSERT (ip4_config != NULL,
- "dhcp-rfc3442", "failed to parse DHCP4 options");
+ "dhcp-classless-1", "failed to parse DHCP4 options");
/* IP4 routes */
ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2,
- "dhcp-rfc3442", "unexpected number of IP routes");
+ "dhcp-classless-1", "unexpected number of IP routes");
+ ip4_test_route ("dhcp-classless-1", ip4_config, 0,
+ expected_route1_dest, expected_route1_gw, 24);
+ ip4_test_route ("dhcp-classless-1", ip4_config, 1,
+ expected_route2_dest, expected_route2_gw, 8);
- /* Route #1 */
- route = nm_ip4_config_get_route (ip4_config, 0);
- ASSERT (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0,
- "dhcp-rfc3442", "couldn't convert expected route destination #1");
- ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr,
- "dhcp-rfc3442", "unexpected route #1 destination");
-
- ASSERT (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0,
- "dhcp-rfc3442", "couldn't convert expected route next hop #1");
- ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr,
- "dhcp-rfc3442", "unexpected route #1 next hop");
+ g_hash_table_destroy (options);
+}
- ASSERT (nm_ip4_route_get_prefix (route) == 24,
- "dhcp-rfc3442", "unexpected route #1 prefix");
- ASSERT (nm_ip4_route_get_metric (route) == 0,
- "dhcp-rfc3442", "unexpected route #1 metric");
+static void
+test_classless_static_routes_2 (const char *client)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const char *expected_route1_dest = "192.168.10.0";
+ const char *expected_route1_gw = "192.168.1.1";
+ const char *expected_route2_dest = "10.0.0.0";
+ const char *expected_route2_gw = "10.17.66.41";
+ static Option data[] = {
+ /* dhcpcd format */
+ { "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.0.0/8 10.17.66.41" },
+ { NULL, NULL }
+ };
- /* Route #2 */
- route = nm_ip4_config_get_route (ip4_config, 1);
- ASSERT (inet_pton (AF_INET, expected_route2_dest, &tmp) > 0,
- "dhcp-rfc3442", "couldn't convert expected route destination #2");
- ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr,
- "dhcp-rfc3442", "unexpected route #2 destination");
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
- ASSERT (inet_pton (AF_INET, expected_route2_gw, &tmp) > 0,
- "dhcp-rfc3442", "couldn't convert expected route next hop #2");
- ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr,
- "dhcp-rfc3442", "unexpected route #2 next hop");
+ ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
+ ASSERT (ip4_config != NULL,
+ "dhcp-classless-2", "failed to parse DHCP4 options");
- ASSERT (nm_ip4_route_get_prefix (route) == 8,
- "dhcp-rfc3442", "unexpected route #2 prefix");
- ASSERT (nm_ip4_route_get_metric (route) == 0,
- "dhcp-rfc3442", "unexpected route #2 metric");
+ /* IP4 routes */
+ ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2,
+ "dhcp-classless-2", "unexpected number of IP routes");
+ ip4_test_route ("dhcp-classless-2", ip4_config, 0,
+ expected_route1_dest, expected_route1_gw, 24);
+ ip4_test_route ("dhcp-classless-2", ip4_config, 1,
+ expected_route2_dest, expected_route2_gw, 8);
g_hash_table_destroy (options);
}
-static Option invalid_classless_routes1[] = {
- /* For dhclient */
- { "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 45 10 17 66 41" },
- /* For dhcpcd */
- { "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.adfadf/44 10.17.66.41" },
- { NULL, NULL }
-};
+static void
+test_fedora_dhclient_classless_static_routes (const char *client)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const char *expected_route1_dest = "129.210.177.128";
+ const char *expected_route1_gw = "192.168.0.113";
+ const char *expected_route2_dest = "2.0.0.0";
+ const char *expected_route2_gw = "10.34.255.6";
+ const char *expected_gateway = "192.168.0.113";
+ static Option data[] = {
+ /* Fedora dhclient format */
+ { "new_classless_static_routes", "0 192.168.0.113 25.129.210.177.132 192.168.0.113 7.2 10.34.255.6" },
+ { NULL, NULL }
+ };
+
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
+
+ ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
+ ASSERT (ip4_config != NULL,
+ "dhcp-fedora-dhclient-classless", "failed to parse DHCP4 options");
+
+ /* IP4 routes */
+ ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2,
+ "dhcp-fedora-dhclient-classless", "unexpected number of IP routes");
+ ip4_test_route ("dhcp-fedora-dhclient-classless", ip4_config, 0,
+ expected_route1_dest, expected_route1_gw, 25);
+ ip4_test_route ("dhcp-fedora-dhclient-classless", ip4_config, 1,
+ expected_route2_dest, expected_route2_gw, 7);
+
+ /* Gateway */
+ ip4_test_gateway ("dhcp-fedora-dhclient-classless", ip4_config, expected_gateway);
+
+ g_hash_table_destroy (options);
+}
static void
-test_invalid_classless_routes1 (const char *client)
+test_dhclient_invalid_classless_routes_1 (const char *client)
{
GHashTable *options;
NMIP4Config *ip4_config;
- NMIP4Route *route;
- struct in_addr tmp;
const char *expected_route1_dest = "192.168.10.0";
const char *expected_route1_gw = "192.168.1.1";
+ static Option data[] = {
+ /* dhclient format */
+ { "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 45 10 17 66 41" },
+ { NULL, NULL }
+ };
options = fill_table (generic_options, NULL);
- options = fill_table (invalid_classless_routes1, options);
+ options = fill_table (data, options);
ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
ASSERT (ip4_config != NULL,
- "dhcp-rfc3442-invalid-1", "failed to parse DHCP4 options");
+ "dhcp-dhclient-classless-invalid-1", "failed to parse DHCP4 options");
/* IP4 routes */
ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 1,
- "dhcp-rfc3442-invalid-1", "unexpected number of IP routes");
+ "dhcp-dhclient-classless-invalid-1", "unexpected number of IP routes");
- /* Route #1 */
- route = nm_ip4_config_get_route (ip4_config, 0);
- ASSERT (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0,
- "dhcp-rfc3442-invalid-1", "couldn't convert expected route destination #1");
- ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr,
- "dhcp-rfc3442-invalid-1", "unexpected route #1 destination");
+ ip4_test_route ("dhcp-dhclient-classless-invalid-1", ip4_config, 0,
+ expected_route1_dest, expected_route1_gw, 24);
- ASSERT (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0,
- "dhcp-rfc3442-invalid-1", "couldn't convert expected route next hop #1");
- ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr,
- "dhcp-rfc3442-invalid-1", "unexpected route #1 next hop");
+ g_hash_table_destroy (options);
+}
- ASSERT (nm_ip4_route_get_prefix (route) == 24,
- "dhcp-rfc3442-invalid-1", "unexpected route #1 prefix");
- ASSERT (nm_ip4_route_get_metric (route) == 0,
- "dhcp-rfc3442-invalid-1", "unexpected route #1 metric");
+static void
+test_dhcpcd_invalid_classless_routes_1 (const char *client)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const char *expected_route1_dest = "10.1.1.5";
+ const char *expected_route1_gw = "10.1.1.1";
+ const char *expected_route2_dest = "100.99.88.56";
+ const char *expected_route2_gw = "10.1.1.1";
+ static Option data[] = {
+ /* dhcpcd format */
+ { "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 10.0.adfadf/44 10.17.66.41" },
+ { NULL, NULL }
+ };
+
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
+
+ ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
+ ASSERT (ip4_config != NULL,
+ "dhcp-dhcpcd-classless-invalid-1", "failed to parse DHCP4 options");
+
+ /* Test falling back to old-style static routes if the classless static
+ * routes are invalid.
+ */
+ ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2,
+ "dhcp-dhcpcdp-classless-invalid-1", "unexpected number of routes");
+ ip4_test_route ("dhcp-dhcpcdp-classless-invalid-1", ip4_config, 0,
+ expected_route1_dest, expected_route1_gw, 32);
+ ip4_test_route ("dhcp-dhcpcdp-classless-invalid-1", ip4_config, 1,
+ expected_route2_dest, expected_route2_gw, 32);
g_hash_table_destroy (options);
}
-static Option invalid_classless_routes2[] = {
- /* For dhclient */
- { "new_rfc3442_classless_static_routes", "45 10 17 66 41 24 192 168 10 192 168 1 1" },
- /* For dhcpcd */
- { "new_classless_static_routes", "10.0.adfadf/44 10.17.66.41 192.168.10.0/24 192.168.1.1" },
- { NULL, NULL }
-};
-
static void
-test_invalid_classless_routes2 (const char *client)
+test_dhclient_invalid_classless_routes_2 (const char *client)
{
GHashTable *options;
NMIP4Config *ip4_config;
- NMIP4Route *route;
- struct in_addr tmp;
const char *expected_route1_dest = "10.1.1.5";
const char *expected_route1_gw = "10.1.1.1";
const char *expected_route2_dest = "100.99.88.56";
const char *expected_route2_gw = "10.1.1.1";
+ static Option data[] = {
+ { "new_rfc3442_classless_static_routes", "45 10 17 66 41 24 192 168 10 192 168 1 1" },
+ { NULL, NULL }
+ };
options = fill_table (generic_options, NULL);
- options = fill_table (invalid_classless_routes2, options);
+ options = fill_table (data, options);
ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
ASSERT (ip4_config != NULL,
- "dhcp-rfc3442-invalid-2", "failed to parse DHCP4 options");
+ "dhcp-dhclient-classless-invalid-2", "failed to parse DHCP4 options");
/* Test falling back to old-style static routes if the classless static
* routes are invalid.
*/
-
- /* Routes */
ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2,
- "dhcp-rfc3442-invalid-2", "unexpected number of routes");
+ "dhcp-dhclient-classless-invalid-2", "unexpected number of routes");
+ ip4_test_route ("dhcp-dhclient-classless-invalid-2", ip4_config, 0,
+ expected_route1_dest, expected_route1_gw, 32);
+ ip4_test_route ("dhcp-dhclient-classless-invalid-2", ip4_config, 1,
+ expected_route2_dest, expected_route2_gw, 32);
- /* Route #1 */
- route = nm_ip4_config_get_route (ip4_config, 0);
- ASSERT (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0,
- "dhcp-rfc3442-invalid-2", "couldn't convert expected route destination #1");
- ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr,
- "dhcp-rfc3442-invalid-2", "unexpected route #1 destination");
+ g_hash_table_destroy (options);
+}
- ASSERT (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0,
- "dhcp-rfc3442-invalid-2", "couldn't convert expected route next hop #1");
- ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr,
- "dhcp-rfc3442-invalid-2", "unexpected route #1 next hop");
+static void
+test_dhcpcd_invalid_classless_routes_2 (const char *client)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const char *expected_route1_dest = "10.1.1.5";
+ const char *expected_route1_gw = "10.1.1.1";
+ const char *expected_route2_dest = "100.99.88.56";
+ const char *expected_route2_gw = "10.1.1.1";
+ static Option data[] = {
+ { "new_classless_static_routes", "10.0.adfadf/44 10.17.66.41 192.168.10.0/24 192.168.1.1" },
+ { NULL, NULL }
+ };
- ASSERT (nm_ip4_route_get_prefix (route) == 32,
- "dhcp-rfc3442-invalid-2", "unexpected route #1 prefix");
- ASSERT (nm_ip4_route_get_metric (route) == 0,
- "dhcp-rfc3442-invalid-2", "unexpected route #1 metric");
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
- /* Route #2 */
- route = nm_ip4_config_get_route (ip4_config, 1);
- ASSERT (inet_pton (AF_INET, expected_route2_dest, &tmp) > 0,
- "dhcp-rfc3442-invalid-2", "couldn't convert expected route destination #2");
- ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr,
- "dhcp-rfc3442-invalid-2", "unexpected route #2 destination");
+ ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
+ ASSERT (ip4_config != NULL,
+ "dhcp-dhcpcd-classless-invalid-2", "failed to parse DHCP4 options");
- ASSERT (inet_pton (AF_INET, expected_route2_gw, &tmp) > 0,
- "dhcp-rfc3442-invalid-2", "couldn't convert expected route next hop #2");
- ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr,
- "dhcp-rfc3442-invalid-2", "unexpected route #2 next hop");
+ /* Test falling back to old-style static routes if the classless static
+ * routes are invalid.
+ */
- ASSERT (nm_ip4_route_get_prefix (route) == 32,
- "dhcp-rfc3442-invalid-2", "unexpected route #2 prefix");
- ASSERT (nm_ip4_route_get_metric (route) == 0,
- "dhcp-rfc3442-invalid-2", "unexpected route #2 metric");
+ /* Routes */
+ ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 2,
+ "dhcp-dhcpcd-classless-invalid-2", "unexpected number of routes");
+ ip4_test_route ("dhcp-dhcpcd-classless-invalid-2", ip4_config, 0,
+ expected_route1_dest, expected_route1_gw, 32);
+ ip4_test_route ("dhcp-dhcpcd-classless-invalid-2", ip4_config, 1,
+ expected_route2_dest, expected_route2_gw, 32);
g_hash_table_destroy (options);
}
-static Option invalid_classless_routes3[] = {
- /* For dhclient */
- { "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 32 128 10 17 66 41" },
- /* For dhcpcd */
- { "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 128/32 10.17.66.41" },
- { NULL, NULL }
-};
-
static void
-test_invalid_classless_routes3 (const char *client)
+test_dhclient_invalid_classless_routes_3 (const char *client)
{
GHashTable *options;
NMIP4Config *ip4_config;
- NMIP4Route *route;
- struct in_addr tmp;
const char *expected_route1_dest = "192.168.10.0";
const char *expected_route1_gw = "192.168.1.1";
+ static Option data[] = {
+ { "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 32 128 10 17 66 41" },
+ { NULL, NULL }
+ };
options = fill_table (generic_options, NULL);
- options = fill_table (invalid_classless_routes3, options);
+ options = fill_table (data, options);
ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
ASSERT (ip4_config != NULL,
- "dhcp-rfc3442-invalid-3", "failed to parse DHCP4 options");
+ "dhcp-dhclient-classless-invalid-3", "failed to parse DHCP4 options");
/* IP4 routes */
ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 1,
- "dhcp-rfc3442-invalid-3", "unexpected number of IP routes");
+ "dhcp-dhclient-classless-invalid-3", "unexpected number of IP routes");
+ ip4_test_route ("dhcp-dhclient-classless-invalid-3", ip4_config, 0,
+ expected_route1_dest, expected_route1_gw, 24);
- /* Route #1 */
- route = nm_ip4_config_get_route (ip4_config, 0);
- ASSERT (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0,
- "dhcp-rfc3442-invalid-3", "couldn't convert expected route destination #1");
- ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr,
- "dhcp-rfc3442-invalid-3", "unexpected route #1 destination");
+ g_hash_table_destroy (options);
+}
- ASSERT (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0,
- "dhcp-rfc3442-invalid-3", "couldn't convert expected route next hop #1");
- ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr,
- "dhcp-rfc3442-invalid-3", "unexpected route #1 next hop");
+static void
+test_dhcpcd_invalid_classless_routes_3 (const char *client)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const char *expected_route1_dest = "192.168.10.0";
+ const char *expected_route1_gw = "192.168.1.1";
+ static Option data[] = {
+ { "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 128/32 10.17.66.41" },
+ { NULL, NULL }
+ };
- ASSERT (nm_ip4_route_get_prefix (route) == 24,
- "dhcp-rfc3442-invalid-3", "unexpected route #1 prefix");
- ASSERT (nm_ip4_route_get_metric (route) == 0,
- "dhcp-rfc3442-invalid-3", "unexpected route #1 metric");
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
+
+ ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
+ ASSERT (ip4_config != NULL,
+ "dhcp-dhcpcd-classless-invalid-3", "failed to parse DHCP4 options");
+
+ /* IP4 routes */
+ ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 1,
+ "dhcp-dhcpcd-classless-invalid-3", "unexpected number of IP routes");
+ ip4_test_route ("dhcp-dhcpcd-classless-invalid-3", ip4_config, 0,
+ expected_route1_dest, expected_route1_gw, 24);
g_hash_table_destroy (options);
}
-static Option gw_in_classless_routes[] = {
- /* For dhclient */
- { "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 0 192 2 3 4" },
- /* For dhcpcd */
- { "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 0.0.0.0/0 192.2.3.4" },
- { NULL, NULL }
-};
-
static void
-test_gateway_in_classless_routes (const char *client)
+test_dhclient_gw_in_classless_routes (const char *client)
{
GHashTable *options;
NMIP4Config *ip4_config;
- NMIP4Address *addr;
- NMIP4Route *route;
- struct in_addr tmp;
const char *expected_route1_dest = "192.168.10.0";
const char *expected_route1_gw = "192.168.1.1";
const char *expected_gateway = "192.2.3.4";
+ static Option data[] = {
+ { "new_rfc3442_classless_static_routes", "24 192 168 10 192 168 1 1 0 192 2 3 4" },
+ { NULL, NULL }
+ };
options = fill_table (generic_options, NULL);
- options = fill_table (gw_in_classless_routes, options);
+ options = fill_table (data, options);
ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
ASSERT (ip4_config != NULL,
- "dhcp-rfc3442-gateway", "failed to parse DHCP4 options");
+ "dhcp-dhclient-classless-gateway", "failed to parse DHCP4 options");
/* IP4 routes */
ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 1,
- "dhcp-rfc3442-gateway", "unexpected number of IP routes");
+ "dhcp-dhclient-classless-gateway", "unexpected number of IP routes");
+ ip4_test_route ("dhcp-dhclient-classless-gateway", ip4_config, 0,
+ expected_route1_dest, expected_route1_gw, 24);
- /* Route #1 */
- route = nm_ip4_config_get_route (ip4_config, 0);
- ASSERT (inet_pton (AF_INET, expected_route1_dest, &tmp) > 0,
- "dhcp-rfc3442-gateway", "couldn't convert expected route destination #1");
- ASSERT (nm_ip4_route_get_dest (route) == tmp.s_addr,
- "dhcp-rfc3442-gateway", "unexpected route #1 destination");
+ /* Gateway */
+ ip4_test_gateway ("dhcp-dhclient-classless-gateway", ip4_config, expected_gateway);
- ASSERT (inet_pton (AF_INET, expected_route1_gw, &tmp) > 0,
- "dhcp-rfc3442-gateway", "couldn't convert expected route next hop #1");
- ASSERT (nm_ip4_route_get_next_hop (route) == tmp.s_addr,
- "dhcp-rfc3442-gateway", "unexpected route #1 next hop");
+ g_hash_table_destroy (options);
+}
- ASSERT (nm_ip4_route_get_prefix (route) == 24,
- "dhcp-rfc3442-gateway", "unexpected route #1 prefix");
- ASSERT (nm_ip4_route_get_metric (route) == 0,
- "dhcp-rfc3442-gateway", "unexpected route #1 metric");
+static void
+test_dhcpcd_gw_in_classless_routes (const char *client)
+{
+ GHashTable *options;
+ NMIP4Config *ip4_config;
+ const char *expected_route1_dest = "192.168.10.0";
+ const char *expected_route1_gw = "192.168.1.1";
+ const char *expected_gateway = "192.2.3.4";
+ static Option data[] = {
+ { "new_classless_static_routes", "192.168.10.0/24 192.168.1.1 0.0.0.0/0 192.2.3.4" },
+ { NULL, NULL }
+ };
- /* Address */
- ASSERT (nm_ip4_config_get_num_addresses (ip4_config) == 1,
- "dhcp-rfc3442-gateway", "unexpected number of IP addresses");
- addr = nm_ip4_config_get_address (ip4_config, 0);
- ASSERT (inet_pton (AF_INET, expected_gateway, &tmp) > 0,
- "dhcp-rfc3442-gateway", "couldn't convert expected IP gateway");
- ASSERT (nm_ip4_address_get_gateway (addr) == tmp.s_addr,
- "dhcp-rfc3442-gateway", "unexpected IP gateway");
+ options = fill_table (generic_options, NULL);
+ options = fill_table (data, options);
+
+ ip4_config = nm_dhcp_manager_test_ip4_options_to_config (client, "eth0", options, "rebind");
+ ASSERT (ip4_config != NULL,
+ "dhcp-dhcpcd-classless-gateway", "failed to parse DHCP4 options");
+
+ /* IP4 routes */
+ ASSERT (nm_ip4_config_get_num_routes (ip4_config) == 1,
+ "dhcp-dhcpcd-classless-gateway", "unexpected number of IP routes");
+ ip4_test_route ("dhcp-dhcpcd-classless-gateway", ip4_config, 0,
+ expected_route1_dest, expected_route1_gw, 24);
+
+ /* Gateway */
+ ip4_test_gateway ("dhcp-dhcpcd-classless-gateway", ip4_config, expected_gateway);
g_hash_table_destroy (options);
}
@@ -694,11 +800,17 @@ int main (int argc, char **argv)
test_generic_options (client);
test_wins_options (client);
- test_classless_static_routes (client);
- test_invalid_classless_routes1 (client);
- test_invalid_classless_routes2 (client);
- test_invalid_classless_routes3 (client);
- test_gateway_in_classless_routes (client);
+ test_classless_static_routes_1 (client);
+ test_classless_static_routes_2 (client);
+ test_fedora_dhclient_classless_static_routes (client);
+ test_dhclient_invalid_classless_routes_1 (client);
+ test_dhcpcd_invalid_classless_routes_1 (client);
+ test_dhclient_invalid_classless_routes_2 (client);
+ test_dhcpcd_invalid_classless_routes_2 (client);
+ test_dhclient_invalid_classless_routes_3 (client);
+ test_dhcpcd_invalid_classless_routes_3 (client);
+ test_dhclient_gw_in_classless_routes (client);
+ test_dhcpcd_gw_in_classless_routes (client);
test_escaped_domain_searches (client);
test_invalid_escaped_domain_searches (client);
test_ip4_missing_prefix (client, "192.168.1.10", 24);
diff --git a/src/tests/test-policy-hosts.c b/src/tests/test-policy-hosts.c
index e14f15e5f2..62862e756f 100644
--- a/src/tests/test-policy-hosts.c
+++ b/src/tests/test-policy-hosts.c
@@ -23,46 +23,38 @@
#include "nm-policy-hosts.h"
-#define FALLBACK_HOSTNAME "localhost.localdomain"
+#define DEBUG 1
static void
-test_generic (const char *before,
- const char *after,
- const char *hostname,
- gboolean expect_error)
+test_generic (const char *before, const char *after)
{
- char **lines;
GString *newc;
- GError *error = NULL;
/* Get the new /etc/hosts contents */
- lines = g_strsplit_set (before, "\n\r", 0);
- newc = nm_policy_get_etc_hosts ((const char **) lines,
- strlen (before),
- hostname,
- FALLBACK_HOSTNAME,
- &error);
- g_strfreev (lines);
+ newc = nm_policy_get_etc_hosts (before, strlen (before));
- if (expect_error) {
- g_assert (newc == NULL);
- g_assert (error != NULL);
- g_clear_error (&error);
- } else if (after == NULL) {
+ if (after == NULL) {
/* No change to /etc/hosts required */
+#if DEBUG
+ if (newc != NULL) {
+ g_message ("\n- NEW ---------------------------------\n"
+ "%s"
+ "+ EXPECTED NONE +++++++++++++++++++++++++\n",
+ newc->str);
+ }
+#endif
g_assert (newc == NULL);
- g_assert (error == NULL);
} else {
g_assert (newc != NULL);
- g_assert (error == NULL);
-#if 0
- g_message ("\n--------------------------------------\n"
+#if DEBUG
+ g_message ("\n- NEW ---------------------------------\n"
+ "%s"
+ "+ EXPECTED ++++++++++++++++++++++++++++++\n"
"%s"
- "--------------------------------------",
- newc->str);
+ "^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\n",
+ newc->str, after);
#endif
- g_assert (strlen (newc->str) == strlen (after));
g_assert (strcmp (newc->str, after) == 0);
g_string_free (newc, TRUE);
}
@@ -80,7 +72,7 @@ static const char *generic_before = \
static void
test_hosts_generic (void)
{
- test_generic (generic_before, NULL, "localhost.localdomain", FALSE);
+ test_generic (generic_before, NULL);
}
/*******************************************/
@@ -93,277 +85,76 @@ static const char *generic_no_boilerplate_before = \
static void
test_hosts_generic_no_boilerplate (void)
{
- test_generic (generic_no_boilerplate_before, NULL, "localhost.localdomain", FALSE);
-}
-
-/*******************************************/
-
-static const char *generic_no_boilerplate_no_lh_before = \
- "127.0.0.1 localhost.localdomain\n"
- "::1 localhost6.localdomain6 localhost6\n"
- "127.0.0.1 lcmd.us.intellitxt.com\n";
-
-static const char *generic_no_boilerplate_no_lh_after = \
- "127.0.0.1 localhost\n"
- "127.0.0.1 localhost.localdomain\n"
- "::1 localhost6.localdomain6 localhost6\n"
- "127.0.0.1 lcmd.us.intellitxt.com\n";
-
-static void
-test_hosts_generic_no_boilerplate_no_lh (void)
-{
- test_generic (generic_no_boilerplate_no_lh_before,
- generic_no_boilerplate_no_lh_after,
- "localhost.localdomain",
- FALSE);
-}
-
-/*******************************************/
-
-
-static const char *generic_no_boilerplate_no_lh_no_host_before = \
- "127.0.0.1 localhost.localdomain\n"
- "::1 localhost6.localdomain6 localhost6\n"
- "127.0.0.1 lcmd.us.intellitxt.com\n";
-
-static const char *generic_no_boilerplate_no_lh_no_host_after = \
- "127.0.0.1 comet localhost.localdomain localhost\n"
- "::1 localhost6.localdomain6 localhost6\n"
- "127.0.0.1 lcmd.us.intellitxt.com\n";
-
-static void
-test_hosts_generic_no_boilerplate_no_lh_no_host (void)
-{
- test_generic (generic_no_boilerplate_no_lh_no_host_before,
- generic_no_boilerplate_no_lh_no_host_after,
- "comet",
- FALSE);
-}
-
-/*******************************************/
-static const char *named_generic_before = \
- "# Do not remove the following line, or various programs\n"
- "# that require network functionality will fail.\n"
- "127.0.0.1 playboy localhost\n"
- "::1 localhost6.localdomain6 localhost6\n"
- "127.0.0.1 lcmd.us.intellitxt.com\n";
-
-static void
-test_hosts_named_generic (void)
-{
- test_generic (named_generic_before, NULL, "playboy", FALSE);
-}
-
-/*******************************************/
-
-static const char *named_non127_before = \
- "# Do not remove the following line, or various programs\n"
- "# that require network functionality will fail.\n"
- "127.0.0.1 localhost.localdomain localhost\n"
- "::1 localhost6.localdomain6 localhost6\n"
- "127.0.0.1 lcmd.us.intellitxt.com\n"
- "192.168.1.2 tomcat\n";
-
-static void
-test_hosts_named_non127 (void)
-{
- test_generic (named_non127_before, NULL, "tomcat", FALSE);
-}
-
-/*******************************************/
-
-static const char *named2_non127_before = \
- "# Do not remove the following line, or various programs\n"
- "# that require network functionality will fail.\n"
- "127.0.0.1 localhost.localdomain localhost\n"
- "::1 localhost6.localdomain6 localhost6\n"
- "127.0.0.1 lcmd.us.intellitxt.com\n"
- "192.168.1.2 tomcat\n"
- "127.0.0.1 lcmd.us.intellitxt.com\n"
- "127.0.0.1 srx.main.ebayrtm.com\n"
- "127.0.0.1 cdn5.tribalfusion.com\n";
-
-static void
-test_hosts_named2_non127 (void)
-{
- test_generic (named2_non127_before, NULL, "tomcat", FALSE);
+ test_generic (generic_no_boilerplate_before, NULL);
}
/*******************************************/
-static const char *named_no_lh_before = \
- "# Do not remove the following line, or various programs\n"
- "# that require network functionality will fail.\n"
- "127.0.0.1 localhost.localdomain\n"
- "::1 localhost6.localdomain6 localhost6\n"
- "127.0.0.1 lcmd.us.intellitxt.com\n"
- "192.168.1.2 tomcat\n";
-
-static const char *named_no_lh_after = \
+static const char *leftover_before = \
"# Do not remove the following line, or various programs\n"
"# that require network functionality will fail.\n"
+ "192.168.1.2 comet # Added by NetworkManager\n"
"127.0.0.1 localhost.localdomain localhost\n"
- "::1 localhost6.localdomain6 localhost6\n"
- "127.0.0.1 lcmd.us.intellitxt.com\n"
- "192.168.1.2 tomcat\n";
-
-static void
-test_hosts_named_no_localhost (void)
-{
- test_generic (named_no_lh_before, named_no_lh_after, "tomcat", FALSE);
-}
-
-/*******************************************/
-
-static const char *no_lh_before = \
- "# Do not remove the following line, or various programs\n"
- "# that require network functionality will fail.\n"
- "127.0.0.1 tomcat\n"
- "::1 localhost6.localdomain6 localhost6\n"
+ "::1 localhost6.localdomain6 localhost6\n"
+ "192.168.1.3 comet\n"
+ "3001:abba::3234 comet\n"
+ "\n"
"127.0.0.1 lcmd.us.intellitxt.com\n";
-static const char *no_lh_after = \
+static const char *leftover_after = \
"# Do not remove the following line, or various programs\n"
"# that require network functionality will fail.\n"
"127.0.0.1 localhost.localdomain localhost\n"
- "127.0.0.1 tomcat\n"
- "::1 localhost6.localdomain6 localhost6\n"
+ "::1 localhost6.localdomain6 localhost6\n"
+ "192.168.1.3 comet\n"
+ "3001:abba::3234 comet\n"
+ "\n"
"127.0.0.1 lcmd.us.intellitxt.com\n";
static void
-test_hosts_no_localhost (void)
+test_hosts_leftover (void)
{
- test_generic (no_lh_before, no_lh_after, "tomcat", FALSE);
+ test_generic (leftover_before, leftover_after);
}
/*******************************************/
-static const char *named_last_before = \
+static const char *leftover_double_newline_before = \
"# Do not remove the following line, or various programs\n"
"# that require network functionality will fail.\n"
- "127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 sparcbook.ausil.us\n"
- "::1 localhost localhost.localdomain localhost6 localhost6.localdomain6 sparcbook.ausil.us\n";
-
-static void
-test_hosts_named_last (void)
-{
- test_generic (named_last_before, NULL, "sparcbook.ausil.us", FALSE);
-}
-
-/*******************************************/
-
-static const char *no_host_before = \
- "# Do not remove the following line, or various programs\n"
- "# that require network functionality will fail.\n"
- "::1 localhost6.localdomain6 localhost6\n"
- "\n"
- "127.0.0.1 lcmd.us.intellitxt.com\n"
- "127.0.0.1 srx.main.ebayrtm.com\n"
- "127.0.0.1 cdn5.tribalfusion.com\n"
- "127.0.0.1 a.tribalfusion.com\n";
-
-static const char *no_host_after = \
- "# Do not remove the following line, or various programs\n"
- "# that require network functionality will fail.\n"
- "127.0.0.1 comet localhost.localdomain localhost\n"
- "::1 localhost6.localdomain6 localhost6\n"
+ "192.168.1.2 comet # Added by NetworkManager\n"
+ "127.0.0.1 localhost.localdomain localhost\n"
+ "::1 localhost6.localdomain6 localhost6\n"
+ "192.168.1.3 comet\n"
+ "3001:abba::3234 comet\n"
"\n"
"127.0.0.1 lcmd.us.intellitxt.com\n"
- "127.0.0.1 srx.main.ebayrtm.com\n"
- "127.0.0.1 cdn5.tribalfusion.com\n"
- "127.0.0.1 a.tribalfusion.com\n";
-
-static void
-test_hosts_no_host (void)
-{
- test_generic (no_host_before, no_host_after, "comet", FALSE);
-}
-
-/*******************************************/
+ "\n";
-static const char *long_before = \
+static const char *leftover_double_newline_after = \
"# Do not remove the following line, or various programs\n"
"# that require network functionality will fail.\n"
- "127.0.0.1 localhost.localdomain localhost comet\n"
- "::1 localhost6.localdomain6 localhost6\n"
+ "127.0.0.1 localhost.localdomain localhost\n"
+ "::1 localhost6.localdomain6 localhost6\n"
+ "192.168.1.3 comet\n"
+ "3001:abba::3234 comet\n"
"\n"
"127.0.0.1 lcmd.us.intellitxt.com\n"
- "127.0.0.1 adserver.adtech.de\n"
- "127.0.0.1 a.as-us.falkag.net\n"
- "127.0.0.1 a.as-eu.falkag.net\n"
- "127.0.0.1 ads.doubleclick.com\n"
- "\n"
- "# random comment\n"
- "127.0.0.1 m1.2mdn.net\n"
- "127.0.0.1 ds.serving-sys.com\n"
- "127.0.0.1 pagead2.googlesyndication.com\n"
- "127.0.0.1 ad.doubleclick.com\n"
- "127.0.0.1 ad.doubleclick.net\n"
- "127.0.0.1 oascentral.movietickets.com\n"
- "127.0.0.1 view.atdmt.com\n"
- "127.0.0.1 ads.chumcity.com\n"
- "127.0.0.1 ads.as4x.tmcs.net\n"
- "127.0.0.1 n4403ad.doubleclick.net\n"
- "127.0.0.1 www.assoc-amazon.com\n"
- "127.0.0.1 s25.sitemeter.com\n"
- "127.0.0.1 adlog.com.com\n"
- "127.0.0.1 ahs.laptopmag.com\n"
- "127.0.0.1 altfarm.mediaplex.com\n"
- "127.0.0.1 ads.addynamix.com\n"
- "127.0.0.1 srx.main.ebayrtm.com\n"
- "127.0.0.1 cdn5.tribalfusion.com\n"
- "127.0.0.1 a.tribalfusion.com\n";
-
+ "\n";
static void
-test_hosts_long (void)
+test_hosts_leftover_double_newline (void)
{
- test_generic (long_before, NULL, "comet", FALSE);
+ test_generic (leftover_double_newline_before, leftover_double_newline_after);
}
/*******************************************/
-typedef struct {
- const char *line;
- const char *token;
- gboolean expected;
-} Foo;
-
-static Foo foo[] = {
- /* Using \t here to easily differentiate tabs vs. spaces for testing */
- { "127.0.0.1\tfoobar\tblah", "blah", TRUE },
- { "", "blah", FALSE },
- { "1.1.1.1\tbork\tfoo", "blah", FALSE },
- { "127.0.0.1 foobar\tblah", "blah", TRUE },
- { "127.0.0.1 foobar blah", "blah", TRUE },
- { "127.0.0.1 localhost", "localhost.localdomain", FALSE },
- { "192.168.1.1 blah borkbork", "blah", TRUE },
- { "192.168.1.1 foobar\tblah borkbork", "blah", TRUE },
- { "192.168.1.1\tfoobar\tblah\tborkbork", "blah", TRUE },
- { "192.168.1.1 \tfoobar \tblah \tborkbork\t ", "blah", TRUE },
- { "\t\t\t\t \t\t\tasdfadf a\t\t\t\t\t \t\t\t\t\t ", "blah", FALSE },
- { NULL, NULL, FALSE }
-};
-
-static void
-test_find_token (void)
-{
- Foo *iter = &foo[0];
-
- while (iter->line) {
- gboolean found;
-
- found = nm_policy_hosts_find_token (iter->line, iter->token);
- if (found != iter->expected) {
- g_warning ("find-token: unexpected token result %d for '%s' <= '%s' (expected %d)",
- found, iter->line, iter->token, iter->expected);
- }
- g_assert (found == iter->expected);
- iter++;
- }
-}
-
+#if GLIB_CHECK_VERSION(2,25,12)
+typedef GTestFixtureFunc TCFunc;
+#else
typedef void (*TCFunc)(void);
+#endif
#define TESTCASE(t, d) g_test_create_case (#t, 0, d, NULL, (TCFunc) t, NULL)
@@ -375,19 +166,10 @@ int main (int argc, char **argv)
suite = g_test_get_root ();
- g_test_suite_add (suite, TESTCASE (test_find_token, NULL));
g_test_suite_add (suite, TESTCASE (test_hosts_generic, NULL));
g_test_suite_add (suite, TESTCASE (test_hosts_generic_no_boilerplate, NULL));
- g_test_suite_add (suite, TESTCASE (test_hosts_generic_no_boilerplate_no_lh, NULL));
- g_test_suite_add (suite, TESTCASE (test_hosts_generic_no_boilerplate_no_lh_no_host, NULL));
- g_test_suite_add (suite, TESTCASE (test_hosts_named_generic, NULL));
- g_test_suite_add (suite, TESTCASE (test_hosts_named_non127, NULL));
- g_test_suite_add (suite, TESTCASE (test_hosts_named2_non127, NULL));
- g_test_suite_add (suite, TESTCASE (test_hosts_named_no_localhost, NULL));
- g_test_suite_add (suite, TESTCASE (test_hosts_no_localhost, NULL));
- g_test_suite_add (suite, TESTCASE (test_hosts_named_last, NULL));
- g_test_suite_add (suite, TESTCASE (test_hosts_no_host, NULL));
- g_test_suite_add (suite, TESTCASE (test_hosts_long, NULL));
+ g_test_suite_add (suite, TESTCASE (test_hosts_leftover, NULL));
+ g_test_suite_add (suite, TESTCASE (test_hosts_leftover_double_newline, NULL));
return g_test_run ();
}
diff --git a/src/vpn-manager/Makefile.am b/src/vpn-manager/Makefile.am
index 56e4a4ced3..b0692d70c3 100644
--- a/src/vpn-manager/Makefile.am
+++ b/src/vpn-manager/Makefile.am
@@ -5,7 +5,7 @@ INCLUDES = \
-I${top_srcdir}/src/logging \
-I${top_srcdir}/src \
-I${top_builddir}/marshallers \
- -I${top_srcdir}/src/named-manager \
+ -I${top_srcdir}/src/dns-manager \
-DVPN_NAME_FILES_DIR=\""$(sysconfdir)/NetworkManager/VPN"\"
@@ -32,10 +32,10 @@ libvpn_manager_la_LIBADD = \
$(GLIB_LIBS)
nm-vpn-connection-glue.h: $(top_srcdir)/introspection/nm-vpn-connection.xml
- dbus-binding-tool --prefix=nm_vpn_connection --mode=glib-server --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_vpn_connection --mode=glib-server --output=$@ $<
nm-vpn-plugin-bindings.h: $(top_srcdir)/introspection/nm-vpn-plugin.xml
- dbus-binding-tool --prefix=nm_vpn_plugin --mode=glib-client --output=$@ $<
+ $(AM_V_GEN) dbus-binding-tool --prefix=nm_vpn_plugin --mode=glib-client --output=$@ $<
BUILT_SOURCES = \
diff --git a/src/vpn-manager/nm-vpn-connection.c b/src/vpn-manager/nm-vpn-connection.c
index eee181ef01..cf844992c0 100644
--- a/src/vpn-manager/nm-vpn-connection.c
+++ b/src/vpn-manager/nm-vpn-connection.c
@@ -44,7 +44,7 @@
#include "nm-properties-changed-signal.h"
#include "nm-dbus-glib-types.h"
#include "NetworkManagerUtils.h"
-#include "nm-named-manager.h"
+#include "nm-dns-manager.h"
#include "nm-netlink-monitor.h"
#include "nm-glib-compat.h"
@@ -119,6 +119,7 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
NMVPNConnectionPrivate *priv;
NMActiveConnectionState new_ac_state;
NMVPNConnectionState old_vpn_state;
+ char *ip_iface;
g_return_if_fail (NM_IS_VPN_CONNECTION (connection));
@@ -130,6 +131,11 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
old_vpn_state = priv->vpn_state;
priv->vpn_state = vpn_state;
+ /* Save ip_iface since when the VPN goes down it may get freed
+ * before we're done with it.
+ */
+ ip_iface = g_strdup (priv->ip_iface);
+
/* Set the NMActiveConnection state based on VPN state */
switch (vpn_state) {
case NM_VPN_CONNECTION_STATE_PREPARE:
@@ -166,7 +172,7 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
nm_utils_call_dispatcher ("vpn-up",
priv->connection,
priv->parent_dev,
- priv->ip_iface);
+ ip_iface);
break;
case NM_VPN_CONNECTION_STATE_FAILED:
case NM_VPN_CONNECTION_STATE_DISCONNECTED:
@@ -174,13 +180,14 @@ nm_vpn_connection_set_vpn_state (NMVPNConnection *connection,
nm_utils_call_dispatcher ("vpn-down",
priv->connection,
priv->parent_dev,
- priv->ip_iface);
+ ip_iface);
}
break;
default:
break;
}
+ g_free (ip_iface);
g_object_unref (connection);
}
@@ -375,6 +382,9 @@ print_vpn_config (NMIP4Config *config,
ip_address_to_string (nm_ip4_route_get_next_hop (route)));
}
+ nm_log_info (LOGD_VPN, "Forbid Default Route: %s",
+ nm_ip4_config_get_never_default (config) ? "yes" : "no");
+
num = nm_ip4_config_get_num_nameservers (config);
for (i = 0; i < num; i++) {
nm_log_info (LOGD_VPN, "Internal IP4 DNS: %s",
@@ -520,6 +530,10 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
g_slist_free (routes);
}
+ val = (GValue *) g_hash_table_lookup (config_hash, NM_VPN_PLUGIN_IP4_CONFIG_NEVER_DEFAULT);
+ if (val && G_VALUE_HOLDS_BOOLEAN (val))
+ nm_ip4_config_set_never_default (config, g_value_get_boolean (val));
+
print_vpn_config (config, priv->ip4_internal_gw, priv->ip_iface, priv->banner);
/* Merge in user overrides from the NMConnection's IPv4 setting */
@@ -529,15 +543,15 @@ nm_vpn_connection_ip4_config_get (DBusGProxy *proxy,
nm_system_device_set_up_down_with_iface (priv->ip_iface, TRUE, NULL);
if (nm_system_apply_ip4_config (priv->ip_iface, config, 0, NM_IP4_COMPARE_FLAG_ALL)) {
- NMNamedManager *named_mgr;
+ NMDnsManager *dns_mgr;
/* Add any explicit route to the VPN gateway through the parent device */
priv->gw_route = nm_system_add_ip4_vpn_gateway_route (priv->parent_dev, config);
/* Add the VPN to DNS */
- named_mgr = nm_named_manager_get ();
- nm_named_manager_add_ip4_config (named_mgr, priv->ip_iface, config, NM_NAMED_IP_CONFIG_TYPE_VPN);
- g_object_unref (named_mgr);
+ dns_mgr = nm_dns_manager_get (NULL);
+ nm_dns_manager_add_ip4_config (dns_mgr, priv->ip_iface, config, NM_DNS_IP_CONFIG_TYPE_VPN);
+ g_object_unref (dns_mgr);
priv->ip4_config = config;
@@ -885,12 +899,12 @@ vpn_cleanup (NMVPNConnection *connection)
if (priv->ip4_config) {
NMIP4Config *parent_config;
- NMNamedManager *named_mgr;
+ NMDnsManager *dns_mgr;
/* Remove attributes of the VPN's IP4 Config */
- named_mgr = nm_named_manager_get ();
- nm_named_manager_remove_ip4_config (named_mgr, priv->ip_iface, priv->ip4_config);
- g_object_unref (named_mgr);
+ dns_mgr = nm_dns_manager_get (NULL);
+ nm_dns_manager_remove_ip4_config (dns_mgr, priv->ip_iface, priv->ip4_config);
+ g_object_unref (dns_mgr);
/* Remove any previously added VPN gateway host route */
if (priv->gw_route)
diff --git a/src/vpn-manager/nm-vpn-manager.c b/src/vpn-manager/nm-vpn-manager.c
index 221a8b548c..4b58be0d48 100644
--- a/src/vpn-manager/nm-vpn-manager.c
+++ b/src/vpn-manager/nm-vpn-manager.c
@@ -15,11 +15,12 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2005 - 2008 Red Hat, Inc.
+ * Copyright (C) 2005 - 2010 Red Hat, Inc.
* Copyright (C) 2006 - 2008 Novell, Inc.
*/
#include <string.h>
+#include <gio/gio.h>
#include "nm-vpn-manager.h"
#include "nm-vpn-service.h"
@@ -28,13 +29,18 @@
#include "nm-dbus-manager.h"
#include "NetworkManagerVPN.h"
#include "nm-marshal.h"
+#include "nm-logging.h"
G_DEFINE_TYPE (NMVPNManager, nm_vpn_manager, G_TYPE_OBJECT)
#define NM_VPN_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_MANAGER, NMVPNManagerPrivate))
typedef struct {
- GSList *services;
+ gboolean disposed;
+
+ GHashTable *services;
+ GFileMonitor *monitor;
+ guint monitor_id;
} NMVPNManagerPrivate;
enum {
@@ -81,53 +87,42 @@ nm_vpn_manager_error_get_type (void)
}
-
static NMVPNService *
-nm_vpn_manager_get_service (NMVPNManager *manager, const char *service_name)
+get_service_by_namefile (NMVPNManager *self, const char *namefile)
{
- GSList *iter;
+ NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
+ GHashTableIter iter;
+ gpointer data;
- for (iter = NM_VPN_MANAGER_GET_PRIVATE (manager)->services; iter; iter = iter->next) {
- NMVPNService *service = NM_VPN_SERVICE (iter->data);
+ g_return_val_if_fail (namefile, NULL);
+ g_return_val_if_fail (g_path_is_absolute (namefile), NULL);
- if (!strcmp (service_name, nm_vpn_service_get_name (service)))
- return g_object_ref (service);
- }
+ g_hash_table_iter_init (&iter, priv->services);
+ while (g_hash_table_iter_next (&iter, NULL, &data)) {
+ NMVPNService *candidate = NM_VPN_SERVICE (data);
+ const char *service_namefile;
+ service_namefile = nm_vpn_service_get_name_file (candidate);
+ if (!strcmp (namefile, service_namefile))
+ return candidate;
+ }
return NULL;
}
-static void
-remove_service (gpointer data, GObject *service)
-{
- NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (data);
-
- priv->services = g_slist_remove (priv->services, service);
-}
-
-static void
-nm_vpn_manager_add_service (NMVPNManager *manager, NMVPNService *service)
-{
- NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
-
- priv->services = g_slist_prepend (priv->services, service);
- g_object_weak_ref (G_OBJECT (service), remove_service, manager);
-}
-
static NMVPNConnection *
-find_active_vpn_connection_by_connection (NMVPNManager *manager, NMConnection *connection)
+find_active_vpn_connection_by_connection (NMVPNManager *self, NMConnection *connection)
{
- NMVPNManagerPrivate *priv;
- GSList *iter;
+ NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
+ GHashTableIter iter;
+ gpointer data;
+ GSList *connections, *elt;
- g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL);
+ g_return_val_if_fail (connection, NULL);
g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
- priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
- for (iter = priv->services; iter; iter = g_slist_next (iter)) {
- GSList *connections, *elt;
-
- connections = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
+ g_hash_table_iter_init (&iter, priv->services);
+ while (g_hash_table_iter_next (&iter, NULL, &data)) {
+ connections = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
for (elt = connections; elt; elt = g_slist_next (elt)) {
NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data);
@@ -169,7 +164,7 @@ nm_vpn_manager_activate_connection (NMVPNManager *manager,
NMSettingVPN *vpn_setting;
NMVPNService *service;
NMVPNConnection *vpn = NULL;
- const char *service_type;
+ const char *service_name;
g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL);
g_return_val_if_fail (NM_IS_CONNECTION (connection), NULL);
@@ -199,78 +194,78 @@ nm_vpn_manager_activate_connection (NMVPNManager *manager,
vpn = NULL;
}
- service_type = nm_setting_vpn_get_service_type (vpn_setting);
- service = nm_vpn_manager_get_service (manager, service_type);
+ service_name = nm_setting_vpn_get_service_type (vpn_setting);
+ g_assert (service_name);
+ service = g_hash_table_lookup (NM_VPN_MANAGER_GET_PRIVATE (manager)->services, service_name);
if (!service) {
- service = nm_vpn_service_new (service_type);
- if (service)
- nm_vpn_manager_add_service (manager, service);
- }
-
- if (service) {
- vpn = nm_vpn_service_activate (service, connection, act_request, device, error);
- if (vpn) {
- g_signal_connect (vpn, "vpn-state-changed",
- G_CALLBACK (connection_vpn_state_changed),
- manager);
- }
- } else {
g_set_error (error,
NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_INVALID,
- "%s", "The VPN service was invalid.");
+ "The VPN service '%s' was not installed.",
+ service_name);
+ return NULL;
+ }
+
+ vpn = nm_vpn_service_activate (service, connection, act_request, device, error);
+ if (vpn) {
+ g_signal_connect (vpn, "vpn-state-changed",
+ G_CALLBACK (connection_vpn_state_changed),
+ manager);
}
return vpn;
}
gboolean
-nm_vpn_manager_deactivate_connection (NMVPNManager *manager,
+nm_vpn_manager_deactivate_connection (NMVPNManager *self,
const char *path,
NMVPNConnectionStateReason reason)
{
NMVPNManagerPrivate *priv;
- GSList *iter;
- gboolean found = FALSE;
+ GHashTableIter iter;
+ gpointer data;
+ GSList *active, *elt;
- g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), FALSE);
+ g_return_val_if_fail (self, FALSE);
+ g_return_val_if_fail (NM_IS_VPN_MANAGER (self), FALSE);
g_return_val_if_fail (path != NULL, FALSE);
- priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
- for (iter = priv->services; iter; iter = g_slist_next (iter)) {
- GSList *connections, *elt;
-
- connections = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
- for (elt = connections; elt; elt = g_slist_next (elt)) {
+ priv = NM_VPN_MANAGER_GET_PRIVATE (self);
+ g_hash_table_iter_init (&iter, priv->services);
+ while (g_hash_table_iter_next (&iter, NULL, &data)) {
+ active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
+ for (elt = active; elt; elt = g_slist_next (elt)) {
NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data);
const char *vpn_path;
vpn_path = nm_vpn_connection_get_active_connection_path (vpn);
if (!strcmp (path, vpn_path)) {
nm_vpn_connection_disconnect (vpn, reason);
- found = TRUE;
+ return TRUE;
}
}
}
- return found ? TRUE : FALSE;
+ return FALSE;
}
void
-nm_vpn_manager_add_active_connections (NMVPNManager *manager,
+nm_vpn_manager_add_active_connections (NMVPNManager *self,
NMConnection *filter,
GPtrArray *array)
{
NMVPNManagerPrivate *priv;
- GSList *iter;
+ GHashTableIter iter;
+ gpointer data;
+ GSList *active, *elt;
- g_return_if_fail (NM_IS_VPN_MANAGER (manager));
+ g_return_if_fail (self);
+ g_return_if_fail (NM_IS_VPN_MANAGER (self));
g_return_if_fail (array != NULL);
- priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
- for (iter = priv->services; iter; iter = g_slist_next (iter)) {
- GSList *active, *elt;
-
- active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
+ priv = NM_VPN_MANAGER_GET_PRIVATE (self);
+ g_hash_table_iter_init (&iter, priv->services);
+ while (g_hash_table_iter_next (&iter, NULL, &data)) {
+ active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
for (elt = active; elt; elt = g_slist_next (elt)) {
NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data);
const char *path;
@@ -284,26 +279,155 @@ nm_vpn_manager_add_active_connections (NMVPNManager *manager,
}
GSList *
-nm_vpn_manager_get_active_connections (NMVPNManager *manager)
+nm_vpn_manager_get_active_connections (NMVPNManager *self)
{
NMVPNManagerPrivate *priv;
- GSList *iter;
- GSList *list = NULL;
+ GHashTableIter iter;
+ gpointer data;
+ GSList *list = NULL, *active, *elt;
+
+ g_return_val_if_fail (self, NULL);
+ g_return_val_if_fail (NM_IS_VPN_MANAGER (self), NULL);
+
+ priv = NM_VPN_MANAGER_GET_PRIVATE (self);
+ g_hash_table_iter_init (&iter, priv->services);
+ while (g_hash_table_iter_next (&iter, NULL, &data)) {
+ active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
+ for (elt = active; elt; elt = g_slist_next (elt))
+ list = g_slist_append (list, g_object_ref (G_OBJECT (elt->data)));
+ }
+ return list;
+}
+
+NMConnection *
+nm_vpn_manager_get_connection_for_active (NMVPNManager *manager,
+ const char *active_path)
+{
+ NMVPNManagerPrivate *priv;
+ GHashTableIter iter;
+ gpointer data;
+ GSList *active, *elt;
g_return_val_if_fail (NM_IS_VPN_MANAGER (manager), NULL);
priv = NM_VPN_MANAGER_GET_PRIVATE (manager);
- for (iter = priv->services; iter; iter = g_slist_next (iter)) {
- GSList *active, *elt;
+ g_hash_table_iter_init (&iter, priv->services);
+ while (g_hash_table_iter_next (&iter, NULL, &data)) {
+ active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (data));
+ for (elt = active; elt; elt = g_slist_next (elt)) {
+ NMVPNConnection *vpn = NM_VPN_CONNECTION (elt->data);
+ const char *ac_path;
- active = nm_vpn_service_get_active_connections (NM_VPN_SERVICE (iter->data));
- for (elt = active; elt; elt = g_slist_next (elt))
- list = g_slist_append (list, g_object_ref (NM_VPN_CONNECTION (elt->data)));
+ ac_path = nm_vpn_connection_get_active_connection_path (vpn);
+ if (ac_path && !strcmp (ac_path, active_path))
+ return nm_vpn_connection_get_connection (vpn);
+ }
}
- return list;
+ return NULL;
}
+static char *
+service_name_from_file (const char *path)
+{
+ GKeyFile *kf = NULL;
+ char *service_name = NULL;
+
+ g_return_val_if_fail (g_path_is_absolute (path), NULL);
+
+ if (!g_str_has_suffix (path, ".name"))
+ return NULL;
+
+ kf = g_key_file_new ();
+ if (g_key_file_load_from_file (kf, path, G_KEY_FILE_NONE, NULL))
+ service_name = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "service", NULL);
+
+ g_key_file_free (kf);
+ return service_name;
+}
+
+static void
+try_add_service (NMVPNManager *self, const char *namefile)
+{
+ NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
+ NMVPNService *service = NULL;
+ GError *error = NULL;
+ const char *service_name;
+ char *tmp;
+
+ g_return_if_fail (g_path_is_absolute (namefile));
+
+ /* Make sure we don't add dupes */
+ tmp = service_name_from_file (namefile);
+ if (tmp)
+ service = g_hash_table_lookup (priv->services, tmp);
+ g_free (tmp);
+ if (service)
+ return;
+
+ /* New service, add it */
+ service = nm_vpn_service_new (namefile, &error);
+ if (!service) {
+ nm_log_warn (LOGD_VPN, "failed to load VPN service file %s: (%d) %s",
+ error ? error->code : -1,
+ error && error->message ? error->message : "(unknown)");
+ g_clear_error (&error);
+ return;
+ }
+
+ service_name = nm_vpn_service_get_dbus_service (service);
+ g_hash_table_insert (priv->services, (char *) service_name, service);
+ nm_log_info (LOGD_VPN, "VPN: loaded %s", service_name, service);
+}
+
+static void
+vpn_dir_changed (GFileMonitor *monitor,
+ GFile *file,
+ GFile *other_file,
+ GFileMonitorEvent event_type,
+ gpointer user_data)
+{
+ NMVPNManager *self = NM_VPN_MANAGER (user_data);
+ NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
+ NMVPNService *service;
+ char *path;
+
+ path = g_file_get_path (file);
+ if (!g_str_has_suffix (path, ".name")) {
+ g_free (path);
+ return;
+ }
+
+ switch (event_type) {
+ case G_FILE_MONITOR_EVENT_DELETED:
+ nm_log_dbg (LOGD_VPN, "service file %s deleted", path);
+
+ service = get_service_by_namefile (self, path);
+ if (service) {
+ const char *service_name = nm_vpn_service_get_dbus_service (service);
+
+ /* Stop active VPN connections and destroy the service */
+ nm_vpn_service_connections_stop (service, TRUE,
+ NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
+ nm_log_info (LOGD_VPN, "VPN: unloaded %s", service_name, service);
+ g_hash_table_remove (priv->services, service_name);
+ }
+ break;
+ case G_FILE_MONITOR_EVENT_CREATED:
+ case G_FILE_MONITOR_EVENT_CHANGES_DONE_HINT:
+ nm_log_dbg (LOGD_VPN, "service file %s created or modified", path);
+ try_add_service (self, path);
+ break;
+ default:
+ nm_log_dbg (LOGD_VPN, "service file %s change event %d", path, event_type);
+ break;
+ }
+
+ g_free (path);
+}
+
+/******************************************************************************/
+
NMVPNManager *
nm_vpn_manager_get (void)
{
@@ -318,21 +442,61 @@ nm_vpn_manager_get (void)
return singleton;
}
-/******************************************************************************/
-
static void
-nm_vpn_manager_init (NMVPNManager *manager)
+nm_vpn_manager_init (NMVPNManager *self)
{
+ NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (self);
+ GFile *file;
+ GDir *dir;
+ const char *fn;
+ char *path;
+
+ priv->services = g_hash_table_new_full (g_str_hash, g_str_equal,
+ NULL, g_object_unref);
+
+ /* Watch the VPN directory for changes */
+ file = g_file_new_for_path (VPN_NAME_FILES_DIR "/");
+ priv->monitor = g_file_monitor_directory (file, G_FILE_MONITOR_NONE, NULL, NULL);
+ g_object_unref (file);
+ if (priv->monitor) {
+ priv->monitor_id = g_signal_connect (priv->monitor, "changed",
+ G_CALLBACK (vpn_dir_changed), self);
+ }
+
+ /* Load VPN service files */
+ dir = g_dir_open (VPN_NAME_FILES_DIR, 0, NULL);
+ if (dir) {
+ while ((fn = g_dir_read_name (dir))) {
+ /* only parse filenames that end with .name */
+ if (g_str_has_suffix (fn, ".name")) {
+ path = g_build_filename (VPN_NAME_FILES_DIR, fn, NULL);
+ try_add_service (self, path);
+ g_free (path);
+ }
+ }
+ g_dir_close (dir);
+ }
}
static void
-finalize (GObject *object)
+dispose (GObject *object)
{
NMVPNManagerPrivate *priv = NM_VPN_MANAGER_GET_PRIVATE (object);
- g_slist_foreach (priv->services, (GFunc) g_object_unref, NULL);
+ if (!priv->disposed) {
+ priv->disposed = TRUE;
+
+ if (priv->monitor) {
+ if (priv->monitor_id)
+ g_signal_handler_disconnect (priv->monitor, priv->monitor_id);
+ g_file_monitor_cancel (priv->monitor);
+ g_object_unref (priv->monitor);
+ }
+
+ g_hash_table_destroy (priv->services);
+ }
- G_OBJECT_CLASS (nm_vpn_manager_parent_class)->finalize (object);
+ G_OBJECT_CLASS (nm_vpn_manager_parent_class)->dispose (object);
}
static void
@@ -343,7 +507,7 @@ nm_vpn_manager_class_init (NMVPNManagerClass *manager_class)
g_type_class_add_private (manager_class, sizeof (NMVPNManagerPrivate));
/* virtual methods */
- object_class->finalize = finalize;
+ object_class->dispose = dispose;
/* signals */
signals[CONNECTION_ACTIVATED] =
diff --git a/src/vpn-manager/nm-vpn-manager.h b/src/vpn-manager/nm-vpn-manager.h
index d07aa25099..f14844a9d4 100644
--- a/src/vpn-manager/nm-vpn-manager.h
+++ b/src/vpn-manager/nm-vpn-manager.h
@@ -83,4 +83,7 @@ void nm_vpn_manager_add_active_connections (NMVPNManager *manager,
GSList *nm_vpn_manager_get_active_connections (NMVPNManager *manager);
+NMConnection *nm_vpn_manager_get_connection_for_active (NMVPNManager *manager,
+ const char *active_path);
+
#endif /* NM_VPN_VPN_MANAGER_H */
diff --git a/src/vpn-manager/nm-vpn-service.c b/src/vpn-manager/nm-vpn-service.c
index bdbb3774a0..3d44d9004b 100644
--- a/src/vpn-manager/nm-vpn-service.c
+++ b/src/vpn-manager/nm-vpn-service.c
@@ -15,10 +15,11 @@
* with this program; if not, write to the Free Software Foundation, Inc.,
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
- * Copyright (C) 2005 - 2008 Red Hat, Inc.
+ * Copyright (C) 2005 - 2010 Red Hat, Inc.
* Copyright (C) 2005 - 2008 Novell, Inc.
*/
+#include <config.h>
#include <glib.h>
#include <string.h>
#include <dbus/dbus.h>
@@ -36,125 +37,94 @@
G_DEFINE_TYPE (NMVPNService, nm_vpn_service, G_TYPE_OBJECT)
typedef struct {
+ gboolean disposed;
+
NMDBusManager *dbus_mgr;
char *name;
char *dbus_service;
char *program;
+ char *namefile;
GPid pid;
GSList *connections;
- guint service_start_timeout;
- guint service_child_watch;
+ guint start_timeout;
+ guint quit_timeout;
+ guint child_watch;
gulong name_owner_id;
} NMVPNServicePrivate;
#define NM_VPN_SERVICE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_SERVICE, NMVPNServicePrivate))
-#define VPN_CONNECTION_GROUP "VPN Connection"
-
-static GKeyFile *
-find_service_file (const char *name)
-{
- GDir *dir;
- const char *fn;
- GKeyFile *key_file = NULL;
-
- dir = g_dir_open (VPN_NAME_FILES_DIR, 0, NULL);
- if (!dir)
- return NULL;
-
- while ((fn = g_dir_read_name (dir))) {
- char *path;
- gboolean found = FALSE;
-
- /* only parse filenames that end with .name */
- if (!g_str_has_suffix (fn, ".name"))
- continue;
-
- key_file = g_key_file_new ();
- path = g_build_filename (VPN_NAME_FILES_DIR, fn, NULL);
-
- if (g_key_file_load_from_file (key_file, path, G_KEY_FILE_NONE, NULL)) {
- gchar *val;
-
- val = g_key_file_get_string (key_file, VPN_CONNECTION_GROUP, "service", NULL);
- if (val) {
- if (!strcmp (val, name))
- found = TRUE;
- g_free (val);
- }
- }
-
- g_free (path);
-
- if (found)
- break;
-
- g_key_file_free (key_file);
- key_file = NULL;
- }
-
- g_dir_close (dir);
-
- return key_file;
-}
-
NMVPNService *
-nm_vpn_service_new (const char *name)
+nm_vpn_service_new (const char *namefile, GError **error)
{
- GKeyFile *key_file;
- NMVPNService *service = NULL;
- NMVPNServicePrivate *priv;
- char *dbus_service = NULL;
- char *program = NULL;
- gboolean success = FALSE;
+ NMVPNService *self = NULL;
+ GKeyFile *kf;
+ char *dbus_service = NULL, *program = NULL, *name = NULL;
- g_return_val_if_fail (name != NULL, NULL);
+ g_return_val_if_fail (namefile != NULL, NULL);
+ g_return_val_if_fail (g_path_is_absolute (namefile), NULL);
- key_file = find_service_file (name);
- if (!key_file)
+ kf = g_key_file_new ();
+ if (!g_key_file_load_from_file (kf, namefile, G_KEY_FILE_NONE, error)) {
+ g_key_file_free (kf);
return NULL;
+ }
- dbus_service = g_key_file_get_string (key_file, VPN_CONNECTION_GROUP, "service", NULL);
- if (!dbus_service)
+ dbus_service = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "service", NULL);
+ if (!dbus_service) {
+ g_set_error (error, 0, 0, "VPN service file %s had no 'service' key", namefile);
goto out;
+ }
- program = g_key_file_get_string (key_file, VPN_CONNECTION_GROUP, "program", NULL);
- if (!program)
+ program = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "program", NULL);
+ if (!program) {
+ g_set_error (error, 0, 0, "VPN service file %s had no 'program' key", namefile);
goto out;
+ }
- service = (NMVPNService *) g_object_new (NM_TYPE_VPN_SERVICE, NULL);
- if (!service)
+ name = g_key_file_get_string (kf, VPN_CONNECTION_GROUP, "name", NULL);
+ if (!name) {
+ g_set_error (error, 0, 0, "VPN service file %s had no 'name' key", namefile);
goto out;
+ }
- priv = NM_VPN_SERVICE_GET_PRIVATE (service);
-
- priv->name = g_strdup (name);
- priv->dbus_service = dbus_service;
- priv->program = program;
+ self = (NMVPNService *) g_object_new (NM_TYPE_VPN_SERVICE, NULL);
+ if (!self) {
+ g_set_error (error, 0, 0, "out of memory creating VPN service object");
+ goto out;
+ }
- success = TRUE;
+ NM_VPN_SERVICE_GET_PRIVATE (self)->name = g_strdup (name);
+ NM_VPN_SERVICE_GET_PRIVATE (self)->dbus_service = g_strdup (dbus_service);
+ NM_VPN_SERVICE_GET_PRIVATE (self)->program = g_strdup (program);
+ NM_VPN_SERVICE_GET_PRIVATE (self)->namefile = g_strdup (namefile);
out:
- g_key_file_free (key_file);
+ g_key_file_free (kf);
+ g_free (dbus_service);
+ g_free (program);
+ g_free (name);
+ return self;
+}
- if (!success) {
- g_free (dbus_service);
- g_free (program);
- }
+const char *
+nm_vpn_service_get_dbus_service (NMVPNService *service)
+{
+ g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL);
- return service;
+ return NM_VPN_SERVICE_GET_PRIVATE (service)->dbus_service;
}
const char *
-nm_vpn_service_get_name (NMVPNService *service)
+nm_vpn_service_get_name_file (NMVPNService *service)
{
g_return_val_if_fail (NM_IS_VPN_SERVICE (service), NULL);
- return NM_VPN_SERVICE_GET_PRIVATE (service)->name;
+ return NM_VPN_SERVICE_GET_PRIVATE (service)->namefile;
}
-static void
+void
nm_vpn_service_connections_stop (NMVPNService *service,
gboolean fail,
NMVPNConnectionStateReason reason)
@@ -175,6 +145,17 @@ nm_vpn_service_connections_stop (NMVPNService *service,
g_slist_free (copy);
}
+static void
+clear_quit_timeout (NMVPNService *self)
+{
+ NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
+
+ if (priv->quit_timeout) {
+ g_source_remove (priv->quit_timeout);
+ priv->quit_timeout = 0;
+ }
+}
+
/*
* nm_vpn_service_child_setup
*
@@ -200,21 +181,22 @@ vpn_service_watch_cb (GPid pid, gint status, gpointer user_data)
if (err != 0) {
nm_log_warn (LOGD_VPN, "VPN service '%s' exited with error: %d",
- nm_vpn_service_get_name (service), WSTOPSIG (status));
+ priv->name, WSTOPSIG (status));
}
} else if (WIFSTOPPED (status)) {
nm_log_warn (LOGD_VPN, "VPN service '%s' stopped unexpectedly with signal %d",
- nm_vpn_service_get_name (service), WSTOPSIG (status));
+ priv->name, WSTOPSIG (status));
} else if (WIFSIGNALED (status)) {
nm_log_warn (LOGD_VPN, "VPN service '%s' died with signal %d",
- nm_vpn_service_get_name (service), WTERMSIG (status));
+ priv->name, WTERMSIG (status));
} else {
nm_log_warn (LOGD_VPN, "VPN service '%s' died from an unknown cause",
- nm_vpn_service_get_name (service));
+ priv->name);
}
priv->pid = 0;
- priv->service_child_watch = 0;
+ priv->child_watch = 0;
+ clear_quit_timeout (service);
nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
}
@@ -222,14 +204,12 @@ vpn_service_watch_cb (GPid pid, gint status, gpointer user_data)
static gboolean
nm_vpn_service_timeout (gpointer data)
{
- NMVPNService *service = NM_VPN_SERVICE (data);
-
- nm_log_warn (LOGD_VPN, "VPN service '%s' did not start in time, cancelling connections",
- nm_vpn_service_get_name (service));
-
- NM_VPN_SERVICE_GET_PRIVATE (service)->service_start_timeout = 0;
- nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT);
+ NMVPNService *self = NM_VPN_SERVICE (data);
+ NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
+ nm_log_warn (LOGD_VPN, "VPN service '%s' start timed out", priv->name);
+ priv->start_timeout = 0;
+ nm_vpn_service_connections_stop (self, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_START_TIMEOUT);
return FALSE;
}
@@ -253,13 +233,15 @@ nm_vpn_service_daemon_exec (NMVPNService *service, GError **error)
&spawn_error);
if (success) {
nm_log_info (LOGD_VPN, "VPN service '%s' started (%s), PID %d",
- nm_vpn_service_get_name (service), priv->dbus_service, priv->pid);
+ priv->name, priv->dbus_service, priv->pid);
- priv->service_child_watch = g_child_watch_add (priv->pid, vpn_service_watch_cb, service);
- priv->service_start_timeout = g_timeout_add_seconds (5, nm_vpn_service_timeout, service);
+ priv->child_watch = g_child_watch_add (priv->pid, vpn_service_watch_cb, service);
+ priv->start_timeout = g_timeout_add_seconds (5, nm_vpn_service_timeout, service);
} else {
nm_log_warn (LOGD_VPN, "VPN service '%s': could not launch the VPN service. error: (%d) %s.",
- nm_vpn_service_get_name (service), spawn_error->code, spawn_error->message);
+ priv->name,
+ spawn_error ? spawn_error->code : -1,
+ spawn_error && spawn_error->message ? spawn_error->message : "(unknown)");
g_set_error (error,
NM_VPN_MANAGER_ERROR, NM_VPN_MANAGER_ERROR_SERVICE_START_FAILED,
@@ -274,9 +256,40 @@ nm_vpn_service_daemon_exec (NMVPNService *service, GError **error)
}
static gboolean
-destroy_service (gpointer data)
+ensure_killed (gpointer data)
+{
+ int pid = GPOINTER_TO_INT (data);
+
+ if (kill (pid, 0) == 0)
+ kill (pid, SIGKILL);
+
+ /* ensure the child is reaped */
+ nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", pid);
+ waitpid (pid, NULL, 0);
+ nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", pid);
+
+ return FALSE;
+}
+
+static gboolean
+service_quit (gpointer user_data)
{
- g_object_unref (data);
+ NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (user_data);
+
+ if (priv->pid) {
+ if (kill (priv->pid, SIGTERM) == 0)
+ g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (priv->pid));
+ else {
+ kill (priv->pid, SIGKILL);
+
+ /* ensure the child is reaped */
+ nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", priv->pid);
+ waitpid (priv->pid, NULL, 0);
+ nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", priv->pid);
+ }
+ priv->pid = 0;
+ }
+ priv->quit_timeout = 0;
return FALSE;
}
@@ -297,8 +310,9 @@ connection_vpn_state_changed (NMVPNConnection *connection,
g_object_unref (connection);
if (priv->connections == NULL) {
- /* schedule a timeout (10 seconds) to destroy the service */
- g_timeout_add_seconds (10, destroy_service, user_data);
+ /* Tell the service to quit in a few seconds */
+ if (!priv->quit_timeout)
+ priv->quit_timeout = g_timeout_add_seconds (5, service_quit, user_data);
}
break;
default:
@@ -325,6 +339,8 @@ nm_vpn_service_activate (NMVPNService *service,
priv = NM_VPN_SERVICE_GET_PRIVATE (service);
+ clear_quit_timeout (service);
+
vpn = nm_vpn_connection_new (connection, act_request, device);
g_signal_connect (vpn, "vpn-state-changed",
G_CALLBACK (connection_vpn_state_changed),
@@ -335,9 +351,8 @@ nm_vpn_service_activate (NMVPNService *service,
if (nm_dbus_manager_name_has_owner (priv->dbus_mgr, priv->dbus_service)) {
// FIXME: fill in error when errors happen
nm_vpn_connection_activate (vpn);
- } else if (priv->service_start_timeout == 0) {
- nm_log_info (LOGD_VPN, "Starting VPN service '%s'...",
- nm_vpn_service_get_name (service));
+ } else if (priv->start_timeout == 0) {
+ nm_log_info (LOGD_VPN, "Starting VPN service '%s'...", priv->name);
if (!nm_vpn_service_daemon_exec (service, error))
vpn = NULL;
}
@@ -364,14 +379,15 @@ nm_vpn_service_name_owner_changed (NMDBusManager *mgr,
NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
gboolean old_owner_good;
gboolean new_owner_good;
+ GSList *iter;
if (strcmp (name, priv->dbus_service))
return;
/* Service changed, no need to wait for the timeout any longer */
- if (priv->service_start_timeout) {
- g_source_remove (priv->service_start_timeout);
- priv->service_start_timeout = 0;
+ if (priv->start_timeout) {
+ g_source_remove (priv->start_timeout);
+ priv->start_timeout = 0;
}
old_owner_good = (old && (strlen (old) > 0));
@@ -379,18 +395,14 @@ nm_vpn_service_name_owner_changed (NMDBusManager *mgr,
if (!old_owner_good && new_owner_good) {
/* service just appeared */
- GSList *iter;
-
- nm_log_info (LOGD_VPN, "VPN service '%s' appeared, activating connections",
- nm_vpn_service_get_name (service));
+ nm_log_info (LOGD_VPN, "VPN service '%s' appeared; activating connections", priv->name);
+ clear_quit_timeout (service);
for (iter = priv->connections; iter; iter = iter->next)
nm_vpn_connection_activate (NM_VPN_CONNECTION (iter->data));
-
} else if (old_owner_good && !new_owner_good) {
/* service went away */
- nm_log_info (LOGD_VPN, "VPN service '%s' disappeared, cancelling connections",
- nm_vpn_service_get_name (service));
+ nm_log_info (LOGD_VPN, "VPN service '%s' disappeared", priv->name);
nm_vpn_service_connections_stop (service, TRUE, NM_VPN_CONNECTION_STATE_REASON_SERVICE_STOPPED);
}
}
@@ -398,40 +410,28 @@ nm_vpn_service_name_owner_changed (NMDBusManager *mgr,
/******************************************************************************/
static void
-nm_vpn_service_init (NMVPNService *service)
+nm_vpn_service_init (NMVPNService *self)
{
- NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (service);
+ NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
priv->dbus_mgr = nm_dbus_manager_get ();
-
priv->name_owner_id = g_signal_connect (priv->dbus_mgr, "name-owner-changed",
- G_CALLBACK (nm_vpn_service_name_owner_changed),
- service);
-}
-
-static gboolean
-ensure_killed (gpointer data)
-{
- int pid = GPOINTER_TO_INT (data);
-
- if (kill (pid, 0) == 0)
- kill (pid, SIGKILL);
-
- /* ensure the child is reaped */
- nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", pid);
- waitpid (pid, NULL, 0);
- nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", pid);
-
- return FALSE;
+ G_CALLBACK (nm_vpn_service_name_owner_changed),
+ self);
}
static void
-finalize (GObject *object)
+dispose (GObject *object)
{
- NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (object);
+ NMVPNService *self = NM_VPN_SERVICE (object);
+ NMVPNServicePrivate *priv = NM_VPN_SERVICE_GET_PRIVATE (self);
- if (priv->service_start_timeout)
- g_source_remove (priv->service_start_timeout);
+ if (priv->disposed)
+ goto out;
+ priv->disposed = TRUE;
+
+ if (priv->start_timeout)
+ g_source_remove (priv->start_timeout);
nm_vpn_service_connections_stop (NM_VPN_SERVICE (object),
FALSE,
@@ -439,31 +439,21 @@ finalize (GObject *object)
g_signal_handler_disconnect (priv->dbus_mgr, priv->name_owner_id);
- if (priv->service_child_watch)
- g_source_remove (priv->service_child_watch);
+ if (priv->child_watch)
+ g_source_remove (priv->child_watch);
- if (priv->pid) {
- if (kill (priv->pid, SIGTERM) == 0)
- g_timeout_add_seconds (2, ensure_killed, GINT_TO_POINTER (priv->pid));
- else {
- kill (priv->pid, SIGKILL);
-
- /* ensure the child is reaped */
- nm_log_dbg (LOGD_VPN, "waiting for VPN service pid %d to exit", priv->pid);
- waitpid (priv->pid, NULL, 0);
- nm_log_dbg (LOGD_VPN, "VPN service pid %d cleaned up", priv->pid);
- }
-
- priv->pid = 0;
- }
+ clear_quit_timeout (self);
+ service_quit (self);
g_object_unref (priv->dbus_mgr);
g_free (priv->name);
g_free (priv->dbus_service);
g_free (priv->program);
+ g_free (priv->namefile);
- G_OBJECT_CLASS (nm_vpn_service_parent_class)->finalize (object);
+out:
+ G_OBJECT_CLASS (nm_vpn_service_parent_class)->dispose (object);
}
static void
@@ -474,5 +464,5 @@ nm_vpn_service_class_init (NMVPNServiceClass *service_class)
g_type_class_add_private (service_class, sizeof (NMVPNServicePrivate));
/* virtual methods */
- object_class->finalize = finalize;
+ object_class->dispose = dispose;
}
diff --git a/src/vpn-manager/nm-vpn-service.h b/src/vpn-manager/nm-vpn-service.h
index 9c5e0eefc9..c7c1b0366a 100644
--- a/src/vpn-manager/nm-vpn-service.h
+++ b/src/vpn-manager/nm-vpn-service.h
@@ -35,6 +35,8 @@
#define NM_IS_VPN_SERVICE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), NM_TYPE_VPN_SERVICE))
#define NM_VPN_SERVICE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_VPN_SERVICE, NMVPNServiceClass))
+#define VPN_CONNECTION_GROUP "VPN Connection"
+
typedef struct {
GObject parent;
} NMVPNService;
@@ -45,9 +47,13 @@ typedef struct {
GType nm_vpn_service_get_type (void);
-NMVPNService * nm_vpn_service_new (const char *service_name);
+NMVPNService * nm_vpn_service_new (const char *namefile, GError **error);
+
+/* Returns the VPN service's D-Bus service name */
+const char *nm_vpn_service_get_dbus_service (NMVPNService *service);
-const char * nm_vpn_service_get_name (NMVPNService *service);
+/* Returns the path of the VPN service's .name file */
+const char *nm_vpn_service_get_name_file (NMVPNService *service);
NMVPNConnection * nm_vpn_service_activate (NMVPNService *service,
NMConnection *connection,
@@ -57,4 +63,8 @@ NMVPNConnection * nm_vpn_service_activate (NMVPNService *service,
GSList * nm_vpn_service_get_active_connections (NMVPNService *service);
+void nm_vpn_service_connections_stop (NMVPNService *service,
+ gboolean fail,
+ NMVPNConnectionStateReason reason);
+
#endif /* NM_VPN_VPN_SERVICE_H */