summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Williams <dcbw@redhat.com>2014-11-07 12:19:50 -0600
committerDan Williams <dcbw@redhat.com>2014-11-07 12:20:43 -0600
commit289f788158d2106b69073aecaa76f9afd4da2c58 (patch)
treee73aa5dc03a2d1897841029ec2451fbfffd6b899
parentcbabd135818fd4749ad0b827ba1f0bc444b92a97 (diff)
parent5149fd120d24e7622fb264fffaa4bed04eb579d6 (diff)
downloadNetworkManager-289f788158d2106b69073aecaa76f9afd4da2c58.tar.gz
merge: add 'configure-and-quit' option (rh #1083683) (rh #863515)
When initial configuration is complete, spawns helpers to preserve DHCP and RA addresses on interfaces and quits the main NM process.
-rw-r--r--.gitignore1
-rw-r--r--clients/cli/settings.c10
-rw-r--r--contrib/fedora/rpm/NetworkManager.spec1
-rw-r--r--libnm-core/nm-utils.c103
-rw-r--r--libnm-core/nm-utils.h5
-rw-r--r--libnm-core/tests/test-general.c37
-rw-r--r--libnm/libnm.ver1
-rw-r--r--libnm/nm-device.c15
-rw-r--r--man/NetworkManager.conf.xml.in24
-rw-r--r--po/POTFILES.in2
-rw-r--r--src/Makefile.am111
-rw-r--r--src/NetworkManagerUtils.c51
-rw-r--r--src/NetworkManagerUtils.h1
-rw-r--r--src/devices/nm-device.c170
-rw-r--r--src/devices/nm-device.h2
-rw-r--r--src/dhcp-manager/nm-dhcp-client.c74
-rw-r--r--src/dhcp-manager/nm-dhcp-client.h35
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient-utils.c134
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient-utils.h7
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient.c86
-rw-r--r--src/dhcp-manager/nm-dhcp-dhclient.h6
-rw-r--r--src/dhcp-manager/nm-dhcp-dhcpcd.c32
-rw-r--r--src/dhcp-manager/nm-dhcp-dhcpcd.h2
-rw-r--r--src/dhcp-manager/nm-dhcp-listener.c289
-rw-r--r--src/dhcp-manager/nm-dhcp-listener.h39
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.c379
-rw-r--r--src/dhcp-manager/nm-dhcp-manager.h3
-rw-r--r--src/dhcp-manager/nm-dhcp-systemd.c97
-rw-r--r--src/dhcp-manager/nm-dhcp-systemd.h4
-rw-r--r--src/dhcp-manager/nm-dhcp-utils.c41
-rw-r--r--src/dhcp-manager/tests/Makefile.am4
-rw-r--r--src/dhcp-manager/tests/test-dhcp-dhclient.c124
-rw-r--r--src/main-utils.c281
-rw-r--r--src/main-utils.h39
-rw-r--r--src/main.c253
-rw-r--r--src/nm-config.c88
-rw-r--r--src/nm-config.h1
-rw-r--r--src/nm-iface-helper.c529
-rw-r--r--src/nm-ip4-config.c4
-rw-r--r--src/nm-ip6-config.c4
-rw-r--r--src/nm-manager.c28
-rw-r--r--src/nm-manager.h2
-rw-r--r--src/settings/plugins/ifcfg-rh/reader.c42
-rw-r--r--src/settings/plugins/ifnet/connection_parser.c41
-rw-r--r--src/supplicant-manager/nm-supplicant-config.c34
45 files changed, 2355 insertions, 881 deletions
diff --git a/.gitignore b/.gitignore
index d7c47e3899..8aa06cad5d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -232,6 +232,7 @@ valgrind-*.log
/src/dhcp-manager/tests/test-dhcp-options
/src/dhcp-manager/tests/test-dhcp-utils
/src/dnsmasq-manager/tests/test-dnsmasq-utils
+/src/nm-iface-helper
/src/settings/plugins/ibft/tests/test-ibft
/src/settings/plugins/ifcfg-rh/tests/network-scripts/*-Test_Write_*
/src/settings/plugins/ifcfg-rh/tests/network-scripts/Test_Write_*
diff --git a/clients/cli/settings.c b/clients/cli/settings.c
index 2c6be705dc..dcd4f1cc72 100644
--- a/clients/cli/settings.c
+++ b/clients/cli/settings.c
@@ -2343,7 +2343,7 @@ nmc_property_set_byte_array (NMSetting *setting, const char *prop, const char *v
char *val_strip;
const char *delimiters = " \t,";
long int val_int;
- char *bin;
+ GBytes *bytes;
GByteArray *array = NULL;
gboolean success = TRUE;
@@ -2352,11 +2352,9 @@ nmc_property_set_byte_array (NMSetting *setting, const char *prop, const char *v
val_strip = g_strstrip (g_strdup (val));
/* First try hex string in the format of AAbbCCDd */
- bin = nm_utils_hexstr2bin (val_strip, strlen (val_strip));
- if (bin) {
- array = g_byte_array_sized_new (strlen (val_strip)/2);
- g_byte_array_append (array, (const guint8 *) bin, strlen (val_strip)/2);
- g_free (bin);
+ bytes = nm_utils_hexstr2bin (val_strip);
+ if (bytes) {
+ array = g_bytes_unref_to_array (bytes);
goto done;
}
diff --git a/contrib/fedora/rpm/NetworkManager.spec b/contrib/fedora/rpm/NetworkManager.spec
index 20d1a43a0a..b0c76790b9 100644
--- a/contrib/fedora/rpm/NetworkManager.spec
+++ b/contrib/fedora/rpm/NetworkManager.spec
@@ -518,6 +518,7 @@ fi
%{_libexecdir}/nm-dhcp-helper
%{_libexecdir}/nm-avahi-autoipd.action
%{_libexecdir}/nm-dispatcher
+%{_libexecdir}/nm-iface-helper
%dir %{_libdir}/NetworkManager
%{_libdir}/NetworkManager/libnm-settings-plugin*.so
%{_mandir}/man1/*
diff --git a/libnm-core/nm-utils.c b/libnm-core/nm-utils.c
index 12ad1f2d50..37f9ca0cf4 100644
--- a/libnm-core/nm-utils.c
+++ b/libnm-core/nm-utils.c
@@ -33,6 +33,7 @@
#include "nm-glib-compat.h"
#include "nm-setting-private.h"
#include "crypto.h"
+#include "gsystem-local-alloc.h"
#include "nm-setting-bond.h"
#include "nm-setting-bridge.h"
@@ -2100,7 +2101,7 @@ nm_utils_rsa_key_encrypt_helper (const char *cipher,
if (!in_password) {
if (!crypto_randomize (pw_buf, sizeof (pw_buf), error))
return NULL;
- in_password = tmp_password = nm_utils_bin2hexstr ((const char *) pw_buf, sizeof (pw_buf), -1);
+ in_password = tmp_password = nm_utils_bin2hexstr (pw_buf, sizeof (pw_buf), -1);
}
if (g_strcmp0 (cipher, CIPHER_AES_CBC) == 0)
@@ -2124,7 +2125,7 @@ nm_utils_rsa_key_encrypt_helper (const char *cipher,
g_string_append (pem, "Proc-Type: 4,ENCRYPTED\n");
/* Convert the salt to a hex string */
- tmp = nm_utils_bin2hexstr ((const char *) salt, salt_len, salt_len * 2);
+ tmp = nm_utils_bin2hexstr (salt, salt_len, salt_len * 2);
g_string_append_printf (pem, "DEK-Info: %s,%s\n\n", cipher, tmp);
g_free (tmp);
@@ -2876,13 +2877,13 @@ _nm_utils_hwaddr_from_dbus (GVariant *dbus_value,
/**
* nm_utils_bin2hexstr:
- * @bytes: an array of bytes
+ * @src: an array of bytes
* @len: the length of the @bytes array
* @final_len: an index where to cut off the returned string, or -1
*
- * Converts a byte-array @bytes into a hexadecimal string.
- * If @final_len is greater than -1, the returned string is terminated at
- * that index (returned_string[final_len] == '\0'),
+ * Converts the byte array @src into a hexadecimal string. If @final_len is
+ * greater than -1, the returned string is terminated at that index
+ * (returned_string[final_len] == '\0'),
*
* Return value: (transfer full): the textual form of @bytes
*/
@@ -2891,9 +2892,10 @@ _nm_utils_hwaddr_from_dbus (GVariant *dbus_value,
* copyright Red Hat, Inc. under terms of the LGPL.
*/
char *
-nm_utils_bin2hexstr (const char *bytes, int len, int final_len)
+nm_utils_bin2hexstr (gconstpointer src, gsize len, int final_len)
{
static char hex_digits[] = "0123456789abcdef";
+ const guint8 *bytes = src;
char *result;
int i;
gsize buflen = (len * 2) + 1;
@@ -2918,64 +2920,61 @@ nm_utils_bin2hexstr (const char *bytes, int len, int final_len)
return result;
}
-/* From hostap, Copyright (c) 2002-2005, Jouni Malinen <jkmaline@cc.hut.fi> */
/**
- * nm_utils_hex2byte:
- * @hex: a string representing a hex byte
+ * nm_utils_hexstr2bin:
+ * @hex: a string of hexadecimal characters with optional ':' separators
*
- * Converts a hex string (2 characters) into its byte representation.
+ * Converts a hexadecimal string @hex into an array of bytes. The optional
+ * separator ':' may be used between single or pairs of hexadecimal characters,
+ * eg "00:11" or "0:1". Any "0x" at the beginning of @hex is ignored. @hex
+ * may not start or end with ':'.
*
- * Return value: a byte, or -1 if @hex doesn't represent a hex byte
+ * Return value: (transfer full): the converted bytes, or %NULL on error
*/
-int
-nm_utils_hex2byte (const char *hex)
+GBytes *
+nm_utils_hexstr2bin (const char *hex)
{
+ guint i = 0, x = 0;
+ gs_free guint8 *c = NULL;
int a, b;
- a = g_ascii_xdigit_value (*hex++);
- if (a < 0)
- return -1;
- b = g_ascii_xdigit_value (*hex++);
- if (b < 0)
- return -1;
- return (a << 4) | b;
-}
+ gboolean found_colon = FALSE;
-/**
- * nm_utils_hexstr2bin:
- * @hex: an hex string
- * @len: the length of the @hex string (it has to be even)
- *
- * Converts a hexadecimal string @hex into a byte-array. The returned array
- * length is @len/2.
- *
- * Return value: (transfer full): a array of bytes, or %NULL on error
- */
-char *
-nm_utils_hexstr2bin (const char *hex, size_t len)
-{
- size_t i;
- int a;
- const char * ipos = hex;
- char * buf = NULL;
- char * opos;
+ g_return_val_if_fail (hex != NULL, NULL);
- /* Length must be a multiple of 2 */
- if ((len % 2) != 0)
- return NULL;
+ if (strncasecmp (hex, "0x", 2) == 0)
+ hex += 2;
+ found_colon = !!strchr (hex, ':');
- opos = buf = g_malloc0 ((len / 2) + 1);
- for (i = 0; i < len; i += 2) {
- a = nm_utils_hex2byte (ipos);
- if (a < 0) {
- g_free (buf);
+ c = g_malloc (strlen (hex) / 2 + 1);
+ for (;;) {
+ a = g_ascii_xdigit_value (hex[i++]);
+ if (a < 0)
+ return NULL;
+
+ if (hex[i] && hex[i] != ':') {
+ b = g_ascii_xdigit_value (hex[i++]);
+ if (b < 0)
+ return NULL;
+ c[x++] = ((guint) a << 4) | ((guint) b);
+ } else
+ c[x++] = (guint) a;
+
+ if (!hex[i])
+ break;
+ if (hex[i] == ':') {
+ if (!hex[i + 1]) {
+ /* trailing ':' is invalid */
+ return NULL;
+ }
+ i++;
+ } else if (found_colon) {
+ /* If colons exist, they must delimit 1 or 2 hex chars */
return NULL;
}
- *opos++ = a;
- ipos += 2;
}
- return buf;
+
+ return g_bytes_new (c, x);
}
-/* End from hostap */
/**
* nm_utils_iface_valid_name:
diff --git a/libnm-core/nm-utils.h b/libnm-core/nm-utils.h
index 25f09c45ea..a4253927be 100644
--- a/libnm-core/nm-utils.h
+++ b/libnm-core/nm-utils.h
@@ -167,9 +167,8 @@ gboolean nm_utils_hwaddr_matches (gconstpointer hwaddr1,
gconstpointer hwaddr2,
gssize hwaddr2_len);
-char *nm_utils_bin2hexstr (const char *bytes, int len, int final_len);
-int nm_utils_hex2byte (const char *hex);
-char *nm_utils_hexstr2bin (const char *hex, size_t len);
+char *nm_utils_bin2hexstr (gconstpointer src, gsize len, int final_len);
+GBytes *nm_utils_hexstr2bin (const char *hex);
gboolean nm_utils_iface_valid_name(const char *name);
diff --git a/libnm-core/tests/test-general.c b/libnm-core/tests/test-general.c
index 2c77726654..522c6eb15f 100644
--- a/libnm-core/tests/test-general.c
+++ b/libnm-core/tests/test-general.c
@@ -3550,6 +3550,41 @@ test_setting_ip6_gateway (void)
g_object_unref (conn);
}
+typedef struct {
+ const char *str;
+ const guint8 expected[20];
+ const guint expected_len;
+} HexItem;
+
+static void
+test_hexstr2bin (void)
+{
+ static const HexItem items[] = {
+ { "aaBBCCddDD10496a", { 0xaa, 0xbb, 0xcc, 0xdd, 0xdd, 0x10, 0x49, 0x6a }, 8 },
+ { "aa:bb:cc:dd:10:49:6a", { 0xaa, 0xbb, 0xcc, 0xdd, 0x10, 0x49, 0x6a }, 7 },
+ { "0xccddeeff", { 0xcc, 0xdd, 0xee, 0xff }, 4 },
+ { "1:2:66:77:80", { 0x01, 0x02, 0x66, 0x77, 0x80 }, 5 },
+ { "e", { 0x0e }, 1 },
+ { "aabb1199:" },
+ { ":aabb1199" },
+ { "aabb$$dd" },
+ { "aab:ccc:ddd" },
+ { "aab::ccc:ddd" },
+ };
+ GBytes *b;
+ guint i;
+
+ for (i = 0; i < G_N_ELEMENTS (items); i++) {
+ b = nm_utils_hexstr2bin (items[i].str);
+ if (items[i].expected_len) {
+ g_assert (b);
+ g_assert_cmpint (g_bytes_get_size (b), ==, items[i].expected_len);
+ g_assert (memcmp (g_bytes_get_data (b, NULL), items[i].expected, g_bytes_get_size (b)) == 0);
+ } else
+ g_assert (b == NULL);
+ }
+}
+
NMTST_DEFINE ();
int main (int argc, char **argv)
@@ -3641,6 +3676,8 @@ int main (int argc, char **argv)
g_test_add_func ("/core/general/test_setting_ip4_gateway", test_setting_ip4_gateway);
g_test_add_func ("/core/general/test_setting_ip6_gateway", test_setting_ip6_gateway);
+ g_test_add_func ("/core/general/hexstr2bin", test_hexstr2bin);
+
return g_test_run ();
}
diff --git a/libnm/libnm.ver b/libnm/libnm.ver
index baf83ca548..d1957ececc 100644
--- a/libnm/libnm.ver
+++ b/libnm/libnm.ver
@@ -756,7 +756,6 @@ global:
nm_utils_deinit;
nm_utils_escape_ssid;
nm_utils_file_is_pkcs12;
- nm_utils_hex2byte;
nm_utils_hexstr2bin;
nm_utils_hwaddr_atoba;
nm_utils_hwaddr_aton;
diff --git a/libnm/nm-device.c b/libnm/nm-device.c
index ba5dbacb85..a6d5fe3067 100644
--- a/libnm/nm-device.c
+++ b/libnm/nm-device.c
@@ -1249,6 +1249,19 @@ nm_device_get_available_connections (NMDevice *device)
return NM_DEVICE_GET_PRIVATE (device)->available_connections;
}
+static inline guint8
+hex2byte (const char *hex)
+{
+ int a, b;
+ a = g_ascii_xdigit_value (*hex++);
+ if (a < 0)
+ return -1;
+ b = g_ascii_xdigit_value (*hex++);
+ if (b < 0)
+ return -1;
+ return (a << 4) | b;
+}
+
static char *
get_decoded_property (GUdevDevice *device, const char *property)
{
@@ -1264,7 +1277,7 @@ get_decoded_property (GUdevDevice *device, const char *property)
n = unescaped = g_malloc0 (len + 1);
while (*p) {
if ((len >= 4) && (*p == '\\') && (*(p+1) == 'x')) {
- *n++ = (char) nm_utils_hex2byte (p + 2);
+ *n++ = (char) hex2byte (p + 2);
p += 4;
len -= 4;
} else {
diff --git a/man/NetworkManager.conf.xml.in b/man/NetworkManager.conf.xml.in
index 67919cfb4f..4fb8f502c0 100644
--- a/man/NetworkManager.conf.xml.in
+++ b/man/NetworkManager.conf.xml.in
@@ -3,7 +3,7 @@
"http://www.oasis-open.org/docbook/xml/4.2/docbookx.dtd">
<!--
-Copyright (C) 2010 - 2013 Red Hat, Inc.
+Copyright 2010 - 2014 Red Hat, Inc.
-->
<refentry id="NetworkManager.conf">
@@ -200,6 +200,28 @@ Copyright (C) 2010 - 2013 Red Hat, Inc.
</varlistentry>
<varlistentry>
+ <term><varname>configure-and-quit</varname></term>
+ <listitem>
+ <para>
+ When set to <literal>true</literal>, NetworkManager quits after
+ performing initial network configuration but spawns small helpers
+ to preserve DHCP leases and IPv6 addresses. This is useful in
+ environments where network setup is more or less static or it is
+ desirable to save process time but still handle some dynamic
+ configurations. When this option is <literal>true</literal>,
+ network configuration for WiFi, WWAN, Bluetooth, ADSL, and PPPoE
+ interfaces cannot be preserved due to their use of external
+ services, and these devices will be deconfigured when NetworkManager
+ quits even though other interface's configuration may be preserved.
+ The default value is <literal>false</literal>, meaning that
+ NetworkManager will continue running after initial network
+ configuration and continue responding to system and hardware events,
+ D-Bus requests, and user commands.
+ </para>
+ </listitem>
+ </varlistentry>
+
+ <varlistentry>
<term><varname>dns</varname></term>
<listitem><para>Set the DNS (<filename>resolv.conf</filename>) processing mode.</para>
<para><literal>default</literal>: The default if the key is
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 5c44103240..0571f0e238 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -126,6 +126,7 @@ libnm/nm-vpn-plugin-old.c
policy/org.freedesktop.NetworkManager.policy.in.in
src/NetworkManagerUtils.c
src/main.c
+src/main-utils.c
src/dhcp-manager/nm-dhcp-dhclient.c
src/dhcp-manager/nm-dhcp-dhclient-utils.c
src/dhcp-manager/nm-dhcp-manager.c
@@ -146,6 +147,7 @@ src/devices/wifi/nm-wifi-ap-utils.c
src/devices/wimax/nm-device-wimax.c
src/devices/wwan/nm-modem-broadband.c
src/nm-config.c
+src/nm-iface-helper.c
src/nm-logging.c
src/nm-manager.c
src/nm-sleep-monitor-systemd.c
diff --git a/src/Makefile.am b/src/Makefile.am
index a0447bdd42..eb5c64c089 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -48,7 +48,10 @@ AM_CPPFLAGS = \
# primarily for its side effect of removing duplicates.
AM_CPPFLAGS += $(foreach d,$(sort $(dir $(libNetworkManager_la_SOURCES))),-I$(top_srcdir)/src/$d)
-noinst_LTLIBRARIES = libNetworkManager.la libsystemd-dhcp.la
+noinst_LTLIBRARIES = \
+ libNetworkManager.la \
+ libnm-iface-helper.la \
+ libsystemd-dhcp.la
######################
# libsystemd-dhcp
@@ -124,6 +127,9 @@ sbin_PROGRAMS = NetworkManager
NetworkManager_SOURCES = \
$(nm_device_sources) $(nm_device_headers) \
+ $(nm_dhcp_client_sources) $(nm_dhcp_client_headers) \
+ main-utils.c \
+ main-utils.h \
main.c
NetworkManager_LDADD = libNetworkManager.la
@@ -153,8 +159,21 @@ nm_device_headers = \
devices/nm-device-vlan.h \
devices/nm-device-vxlan.h
+nm_dhcp_client_sources = \
+ dhcp-manager/nm-dhcp-dhclient.c \
+ dhcp-manager/nm-dhcp-dhclient-utils.c \
+ dhcp-manager/nm-dhcp-dhcpcd.c \
+ dhcp-manager/nm-dhcp-systemd.c
+
+nm_dhcp_client_headers = \
+ dhcp-manager/nm-dhcp-dhclient.h \
+ dhcp-manager/nm-dhcp-dhclient-utils.h \
+ dhcp-manager/nm-dhcp-dhcpcd.h \
+ dhcp-manager/nm-dhcp-systemd.h
+
nm_sources = \
$(nm_device_headers) \
+ $(nm_dhcp_client_headers) \
devices/nm-device.c \
devices/nm-device.h \
devices/nm-device-ethernet-utils.c \
@@ -170,14 +189,8 @@ nm_sources = \
dhcp-manager/nm-dhcp-client.h \
dhcp-manager/nm-dhcp-utils.c \
dhcp-manager/nm-dhcp-utils.h \
- dhcp-manager/nm-dhcp-dhclient.c \
- dhcp-manager/nm-dhcp-dhclient.h \
- dhcp-manager/nm-dhcp-dhclient-utils.c \
- dhcp-manager/nm-dhcp-dhclient-utils.h \
- dhcp-manager/nm-dhcp-dhcpcd.c \
- dhcp-manager/nm-dhcp-dhcpcd.h \
- dhcp-manager/nm-dhcp-systemd.h \
- dhcp-manager/nm-dhcp-systemd.c \
+ dhcp-manager/nm-dhcp-listener.c \
+ dhcp-manager/nm-dhcp-listener.h \
dhcp-manager/nm-dhcp-manager.c \
dhcp-manager/nm-dhcp-manager.h \
\
@@ -448,6 +461,86 @@ NetworkManager_LDFLAGS = -rdynamic
######################
+libnm_iface_helper_la_SOURCES = \
+ dhcp-manager/nm-dhcp-client.c \
+ dhcp-manager/nm-dhcp-client.h \
+ dhcp-manager/nm-dhcp-utils.c \
+ dhcp-manager/nm-dhcp-utils.h \
+ dhcp-manager/nm-dhcp-manager.c \
+ dhcp-manager/nm-dhcp-manager.h \
+ \
+ platform/nm-linux-platform.c \
+ platform/nm-linux-platform.h \
+ platform/nm-platform.c \
+ platform/nm-platform.h \
+ platform/wifi/wifi-utils-nl80211.c \
+ platform/wifi/wifi-utils-nl80211.h \
+ platform/wifi/wifi-utils-private.h \
+ platform/wifi/wifi-utils.c \
+ platform/wifi/wifi-utils.h \
+ \
+ rdisc/nm-fake-rdisc.c \
+ rdisc/nm-fake-rdisc.h \
+ rdisc/nm-lndp-rdisc.c \
+ rdisc/nm-lndp-rdisc.h \
+ rdisc/nm-rdisc.c \
+ rdisc/nm-rdisc.h \
+ \
+ nm-ip4-config.c \
+ nm-ip4-config.h \
+ nm-ip6-config.c \
+ nm-ip6-config.h \
+ \
+ nm-enum-types.c \
+ nm-enum-types.h \
+ nm-logging.c \
+ nm-logging.h \
+ nm-posix-signals.c \
+ nm-posix-signals.h \
+ NetworkManagerUtils.c \
+ NetworkManagerUtils.h
+
+if WITH_WEXT
+libnm_iface_helper_la_SOURCES += \
+ platform/wifi/wifi-utils-wext.c \
+ platform/wifi/wifi-utils-wext.h
+endif
+
+libnm_iface_helper_la_LIBADD = \
+ $(top_builddir)/libnm-core/libnm-core.la \
+ libsystemd-dhcp.la \
+ $(DBUS_LIBS) \
+ $(GLIB_LIBS) \
+ $(GUDEV_LIBS) \
+ $(LIBNL_LIBS) \
+ $(LIBNDP_LIBS) \
+ $(LIBDL) \
+ $(LIBM)
+
+libexec_PROGRAMS = nm-iface-helper
+
+nm_iface_helper_SOURCES = \
+ dhcp-manager/nm-dhcp-systemd.h \
+ dhcp-manager/nm-dhcp-systemd.c \
+ nm-iface-helper.c \
+ main-utils.c \
+ main-utils.h
+
+nm_iface_helper_LDADD = \
+ $(top_builddir)/libnm-core/libnm-core.la \
+ libsystemd-dhcp.la \
+ libnm-iface-helper.la \
+ $(DBUS_LIBS) \
+ $(GLIB_LIBS) \
+ $(GUDEV_LIBS) \
+ $(LIBNL_LIBS) \
+ $(LIBNDP_LIBS) \
+ $(LIBM)
+
+nm_iface_helper_LDFLAGS = -rdynamic
+
+######################
+
dbusservicedir = $(DBUS_SYS_DIR)
dbusservice_DATA = org.freedesktop.NetworkManager.conf
diff --git a/src/NetworkManagerUtils.c b/src/NetworkManagerUtils.c
index 538295a10c..0dcd643bf3 100644
--- a/src/NetworkManagerUtils.c
+++ b/src/NetworkManagerUtils.c
@@ -2156,6 +2156,32 @@ out:
}
+#define IPV6_PROPERTY_DIR "/proc/sys/net/ipv6/conf/"
+#define IPV4_PROPERTY_DIR "/proc/sys/net/ipv4/conf/"
+G_STATIC_ASSERT (sizeof (IPV4_PROPERTY_DIR) == sizeof (IPV6_PROPERTY_DIR));
+
+static const char *
+_get_property_path (const char *ifname,
+ const char *property,
+ gboolean ipv6)
+{
+ static char path[sizeof (IPV6_PROPERTY_DIR) + IFNAMSIZ + 32];
+ int len;
+
+ ifname = ASSERT_VALID_PATH_COMPONENT (ifname);
+ property = ASSERT_VALID_PATH_COMPONENT (property);
+
+ len = g_snprintf (path,
+ sizeof (path),
+ "%s%s/%s",
+ ipv6 ? IPV6_PROPERTY_DIR : IPV4_PROPERTY_DIR,
+ ifname,
+ property);
+ g_assert (len < sizeof (path) - 1);
+
+ return path;
+}
+
/**
* nm_utils_ip6_property_path:
* @ifname: an interface name
@@ -2167,18 +2193,21 @@ out:
const char *
nm_utils_ip6_property_path (const char *ifname, const char *property)
{
-#define IPV6_PROPERTY_DIR "/proc/sys/net/ipv6/conf/"
- static char path[sizeof (IPV6_PROPERTY_DIR) + IFNAMSIZ + 32];
- int len;
-
- ifname = ASSERT_VALID_PATH_COMPONENT (ifname);
- property = ASSERT_VALID_PATH_COMPONENT (property);
-
- len = g_snprintf (path, sizeof (path), IPV6_PROPERTY_DIR "%s/%s",
- ifname, property);
- g_assert (len < sizeof (path) - 1);
+ return _get_property_path (ifname, property, TRUE);
+}
- return path;
+/**
+ * nm_utils_ip4_property_path:
+ * @ifname: an interface name
+ * @property: a property name
+ *
+ * Returns the path to IPv4 property @property on @ifname. Note that
+ * this uses a static buffer.
+ */
+const char *
+nm_utils_ip4_property_path (const char *ifname, const char *property)
+{
+ return _get_property_path (ifname, property, FALSE);
}
const char *
diff --git a/src/NetworkManagerUtils.h b/src/NetworkManagerUtils.h
index 492edd133e..0ed7587a63 100644
--- a/src/NetworkManagerUtils.h
+++ b/src/NetworkManagerUtils.h
@@ -168,6 +168,7 @@ gint32 nm_utils_get_monotonic_timestamp_s (void);
const char *ASSERT_VALID_PATH_COMPONENT (const char *name) G_GNUC_WARN_UNUSED_RESULT;
const char *nm_utils_ip6_property_path (const char *ifname, const char *property);
+const char *nm_utils_ip4_property_path (const char *ifname, const char *property);
gboolean nm_utils_is_specific_hostname (const char *name);
diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c
index 9eb25c96aa..3390bf765e 100644
--- a/src/devices/nm-device.c
+++ b/src/devices/nm-device.c
@@ -2975,7 +2975,8 @@ dhcp4_start (NMDevice *self,
nm_setting_ip_config_get_dhcp_hostname (s_ip4),
nm_setting_ip4_config_get_dhcp_client_id (NM_SETTING_IP4_CONFIG (s_ip4)),
priv->dhcp_timeout,
- priv->dhcp_anycast_address);
+ priv->dhcp_anycast_address,
+ NULL);
if (tmp)
g_byte_array_free (tmp, TRUE);
@@ -6989,6 +6990,173 @@ nm_device_cleanup (NMDevice *self, NMDeviceStateReason reason)
_cleanup_generic_post (self, TRUE);
}
+static char *
+bin2hexstr (const char *bytes, gsize len)
+{
+ GString *str;
+ int i;
+
+ g_return_val_if_fail (bytes != NULL, NULL);
+ g_return_val_if_fail (len > 0, NULL);
+
+ str = g_string_sized_new (len * 2 + 1);
+ for (i = 0; i < len; i++) {
+ if (str->len)
+ g_string_append_c (str, ':');
+ g_string_append_printf (str, "%02x", (guint8) bytes[i]);
+ }
+ return g_string_free (str, FALSE);
+}
+
+static char *
+find_dhcp4_address (NMDevice *self)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ guint i, n;
+
+ if (!priv->ip4_config)
+ return NULL;
+
+ n = nm_ip4_config_get_num_addresses (priv->ip4_config);
+ for (i = 0; i < n; i++) {
+ const NMPlatformIP4Address *a = nm_ip4_config_get_address (priv->ip4_config, i);
+
+ if (a->source == NM_IP_CONFIG_SOURCE_DHCP)
+ return g_strdup (nm_utils_inet4_ntop (a->address, NULL));
+ }
+ return NULL;
+}
+
+void
+nm_device_spawn_iface_helper (NMDevice *self)
+{
+ NMDevicePrivate *priv = NM_DEVICE_GET_PRIVATE (self);
+ gboolean priority_set = FALSE, configured = FALSE;
+ NMConnection *connection;
+ GError *error = NULL;
+ const char *method;
+ GPtrArray *argv;
+ gs_free char *dhcp4_address = NULL;
+
+ if (priv->state != NM_DEVICE_STATE_ACTIVATED)
+ return;
+ if (!nm_device_can_assume_connections (self))
+ return;
+
+ connection = nm_device_get_connection (self);
+ g_assert (connection);
+
+ argv = g_ptr_array_sized_new (10);
+ g_ptr_array_set_free_func (argv, g_free);
+
+ g_ptr_array_add (argv, g_strdup (LIBEXECDIR "/nm-iface-helper"));
+ g_ptr_array_add (argv, g_strdup ("--ifname"));
+ g_ptr_array_add (argv, g_strdup (nm_device_get_ip_iface (self)));
+ g_ptr_array_add (argv, g_strdup ("--uuid"));
+ g_ptr_array_add (argv, g_strdup (nm_connection_get_uuid (connection)));
+
+ dhcp4_address = find_dhcp4_address (self);
+
+ method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP4_CONFIG);
+ if ( priv->ip4_config
+ && priv->ip4_state == IP_DONE
+ && g_strcmp0 (method, NM_SETTING_IP4_CONFIG_METHOD_AUTO) == 0
+ && priv->dhcp4_client
+ && dhcp4_address) {
+ NMSettingIPConfig *s_ip4;
+ GBytes *client_id;
+ char *hex_client_id;
+ const char *hostname;
+
+ s_ip4 = nm_connection_get_setting_ip4_config (connection);
+ g_assert (s_ip4);
+
+ g_ptr_array_add (argv, g_strdup ("--priority"));
+ g_ptr_array_add (argv, g_strdup_printf ("%u", nm_dhcp_client_get_priority (priv->dhcp4_client)));
+ priority_set = TRUE;
+
+ g_ptr_array_add (argv, g_strdup ("--dhcp4"));
+ g_ptr_array_add (argv, g_strdup (dhcp4_address));
+ if (nm_setting_ip_config_get_may_fail (s_ip4) == FALSE)
+ g_ptr_array_add (argv, g_strdup ("--dhcp4-required"));
+
+ client_id = nm_dhcp_client_get_client_id (priv->dhcp4_client);
+ if (client_id) {
+ g_ptr_array_add (argv, g_strdup ("--dhcp4-clientid"));
+ hex_client_id = bin2hexstr (g_bytes_get_data (client_id, NULL),
+ g_bytes_get_size (client_id));
+ g_ptr_array_add (argv, hex_client_id);
+ }
+
+ hostname = nm_dhcp_client_get_hostname (priv->dhcp4_client);
+ if (client_id) {
+ g_ptr_array_add (argv, g_strdup ("--dhcp4-hostname"));
+ g_ptr_array_add (argv, g_strdup (hostname));
+ }
+
+ configured = TRUE;
+ }
+
+ method = nm_utils_get_ip_config_method (connection, NM_TYPE_SETTING_IP6_CONFIG);
+ if ( priv->ip6_config
+ && priv->ip6_state == IP_DONE
+ && g_strcmp0 (method, NM_SETTING_IP6_CONFIG_METHOD_AUTO) == 0
+ && priv->rdisc
+ && priv->ac_ip6_config) {
+ NMSettingIPConfig *s_ip6;
+ char *hex_iid;
+ NMUtilsIPv6IfaceId iid = NM_UTILS_IPV6_IFACE_ID_INIT;
+
+ s_ip6 = nm_connection_get_setting_ip6_config (connection);
+ g_assert (s_ip6);
+
+ g_ptr_array_add (argv, g_strdup ("--slaac"));
+
+ if (nm_setting_ip_config_get_may_fail (s_ip6) == FALSE)
+ g_ptr_array_add (argv, g_strdup ("--slaac-required"));
+
+ g_ptr_array_add (argv, g_strdup ("--slaac-tempaddr"));
+ g_ptr_array_add (argv, g_strdup_printf ("%d", priv->rdisc_use_tempaddr));
+
+ if (nm_device_get_ip_iface_identifier (self, &iid)) {
+ g_ptr_array_add (argv, g_strdup ("--iid"));
+ hex_iid = bin2hexstr ((const char *) iid.id_u8, sizeof (NMUtilsIPv6IfaceId));
+ g_ptr_array_add (argv, hex_iid);
+ }
+
+ configured = TRUE;
+ }
+
+ if (configured) {
+ GPid pid;
+
+ if (!priority_set) {
+ g_ptr_array_add (argv, g_strdup ("--priority"));
+ g_ptr_array_add (argv, g_strdup_printf ("%u", nm_device_get_priority (self)));
+ }
+
+ g_ptr_array_add (argv, NULL);
+
+ if (nm_logging_enabled (LOGL_DEBUG, LOGD_DEVICE)) {
+ char *tmp;
+
+ tmp = g_strjoinv (" ", (char **) argv->pdata);
+ _LOGD (LOGD_DEVICE, "running '%s'", tmp);
+ g_free (tmp);
+ }
+
+ if (g_spawn_async (NULL, (char **) argv->pdata, NULL,
+ G_SPAWN_DO_NOT_REAP_CHILD, NULL, NULL, &pid, &error)) {
+ _LOGI (LOGD_DEVICE, "spawned helper PID %u", (guint) pid);
+ } else {
+ _LOGW (LOGD_DEVICE, "failed to spawn helper: %s", error->message);
+ g_error_free (error);
+ }
+ }
+
+ g_ptr_array_unref (argv);
+}
+
/***********************************************************/
static gboolean
diff --git a/src/devices/nm-device.h b/src/devices/nm-device.h
index f7b91e00cc..840a19c08b 100644
--- a/src/devices/nm-device.h
+++ b/src/devices/nm-device.h
@@ -365,6 +365,8 @@ NMConnection *nm_device_new_default_connection (NMDevice *self);
const NMPlatformIP4Route *nm_device_get_ip4_default_route (NMDevice *self);
const NMPlatformIP6Route *nm_device_get_ip6_default_route (NMDevice *self);
+void nm_device_spawn_iface_helper (NMDevice *self);
+
G_END_DECLS
/* For testing only */
diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c
index b6b571eb5f..0ffd573849 100644
--- a/src/dhcp-manager/nm-dhcp-client.c
+++ b/src/dhcp-manager/nm-dhcp-client.c
@@ -45,6 +45,8 @@ typedef struct {
guint32 priority;
guint32 timeout;
GByteArray * duid;
+ GBytes * client_id;
+ char * hostname;
NMDhcpState state;
pid_t pid;
@@ -143,6 +145,37 @@ nm_dhcp_client_get_priority (NMDhcpClient *self)
return NM_DHCP_CLIENT_GET_PRIVATE (self)->priority;
}
+GBytes *
+nm_dhcp_client_get_client_id (NMDhcpClient *self)
+{
+ g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+ return NM_DHCP_CLIENT_GET_PRIVATE (self)->client_id;
+}
+
+void
+nm_dhcp_client_set_client_id (NMDhcpClient *self, GBytes *client_id)
+{
+ NMDhcpClientPrivate *priv;
+
+ g_return_if_fail (NM_IS_DHCP_CLIENT (self));
+
+ priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+ if (priv->client_id && client_id && g_bytes_equal (priv->client_id, client_id))
+ return;
+ g_clear_pointer (&priv->client_id, g_bytes_unref);
+ priv->client_id = client_id ? g_bytes_ref (client_id) : NULL;
+}
+
+const char *
+nm_dhcp_client_get_hostname (NMDhcpClient *self)
+{
+ g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), NULL);
+
+ return NM_DHCP_CLIENT_GET_PRIVATE (self)->hostname;
+}
+
/********************************************/
static const char *state_table[NM_DHCP_STATE_MAX + 1] = {
@@ -360,7 +393,8 @@ gboolean
nm_dhcp_client_start_ip4 (NMDhcpClient *self,
const char *dhcp_client_id,
const char *dhcp_anycast_addr,
- const char *hostname)
+ const char *hostname,
+ const char *last_ip4_address)
{
NMDhcpClientPrivate *priv;
@@ -374,7 +408,12 @@ 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);
- return NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_client_id, dhcp_anycast_addr, hostname);
+ nm_dhcp_client_set_client_id (self, dhcp_client_id ? nm_dhcp_utils_client_id_string_to_bytes (dhcp_client_id) : NULL);
+
+ g_clear_pointer (&priv->hostname, g_free);
+ priv->hostname = g_strdup (hostname);
+
+ return NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_anycast_addr, last_ip4_address);
}
/* uuid_parse does not work for machine-id, so we use our own converter */
@@ -518,6 +557,9 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self,
g_free (str);
}
+ g_clear_pointer (&priv->hostname, g_free);
+ priv->hostname = g_strdup (hostname);
+
priv->info_only = info_only;
nm_log_info (LOGD_DHCP, "Activation (%s) Beginning DHCPv6 transaction (timeout in %d seconds)",
@@ -525,7 +567,6 @@ nm_dhcp_client_start_ip6 (NMDhcpClient *self,
return NM_DHCP_CLIENT_GET_CLASS (self)->ip6_start (self,
dhcp_anycast_addr,
- hostname,
info_only,
privacy,
priv->duid);
@@ -673,10 +714,13 @@ copy_option (const char * key,
g_hash_table_insert (hash, g_strdup (key), str_value);
}
-void
-nm_dhcp_client_new_options (NMDhcpClient *self,
- GHashTable *options,
- const char *reason)
+gboolean
+nm_dhcp_client_handle_event (gpointer unused,
+ const char *iface,
+ gint64 pid,
+ GHashTable *options,
+ const char *reason,
+ NMDhcpClient *self)
{
NMDhcpClientPrivate *priv;
guint32 old_state;
@@ -684,11 +728,19 @@ nm_dhcp_client_new_options (NMDhcpClient *self,
GHashTable *str_options = NULL;
GObject *ip_config = NULL;
- g_return_if_fail (NM_IS_DHCP_CLIENT (self));
- g_return_if_fail (options != NULL);
- g_return_if_fail (reason != NULL);
+ g_return_val_if_fail (NM_IS_DHCP_CLIENT (self), FALSE);
+ g_return_val_if_fail (iface != NULL, FALSE);
+ g_return_val_if_fail (pid > 0, FALSE);
+ g_return_val_if_fail (options != NULL, FALSE);
+ g_return_val_if_fail (reason != NULL, FALSE);
priv = NM_DHCP_CLIENT_GET_PRIVATE (self);
+
+ if (g_strcmp0 (priv->iface, iface) != 0)
+ return FALSE;
+ if (priv->pid != pid)
+ return FALSE;
+
old_state = priv->state;
new_state = reason_to_state (priv->iface, reason);
@@ -719,6 +771,8 @@ nm_dhcp_client_new_options (NMDhcpClient *self,
if (str_options)
g_hash_table_destroy (str_options);
g_clear_object (&ip_config);
+
+ return TRUE;
}
/********************************************/
diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h
index 89fe821db5..d732aa05ef 100644
--- a/src/dhcp-manager/nm-dhcp-client.h
+++ b/src/dhcp-manager/nm-dhcp-client.h
@@ -64,13 +64,11 @@ typedef struct {
/* Methods */
gboolean (*ip4_start) (NMDhcpClient *self,
- const char *dhcp_client_id,
const char *anycast_addr,
- const char *hostname);
+ const char *last_ip4_address);
gboolean (*ip6_start) (NMDhcpClient *self,
const char *anycast_addr,
- const char *hostname,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy,
const GByteArray *duid);
@@ -99,6 +97,17 @@ typedef struct {
GType nm_dhcp_client_get_type (void);
+typedef const char *(*NMDhcpClientGetPathFunc) (void);
+
+typedef GSList * (*NMDhcpClientGetLeaseConfigsFunc) (const char *iface,
+ const char *uuid,
+ gboolean ipv6);
+
+void _nm_dhcp_client_register (GType gtype,
+ const char *name,
+ NMDhcpClientGetPathFunc get_path_func,
+ NMDhcpClientGetLeaseConfigsFunc get_lease_configs_func);
+
pid_t nm_dhcp_client_get_pid (NMDhcpClient *self);
const char *nm_dhcp_client_get_iface (NMDhcpClient *self);
@@ -115,10 +124,15 @@ const GByteArray *nm_dhcp_client_get_hw_addr (NMDhcpClient *self);
guint32 nm_dhcp_client_get_priority (NMDhcpClient *self);
+GBytes *nm_dhcp_client_get_client_id (NMDhcpClient *self);
+
+const char *nm_dhcp_client_get_hostname (NMDhcpClient *self);
+
gboolean nm_dhcp_client_start_ip4 (NMDhcpClient *self,
const char *dhcp_client_id,
const char *dhcp_anycast_addr,
- const char *hostname);
+ const char *hostname,
+ const char *last_ip4_address);
gboolean nm_dhcp_client_start_ip6 (NMDhcpClient *self,
const char *dhcp_anycast_addr,
@@ -128,10 +142,6 @@ gboolean nm_dhcp_client_start_ip6 (NMDhcpClient *self,
void nm_dhcp_client_stop (NMDhcpClient *self, gboolean release);
-void nm_dhcp_client_new_options (NMDhcpClient *self,
- GHashTable *options,
- const char *reason);
-
/* Backend helpers for subclasses */
void nm_dhcp_client_stop_existing (const char *pid_file, const char *binary_name);
@@ -144,5 +154,14 @@ void nm_dhcp_client_set_state (NMDhcpClient *self,
GObject *ip_config, /* NMIP4Config or NMIP6Config */
GHashTable *options); /* str:str hash */
+gboolean nm_dhcp_client_handle_event (gpointer unused,
+ const char *iface,
+ gint64 pid,
+ GHashTable *options,
+ const char *reason,
+ NMDhcpClient *self);
+
+void nm_dhcp_client_set_client_id (NMDhcpClient *self, GBytes *client_id);
+
#endif /* __NETWORKMANAGER_DHCP_CLIENT_H__ */
diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.c b/src/dhcp-manager/nm-dhcp-dhclient-utils.c
index a53e95b009..3c3c42c84a 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient-utils.c
+++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.c
@@ -26,14 +26,14 @@
#include <arpa/inet.h>
#include "nm-dhcp-dhclient-utils.h"
+#include "nm-dhcp-utils.h"
#include "nm-ip4-config.h"
#include "nm-utils.h"
#include "nm-platform.h"
#include "NetworkManagerUtils.h"
+#include "gsystem-local-alloc.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 HOSTNAME4_TAG "send host-name"
#define HOSTNAME4_FORMAT HOSTNAME4_TAG " \"%s\"; # added by NetworkManager"
@@ -73,40 +73,39 @@ add_hostname (GString *str, const char *format, const char *hostname)
}
static void
-add_ip4_config (GString *str, const char *dhcp_client_id, const char *hostname)
+add_ip4_config (GString *str, GBytes *client_id, const char *hostname)
{
- if (dhcp_client_id && *dhcp_client_id) {
- gboolean is_octets = TRUE;
- int i = 0;
+ if (client_id) {
+ const char *p;
+ gsize l;
+ guint i;
- while (dhcp_client_id[i]) {
- if (!g_ascii_isxdigit (dhcp_client_id[i])) {
- is_octets = FALSE;
- break;
- }
- i++;
- if (!dhcp_client_id[i])
- break;
- if (g_ascii_isxdigit (dhcp_client_id[i])) {
- i++;
- if (!dhcp_client_id[i])
- break;
- }
- if (dhcp_client_id[i] != ':') {
- is_octets = FALSE;
+ p = g_bytes_get_data (client_id, &l);
+ g_assert (p);
+
+ /* Allow type 0 (non-hardware address) to be represented as a string
+ * as long as all the characters are printable.
+ */
+ for (i = 1; (p[0] == 0) && i < l; i++) {
+ if (!g_ascii_isprint (p[i]))
break;
- }
- i++;
}
- /* 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 (str, CLIENTID_FORMAT_OCTETS "\n", dhcp_client_id);
- else
- g_string_append_printf (str, CLIENTID_FORMAT "\n", dhcp_client_id);
+ g_string_append (str, CLIENTID_TAG " ");
+ if (i < l) {
+ /* Unprintable; convert to a hex string */
+ for (i = 0; i < l; i++) {
+ if (i > 0)
+ g_string_append_c (str, ':');
+ g_string_append_printf (str, "%02x", (guint8) p[i]);
+ }
+ } else {
+ /* Printable; just add to the line minus the 'type' */
+ g_string_append_c (str, '"');
+ g_string_append_len (str, p + 1, l - 1);
+ g_string_append_c (str, '"');
+ }
+ g_string_append (str, "; # added by NetworkManager\n");
}
add_hostname (str, HOSTNAME4_FORMAT "\n", hostname);
@@ -134,14 +133,67 @@ add_ip6_config (GString *str, const char *hostname)
"send fqdn.server-update on;\n");
}
+static GBytes *
+read_client_id (const char *str)
+{
+ gs_free char *s = NULL;
+ char *p;
+
+ g_assert (!strncmp (str, CLIENTID_TAG, STRLEN (CLIENTID_TAG)));
+
+ str += STRLEN (CLIENTID_TAG);
+ while (g_ascii_isspace (*str))
+ str++;
+
+ if (*str == '"') {
+ s = g_strdup (str + 1);
+ p = strrchr (s, '"');
+ if (p)
+ *p = '\0';
+ else
+ return NULL;
+ } else
+ s = g_strdup (str);
+
+ g_strchomp (s);
+ if (s[strlen (s) - 1] == ';')
+ s[strlen (s) - 1] = '\0';
+
+ return nm_dhcp_utils_client_id_string_to_bytes (s);
+}
+
+GBytes *
+nm_dhcp_dhclient_get_client_id_from_config_file (const char *path)
+{
+ gs_free char *contents = NULL;
+ gs_strfreev char **lines = NULL;
+ char **line;
+
+ g_return_val_if_fail (path != NULL, NULL);
+
+ if (!g_file_test (path, G_FILE_TEST_EXISTS))
+ return NULL;
+
+ if (!g_file_get_contents (path, &contents, NULL, NULL))
+ return NULL;
+
+ lines = g_strsplit_set (contents, "\n\r", 0);
+ for (line = lines; lines && *line; line++) {
+ if (!strncmp (*line, CLIENTID_TAG, STRLEN (CLIENTID_TAG)))
+ return read_client_id (*line);
+ }
+ return NULL;
+}
+
char *
nm_dhcp_dhclient_create_config (const char *interface,
gboolean is_ip6,
- const char *dhcp_client_id,
+ GBytes *client_id,
const char *anycast_addr,
const char *hostname,
const char *orig_path,
- const char *orig_contents)
+ const char *orig_contents,
+ GBytes **out_new_client_id)
{
GString *new_contents;
GPtrArray *alsoreq;
@@ -165,11 +217,15 @@ nm_dhcp_dhclient_create_config (const char *interface,
if (!strlen (g_strstrip (p)))
continue;
- /* Override config file "dhcp-client-id" and use one from the
- * connection.
- */
- if (dhcp_client_id && !strncmp (p, CLIENTID_TAG, strlen (CLIENTID_TAG)))
- continue;
+ if (!strncmp (p, CLIENTID_TAG, strlen (CLIENTID_TAG))) {
+ /* Override config file "dhcp-client-id" and use one from the connection */
+ if (client_id)
+ continue;
+
+ /* Otherwise capture and return the existing client id */
+ if (out_new_client_id)
+ *out_new_client_id = read_client_id (p);
+ }
/* Override config file hostname and use one from the connection */
if (hostname) {
@@ -238,7 +294,7 @@ nm_dhcp_dhclient_create_config (const char *interface,
add_also_request (alsoreq, "dhcp6.domain-search");
add_also_request (alsoreq, "dhcp6.client-id");
} else {
- add_ip4_config (new_contents, dhcp_client_id, hostname);
+ add_ip4_config (new_contents, client_id, hostname);
add_also_request (alsoreq, "rfc3442-classless-static-routes");
add_also_request (alsoreq, "ms-classless-static-routes");
add_also_request (alsoreq, "static-routes");
diff --git a/src/dhcp-manager/nm-dhcp-dhclient-utils.h b/src/dhcp-manager/nm-dhcp-dhclient-utils.h
index 3b786a65a6..a1828add4a 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient-utils.h
+++ b/src/dhcp-manager/nm-dhcp-dhclient-utils.h
@@ -27,11 +27,12 @@
char *nm_dhcp_dhclient_create_config (const char *interface,
gboolean is_ip6,
- const char *dhcp_client_id,
+ GBytes *client_id,
const char *anycast_addr,
const char *hostname,
const char *orig_path,
- const char *orig_contents);
+ const char *orig_contents,
+ GBytes **out_new_client_id);
char *nm_dhcp_dhclient_escape_duid (const GByteArray *duid);
@@ -48,5 +49,7 @@ GSList *nm_dhcp_dhclient_read_lease_ip_configs (const char *iface,
gboolean ipv6,
GDateTime *now);
+GBytes *nm_dhcp_dhclient_get_client_id_from_config_file (const char *path);
+
#endif /* __NETWORKMANAGER_DHCP_DHCLIENT_UTILS_H__ */
diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c
index e4b37377bc..4cabb2257b 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient.c
+++ b/src/dhcp-manager/nm-dhcp-dhclient.c
@@ -43,6 +43,8 @@
#include "nm-dhcp-manager.h"
#include "nm-posix-signals.h"
#include "NetworkManagerUtils.h"
+#include "nm-dhcp-listener.h"
+#include "gsystem-local-alloc.h"
G_DEFINE_TYPE (NMDhcpDhclient, nm_dhcp_dhclient, NM_TYPE_DHCP_CLIENT)
@@ -55,7 +57,7 @@ typedef struct {
char *pid_file;
} NMDhcpDhclientPrivate;
-const char *
+static const char *
nm_dhcp_dhclient_get_path (void)
{
const char *path = NULL;
@@ -122,7 +124,7 @@ get_dhclient_leasefile (const char *iface,
return NULL;
}
-GSList *
+static GSList *
nm_dhcp_dhclient_get_lease_ip_configs (const char *iface,
const char *uuid,
gboolean ipv6)
@@ -151,10 +153,11 @@ static gboolean
merge_dhclient_config (const char *iface,
const char *conf_file,
gboolean is_ip6,
- const char *dhcp_client_id,
+ GBytes *client_id,
const char *anycast_addr,
const char *hostname,
const char *orig_path,
+ GBytes **out_new_client_id,
GError **error)
{
char *orig = NULL, *new;
@@ -173,7 +176,7 @@ merge_dhclient_config (const char *iface,
}
}
- new = nm_dhcp_dhclient_create_config (iface, is_ip6, dhcp_client_id, anycast_addr, hostname, orig_path, orig);
+ new = nm_dhcp_dhclient_create_config (iface, is_ip6, client_id, anycast_addr, hostname, orig_path, orig, out_new_client_id);
g_assert (new);
success = g_file_set_contents (conf_file, new, -1, error);
g_free (new);
@@ -257,9 +260,10 @@ static char *
create_dhclient_config (const char *iface,
gboolean is_ip6,
const char *uuid,
- const char *dhcp_client_id,
+ GBytes *client_id,
const char *dhcp_anycast_addr,
- const char *hostname)
+ const char *hostname,
+ GBytes **out_new_client_id)
{
char *orig = NULL, *new = NULL;
GError *error = NULL;
@@ -284,7 +288,7 @@ create_dhclient_config (const char *iface,
}
error = NULL;
- success = merge_dhclient_config (iface, new, is_ip6, dhcp_client_id, dhcp_anycast_addr, hostname, orig, &error);
+ success = merge_dhclient_config (iface, new, is_ip6, client_id, dhcp_anycast_addr, hostname, orig, out_new_client_id, &error);
if (!success) {
nm_log_warn (LOGD_DHCP, "(%s): error creating dhclient%s configuration: %s",
iface, is_ip6 ? "6" : "", error->message);
@@ -473,41 +477,45 @@ dhclient_start (NMDhcpClient *client,
}
static gboolean
-ip4_start (NMDhcpClient *client,
- const char *dhcp_client_id,
- const char *dhcp_anycast_addr,
- const char *hostname)
+ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address)
{
NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
- const char *iface, *uuid;
+ GBytes *client_id;
+ gs_unref_bytes GBytes *new_client_id = NULL;
+ const char *iface, *uuid, *hostname;
+ gboolean success = FALSE;
iface = nm_dhcp_client_get_iface (client);
uuid = nm_dhcp_client_get_uuid (client);
+ client_id = nm_dhcp_client_get_client_id (client);
+ hostname = nm_dhcp_client_get_hostname (client);
- priv->conf_file = create_dhclient_config (iface, FALSE, uuid, dhcp_client_id, dhcp_anycast_addr, hostname);
- if (!priv->conf_file) {
+ priv->conf_file = create_dhclient_config (iface, FALSE, uuid, client_id, dhcp_anycast_addr, hostname, &new_client_id);
+ if (priv->conf_file) {
+ if (new_client_id)
+ nm_dhcp_client_set_client_id (client, new_client_id);
+ success = dhclient_start (client, NULL, NULL, FALSE, NULL);
+ } else
nm_log_warn (LOGD_DHCP4, "(%s): error creating dhclient configuration file.", iface);
- return FALSE;
- }
- return dhclient_start (client, NULL, NULL, FALSE, NULL);
+ return success;
}
static gboolean
ip6_start (NMDhcpClient *client,
const char *dhcp_anycast_addr,
- const char *hostname,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy,
const GByteArray *duid)
{
NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
- const char *iface, *uuid;
+ const char *iface, *uuid, *hostname;
iface = nm_dhcp_client_get_iface (client);
uuid = nm_dhcp_client_get_uuid (client);
+ hostname = nm_dhcp_client_get_hostname (client);
- priv->conf_file = create_dhclient_config (iface, TRUE, uuid, NULL, dhcp_anycast_addr, hostname);
+ priv->conf_file = create_dhclient_config (iface, TRUE, uuid, NULL, dhcp_anycast_addr, hostname, NULL);
if (!priv->conf_file) {
nm_log_warn (LOGD_DHCP6, "(%s): error creating dhclient6 configuration file.", iface);
return FALSE;
@@ -544,6 +552,24 @@ stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
}
}
+static void
+state_changed (NMDhcpClient *client,
+ NMDhcpState state,
+ GObject *ip_config,
+ GHashTable *options)
+{
+ NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client);
+ gs_unref_bytes GBytes *client_id = NULL;
+
+ if (nm_dhcp_client_get_client_id (client))
+ return;
+ if (state != NM_DHCP_STATE_BOUND)
+ return;
+
+ client_id = nm_dhcp_dhclient_get_client_id_from_config_file (priv->conf_file);
+ nm_dhcp_client_set_client_id (client, client_id);
+}
+
static GByteArray *
get_duid (NMDhcpClient *client)
{
@@ -612,6 +638,11 @@ nm_dhcp_dhclient_init (NMDhcpDhclient *self)
/* Fallback option */
if (!priv->def_leasefile)
priv->def_leasefile = SYSCONFDIR "/dhclient6.leases";
+
+ g_signal_connect (nm_dhcp_listener_get (),
+ NM_DHCP_LISTENER_EVENT,
+ G_CALLBACK (nm_dhcp_client_handle_event),
+ self);
}
static void
@@ -619,6 +650,10 @@ dispose (GObject *object)
{
NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (object);
+ g_signal_handlers_disconnect_by_func (nm_dhcp_listener_get (),
+ G_CALLBACK (nm_dhcp_client_handle_event),
+ NM_DHCP_DHCLIENT (object));
+
g_free (priv->pid_file);
g_free (priv->conf_file);
g_free (priv->lease_file);
@@ -641,5 +676,16 @@ nm_dhcp_dhclient_class_init (NMDhcpDhclientClass *dhclient_class)
client_class->ip6_start = ip6_start;
client_class->stop = stop;
client_class->get_duid = get_duid;
+ client_class->state_changed = state_changed;
+}
+
+static void __attribute__((constructor))
+register_dhcp_dhclient (void)
+{
+ g_type_init ();
+ _nm_dhcp_client_register (NM_TYPE_DHCP_DHCLIENT,
+ "dhclient",
+ nm_dhcp_dhclient_get_path,
+ nm_dhcp_dhclient_get_lease_ip_configs);
}
diff --git a/src/dhcp-manager/nm-dhcp-dhclient.h b/src/dhcp-manager/nm-dhcp-dhclient.h
index 6c58826241..5abcc08ee3 100644
--- a/src/dhcp-manager/nm-dhcp-dhclient.h
+++ b/src/dhcp-manager/nm-dhcp-dhclient.h
@@ -41,11 +41,5 @@ typedef struct {
GType nm_dhcp_dhclient_get_type (void);
-GSList *nm_dhcp_dhclient_get_lease_ip_configs (const char *iface,
- const char *uuid,
- gboolean ipv6);
-
-const char *nm_dhcp_dhclient_get_path (void);
-
#endif /* __NETWORKMANAGER_DHCP_DHCLIENT_H__ */
diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c
index a018c4cd42..9aacba7667 100644
--- a/src/dhcp-manager/nm-dhcp-dhcpcd.c
+++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c
@@ -38,6 +38,7 @@
#include "nm-logging.h"
#include "nm-posix-signals.h"
#include "NetworkManagerUtils.h"
+#include "nm-dhcp-listener.h"
G_DEFINE_TYPE (NMDhcpDhcpcd, nm_dhcp_dhcpcd, NM_TYPE_DHCP_CLIENT)
@@ -47,7 +48,7 @@ typedef struct {
char *pid_file;
} NMDhcpDhcpcdPrivate;
-const char *
+static const char *
nm_dhcp_dhcpcd_get_path (void)
{
const char *path = NULL;
@@ -72,17 +73,14 @@ dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED)
}
static gboolean
-ip4_start (NMDhcpClient *client,
- const char *dhcp_client_id,
- const char *dhcp_anycast_addr,
- const char *hostname)
+ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address)
{
NMDhcpDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (client);
GPtrArray *argv = NULL;
pid_t pid = -1;
GError *error = NULL;
char *pid_contents = NULL, *binary_name, *cmd_str;
- const char *iface, *dhcpcd_path = NULL;
+ const char *iface, *dhcpcd_path, *hostname;
g_return_val_if_fail (priv->pid_file == NULL, FALSE);
@@ -129,7 +127,8 @@ ip4_start (NMDhcpClient *client,
g_ptr_array_add (argv, (gpointer) "-4");
#endif
- if (hostname && strlen (hostname)) {
+ hostname = nm_dhcp_client_get_hostname (client);
+ if (hostname) {
g_ptr_array_add (argv, (gpointer) "-h"); /* Send hostname to DHCP server */
g_ptr_array_add (argv, (gpointer) hostname );
}
@@ -160,7 +159,6 @@ ip4_start (NMDhcpClient *client,
static gboolean
ip6_start (NMDhcpClient *client,
const char *dhcp_anycast_addr,
- const char *hostname,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy,
const GByteArray *duid)
@@ -190,6 +188,10 @@ stop (NMDhcpClient *client, gboolean release, const GByteArray *duid)
static void
nm_dhcp_dhcpcd_init (NMDhcpDhcpcd *self)
{
+ g_signal_connect (nm_dhcp_listener_get (),
+ NM_DHCP_LISTENER_EVENT,
+ G_CALLBACK (nm_dhcp_client_handle_event),
+ self);
}
static void
@@ -197,6 +199,10 @@ dispose (GObject *object)
{
NMDhcpDhcpcdPrivate *priv = NM_DHCP_DHCPCD_GET_PRIVATE (object);
+ g_signal_handlers_disconnect_by_func (nm_dhcp_listener_get (),
+ G_CALLBACK (nm_dhcp_client_handle_event),
+ NM_DHCP_DHCPCD (object));
+
g_free (priv->pid_file);
G_OBJECT_CLASS (nm_dhcp_dhcpcd_parent_class)->dispose (object);
@@ -218,3 +224,13 @@ nm_dhcp_dhcpcd_class_init (NMDhcpDhcpcdClass *dhcpcd_class)
client_class->stop = stop;
}
+static void __attribute__((constructor))
+register_dhcp_dhclient (void)
+{
+ g_type_init ();
+ _nm_dhcp_client_register (NM_TYPE_DHCP_DHCPCD,
+ "dhcpcd",
+ nm_dhcp_dhcpcd_get_path,
+ NULL);
+}
+
diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.h b/src/dhcp-manager/nm-dhcp-dhcpcd.h
index 4e3502ab5a..deed70e0f9 100644
--- a/src/dhcp-manager/nm-dhcp-dhcpcd.h
+++ b/src/dhcp-manager/nm-dhcp-dhcpcd.h
@@ -41,7 +41,5 @@ typedef struct {
GType nm_dhcp_dhcpcd_get_type (void);
-const char *nm_dhcp_dhcpcd_get_path (void);
-
#endif /* __NETWORKMANAGER_DHCP_DHCPCD_H__ */
diff --git a/src/dhcp-manager/nm-dhcp-listener.c b/src/dhcp-manager/nm-dhcp-listener.c
new file mode 100644
index 0000000000..ce6dfafad4
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-listener.c
@@ -0,0 +1,289 @@
+/* -*- 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 2014 Red Hat, Inc.
+ *
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <dbus/dbus.h>
+#include <sys/socket.h>
+#include <sys/wait.h>
+#include <signal.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "nm-dhcp-listener.h"
+#include "nm-logging.h"
+#include "nm-dbus-manager.h"
+#include "nm-dbus-glib-types.h"
+#include "nm-glib-compat.h"
+#include "NetworkManagerUtils.h"
+
+#define NM_DHCP_CLIENT_DBUS_IFACE "org.freedesktop.nm_dhcp_client"
+#define PRIV_SOCK_PATH NMRUNDIR "/private-dhcp"
+#define PRIV_SOCK_TAG "dhcp"
+
+typedef struct {
+ NMDBusManager * dbus_mgr;
+ guint new_conn_id;
+ guint dis_conn_id;
+ GHashTable * proxies;
+ DBusGProxy * proxy;
+} NMDhcpListenerPrivate;
+
+#define NM_DHCP_LISTENER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_LISTENER, NMDhcpListenerPrivate))
+
+G_DEFINE_TYPE (NMDhcpListener, nm_dhcp_listener, G_TYPE_OBJECT)
+
+enum {
+ EVENT,
+ LAST_SIGNAL
+};
+static guint signals[LAST_SIGNAL] = { 0 };
+
+/***************************************************/
+
+static char *
+garray_to_string (GArray *array, const char *key)
+{
+ GString *str;
+ int i;
+ unsigned char c;
+ char *converted = NULL;
+
+ g_return_val_if_fail (array != NULL, NULL);
+
+ /* Since the DHCP options come through environment variables, they should
+ * already be UTF-8 safe, but just make sure.
+ */
+ str = g_string_sized_new (array->len);
+ for (i = 0; i < array->len; i++) {
+ c = array->data[i];
+
+ /* Convert NULLs to spaces and non-ASCII characters to ? */
+ if (c == '\0')
+ c = ' ';
+ else if (c > 127)
+ c = '?';
+ str = g_string_append_c (str, c);
+ }
+ str = g_string_append_c (str, '\0');
+
+ converted = str->str;
+ if (!g_utf8_validate (converted, -1, NULL))
+ nm_log_warn (LOGD_DHCP, "DHCP option '%s' couldn't be converted to UTF-8", key);
+ g_string_free (str, FALSE);
+ return converted;
+}
+
+static char *
+get_option (GHashTable *hash, const char *key)
+{
+ GValue *value;
+
+ value = g_hash_table_lookup (hash, key);
+ if (value == NULL)
+ return NULL;
+
+ if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) {
+ nm_log_warn (LOGD_DHCP, "unexpected key %s value type was not "
+ "DBUS_TYPE_G_UCHAR_ARRAY",
+ (char *) key);
+ return NULL;
+ }
+
+ return garray_to_string ((GArray *) g_value_get_boxed (value), key);
+}
+
+static void
+handle_event (DBusGProxy *proxy,
+ GHashTable *options,
+ gpointer user_data)
+{
+ NMDhcpListener *self = NM_DHCP_LISTENER (user_data);
+ char *iface = NULL;
+ char *pid_str = NULL;
+ char *reason = NULL;
+ gint32 pid;
+ gboolean handled = FALSE;
+
+ iface = get_option (options, "interface");
+ if (iface == NULL) {
+ nm_log_warn (LOGD_DHCP, "DHCP event: didn't have associated interface.");
+ goto out;
+ }
+
+ pid_str = get_option (options, "pid");
+ pid = (gint32) nm_utils_ascii_str_to_int64 (pid_str, 10, 0, G_MAXINT32, -1);
+ if (pid == -1 || pid != (GPid) pid) {
+ nm_log_warn (LOGD_DHCP, "DHCP event: couldn't convert PID '%s' to an integer", pid_str ? pid_str : "(null)");
+ goto out;
+ }
+
+ reason = get_option (options, "reason");
+ if (reason == NULL) {
+ nm_log_warn (LOGD_DHCP, "(pid %d) DHCP event didn't have a reason", pid);
+ goto out;
+ }
+
+ g_signal_emit (self, signals[EVENT], 0, iface, pid, options, reason, &handled);
+ if (!handled) {
+ if (g_ascii_strcasecmp (reason, "RELEASE") == 0) {
+ /* Ignore event when the dhcp client gets killed and we receive its last message */
+ nm_log_dbg (LOGD_DHCP, "(pid %d) unhandled RELEASE DHCP event for interface %s", pid, iface);
+ } else
+ nm_log_warn (LOGD_DHCP, "(pid %d) unhandled DHCP event for interface %s", pid, iface);
+ }
+
+out:
+ g_free (iface);
+ g_free (pid_str);
+ g_free (reason);
+}
+
+#if HAVE_DBUS_GLIB_100
+static void
+new_connection_cb (NMDBusManager *mgr,
+ DBusGConnection *connection,
+ NMDhcpListener *self)
+{
+ DBusGProxy *proxy;
+
+ /* Create a new proxy for the client */
+ proxy = dbus_g_proxy_new_for_peer (connection, "/", NM_DHCP_CLIENT_DBUS_IFACE);
+ dbus_g_proxy_add_signal (proxy, "Event", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (proxy, "Event", G_CALLBACK (handle_event), self, NULL);
+
+ g_hash_table_insert (NM_DHCP_LISTENER_GET_PRIVATE (self)->proxies, connection, proxy);
+}
+
+static void
+dis_connection_cb (NMDBusManager *mgr,
+ DBusGConnection *connection,
+ NMDhcpListener *self)
+{
+ NMDhcpListenerPrivate *priv = NM_DHCP_LISTENER_GET_PRIVATE (self);
+ DBusGProxy *proxy;
+
+ proxy = g_hash_table_lookup (priv->proxies, connection);
+ if (proxy) {
+ dbus_g_proxy_disconnect_signal (proxy, "Event", G_CALLBACK (handle_event), self);
+ g_hash_table_remove (priv->proxies, connection);
+ }
+}
+#endif
+
+/***************************************************/
+
+NMDhcpListener *
+nm_dhcp_listener_get (void)
+{
+ static NMDhcpListener *singleton = NULL;
+
+ if (G_UNLIKELY (singleton == NULL))
+ singleton = g_object_new (NM_TYPE_DHCP_LISTENER, NULL);
+ g_assert (singleton);
+ return singleton;
+}
+
+static void
+nm_dhcp_listener_init (NMDhcpListener *self)
+{
+ NMDhcpListenerPrivate *priv = NM_DHCP_LISTENER_GET_PRIVATE (self);
+#if !HAVE_DBUS_GLIB_100
+ DBusGConnection *g_connection;
+#endif
+
+ /* Maps DBusGConnection :: DBusGProxy */
+ priv->proxies = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
+
+ priv->dbus_mgr = nm_dbus_manager_get ();
+
+#if HAVE_DBUS_GLIB_100
+ /* Register the socket our DHCP clients will return lease info on */
+ nm_dbus_manager_private_server_register (priv->dbus_mgr, PRIV_SOCK_PATH, PRIV_SOCK_TAG);
+ priv->new_conn_id = g_signal_connect (priv->dbus_mgr,
+ NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW "::" PRIV_SOCK_TAG,
+ G_CALLBACK (new_connection_cb),
+ self);
+ priv->dis_conn_id = g_signal_connect (priv->dbus_mgr,
+ NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED "::" PRIV_SOCK_TAG,
+ G_CALLBACK (dis_connection_cb),
+ self);
+#else
+ g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
+ priv->proxy = dbus_g_proxy_new_for_name (g_connection,
+ "org.freedesktop.nm_dhcp_client",
+ "/",
+ NM_DHCP_CLIENT_DBUS_IFACE);
+ g_assert (priv->proxy);
+ dbus_g_proxy_add_signal (priv->proxy, "Event", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID);
+ dbus_g_proxy_connect_signal (priv->proxy, "Event", G_CALLBACK (handle_event), self, NULL);
+#endif
+}
+
+static void
+dispose (GObject *object)
+{
+ NMDhcpListenerPrivate *priv = NM_DHCP_LISTENER_GET_PRIVATE (object);
+
+ if (priv->new_conn_id) {
+ g_signal_handler_disconnect (priv->dbus_mgr, priv->new_conn_id);
+ priv->new_conn_id = 0;
+ }
+ if (priv->dis_conn_id) {
+ g_signal_handler_disconnect (priv->dbus_mgr, priv->dis_conn_id);
+ priv->dis_conn_id = 0;
+ }
+ priv->dbus_mgr = NULL;
+
+ if (priv->proxies) {
+ g_hash_table_destroy (priv->proxies);
+ priv->proxies = NULL;
+ }
+ g_clear_object (&priv->proxy);
+
+ G_OBJECT_CLASS (nm_dhcp_listener_parent_class)->dispose (object);
+}
+
+static void
+nm_dhcp_listener_class_init (NMDhcpListenerClass *listener_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (listener_class);
+
+ g_type_class_add_private (listener_class, sizeof (NMDhcpListenerPrivate));
+
+ /* virtual methods */
+ object_class->dispose = dispose;
+
+ /* signals */
+ signals[EVENT] =
+ g_signal_new (NM_DHCP_LISTENER_EVENT,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST, 0,
+ g_signal_accumulator_true_handled,
+ NULL, NULL,
+ G_TYPE_BOOLEAN, /* listeners return TRUE if handled */
+ 4,
+ G_TYPE_STRING, /* iface */
+ G_TYPE_INT64, /* pid */
+ G_TYPE_HASH_TABLE, /* options */
+ G_TYPE_STRING); /* reason */
+}
diff --git a/src/dhcp-manager/nm-dhcp-listener.h b/src/dhcp-manager/nm-dhcp-listener.h
new file mode 100644
index 0000000000..15ec053109
--- /dev/null
+++ b/src/dhcp-manager/nm-dhcp-listener.h
@@ -0,0 +1,39 @@
+/* -*- 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 2014 Red Hat, Inc.
+ */
+
+#ifndef __NETWORKMANAGER_DHCP_LISTENER_H__
+#define __NETWORKMANAGER_DHCP_LISTENER_H__
+
+#include <glib.h>
+#include <glib-object.h>
+
+#define NM_TYPE_DHCP_LISTENER (nm_dhcp_listener_get_type ())
+#define NM_DHCP_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), NM_TYPE_DHCP_LISTENER, NMDhcpListener))
+#define NM_IS_DHCP_LISTENER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), NM_TYPE_DHCP_LISTENER))
+#define NM_DHCP_LISTENER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), NM_TYPE_DHCP_LISTENER, NMDhcpListenerClass))
+
+#define NM_DHCP_LISTENER_EVENT "event"
+
+typedef GObject NMDhcpListener;
+typedef GObjectClass NMDhcpListenerClass;
+
+GType nm_dhcp_listener_get_type (void);
+
+NMDhcpListener *nm_dhcp_listener_get (void);
+
+#endif /* __NETWORKMANAGER_DHCP_LISTENER_H__ */
diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c
index 5c139b8ca2..3147980b4b 100644
--- a/src/dhcp-manager/nm-dhcp-manager.c
+++ b/src/dhcp-manager/nm-dhcp-manager.c
@@ -23,7 +23,6 @@
#include "config.h"
#include <glib.h>
#include <glib/gi18n.h>
-#include <dbus/dbus.h>
#include <sys/socket.h>
#include <sys/wait.h>
#include <signal.h>
@@ -39,19 +38,13 @@
#include "nm-dhcp-dhcpcd.h"
#include "nm-dhcp-systemd.h"
#include "nm-logging.h"
-#include "nm-dbus-manager.h"
#include "nm-config.h"
#include "nm-dbus-glib-types.h"
#include "nm-glib-compat.h"
#include "NetworkManagerUtils.h"
-#define NM_DHCP_CLIENT_DBUS_IFACE "org.freedesktop.nm_dhcp_client"
-
#define DHCP_TIMEOUT 45 /* default DHCP timeout, in seconds */
-#define PRIV_SOCK_PATH NMRUNDIR "/private-dhcp"
-#define PRIV_SOCK_TAG "dhcp"
-
/* default to installed helper, but can be modified for testing */
const char *nm_dhcp_helper_path = LIBEXECDIR "/nm-dhcp-helper";
@@ -59,78 +52,89 @@ typedef GSList * (*GetLeaseConfigFunc) (const char *iface, const char *uuid, gbo
typedef struct {
GType client_type;
- GetLeaseConfigFunc get_lease_ip_configs_func;
-
- NMDBusManager * dbus_mgr;
- guint new_conn_id;
- guint dis_conn_id;
- GHashTable * proxies;
-
GHashTable * clients;
- DBusGProxy * proxy;
char * default_hostname;
} NMDhcpManagerPrivate;
-
#define NM_DHCP_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_DHCP_MANAGER, NMDhcpManagerPrivate))
G_DEFINE_TYPE (NMDhcpManager, nm_dhcp_manager, G_TYPE_OBJECT)
-static char *
-garray_to_string (GArray *array, const char *key)
+/***************************************************/
+
+typedef struct {
+ GType gtype;
+ const char *name;
+ NMDhcpClientGetPathFunc get_path_func;
+ NMDhcpClientGetLeaseConfigsFunc get_lease_configs_func;
+} ClientDesc;
+
+static GSList *client_descs = NULL;
+
+void
+_nm_dhcp_client_register (GType gtype,
+ const char *name,
+ NMDhcpClientGetPathFunc get_path_func,
+ NMDhcpClientGetLeaseConfigsFunc get_lease_configs_func)
{
- GString *str;
- int i;
- unsigned char c;
- char *converted = NULL;
+ ClientDesc *desc;
+ GSList *iter;
- g_return_val_if_fail (array != NULL, NULL);
+ g_return_if_fail (gtype != G_TYPE_INVALID);
+ g_return_if_fail (name != NULL);
- /* Since the DHCP options come through environment variables, they should
- * already be UTF-8 safe, but just make sure.
- */
- str = g_string_sized_new (array->len);
- for (i = 0; i < array->len; i++) {
- c = array->data[i];
-
- /* Convert NULLs to spaces and non-ASCII characters to ? */
- if (c == '\0')
- c = ' ';
- else if (c > 127)
- c = '?';
- str = g_string_append_c (str, c);
+ for (iter = client_descs; iter; iter = iter->next) {
+ desc = iter->data;
+ g_return_if_fail (desc->gtype != gtype);
+ g_return_if_fail (strcmp (desc->name, name) != 0);
}
- str = g_string_append_c (str, '\0');
- converted = str->str;
- if (!g_utf8_validate (converted, -1, NULL))
- nm_log_warn (LOGD_DHCP, "DHCP option '%s' couldn't be converted to UTF-8", key);
- g_string_free (str, FALSE);
- return converted;
+ desc = g_slice_new0 (ClientDesc);
+ desc->gtype = gtype;
+ desc->name = name;
+ desc->get_path_func = get_path_func;
+ desc->get_lease_configs_func = get_lease_configs_func;
+ client_descs = g_slist_prepend (client_descs, desc);
+
+ nm_log_info (LOGD_DHCP, "Registered DHCP client '%s'", name);
}
-static NMDhcpClient *
-get_client_for_pid (NMDhcpManager *manager, GPid pid)
+static ClientDesc *
+find_client_desc (const char *name, GType gtype)
{
- NMDhcpManagerPrivate *priv;
- GHashTableIter iter;
- gpointer value;
+ GSList *iter;
- g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), NULL);
+ g_return_val_if_fail (name || gtype, NULL);
- priv = NM_DHCP_MANAGER_GET_PRIVATE (manager);
-
- g_hash_table_iter_init (&iter, priv->clients);
- while (g_hash_table_iter_next (&iter, NULL, &value)) {
- NMDhcpClient *candidate = NM_DHCP_CLIENT (value);
+ for (iter = client_descs; iter; iter = iter->next) {
+ ClientDesc *desc = iter->data;
- if (nm_dhcp_client_get_pid (candidate) == pid)
- return candidate;
+ if (name && strcmp (desc->name, name) != 0)
+ continue;
+ if (gtype && desc->name != 0)
+ continue;
+ return desc;
}
-
return NULL;
}
+static GType
+is_client_enabled (const char *name, GError **error)
+{
+ ClientDesc *desc;
+
+ desc = find_client_desc (name, G_TYPE_INVALID);
+ if (desc && (!desc->get_path_func || desc->get_path_func()))
+ return desc->gtype;
+
+ g_set_error (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
+ _("'%s' support not found or not enabled."),
+ name);
+ return G_TYPE_INVALID;
+}
+
+/***************************************************/
+
static NMDhcpClient *
get_client_for_ifindex (NMDhcpManager *manager, int ifindex, gboolean ip6)
{
@@ -155,172 +159,26 @@ get_client_for_ifindex (NMDhcpManager *manager, int ifindex, gboolean ip6)
return NULL;
}
-static char *
-get_option (GHashTable *hash, const char *key)
-{
- GValue *value;
-
- value = g_hash_table_lookup (hash, key);
- if (value == NULL)
- return NULL;
-
- if (G_VALUE_TYPE (value) != DBUS_TYPE_G_UCHAR_ARRAY) {
- nm_log_warn (LOGD_DHCP, "unexpected key %s value type was not "
- "DBUS_TYPE_G_UCHAR_ARRAY",
- (char *) key);
- return NULL;
- }
-
- return garray_to_string ((GArray *) g_value_get_boxed (value), key);
-}
-
-static void
-nm_dhcp_manager_handle_event (DBusGProxy *proxy,
- GHashTable *options,
- gpointer user_data)
-{
- NMDhcpManager *manager = NM_DHCP_MANAGER (user_data);
- NMDhcpClient *client;
- char *iface = NULL;
- char *pid_str = NULL;
- char *reason = NULL;
- long pid;
-
- iface = get_option (options, "interface");
- if (iface == NULL) {
- nm_log_warn (LOGD_DHCP, "DHCP event: didn't have associated interface.");
- goto out;
- }
-
- pid_str = get_option (options, "pid");
- pid = nm_utils_ascii_str_to_int64 (pid_str, 10, 0, LONG_MAX, -1);
- if (pid == -1 || pid != (GPid)pid) {
- nm_log_warn (LOGD_DHCP, "DHCP event: couldn't convert PID '%s' to an integer", pid_str ? pid_str : "(null)");
- goto out;
- }
-
- reason = get_option (options, "reason");
- client = get_client_for_pid (manager, (GPid) pid);
- if (client == NULL) {
- if (reason && g_ascii_strcasecmp (reason, "RELEASE") == 0) {
- /* This happens regularly, when the dhcp client gets killed and we receive its last message.
- * Don't log a warning in this case. */
- nm_log_dbg (LOGD_DHCP, "(pid %ld) unhandled RELEASE DHCP event for interface %s", pid, iface);
- } else
- nm_log_warn (LOGD_DHCP, "(pid %ld) unhandled DHCP event for interface %s", pid, iface);
- goto out;
- }
-
- if (strcmp (iface, nm_dhcp_client_get_iface (client))) {
- nm_log_warn (LOGD_DHCP, "(pid %ld) received DHCP event from unexpected interface '%s' (expected '%s')",
- pid, iface, nm_dhcp_client_get_iface (client));
- goto out;
- }
-
- if (reason == NULL) {
- nm_log_warn (LOGD_DHCP, "(pid %ld) DHCP event didn't have a reason", pid);
- goto out;
- }
-
- nm_dhcp_client_new_options (client, options, reason);
-
-out:
- g_free (iface);
- g_free (pid_str);
- g_free (reason);
-}
-
-#if HAVE_DBUS_GLIB_100
-static void
-new_connection_cb (NMDBusManager *mgr,
- DBusGConnection *connection,
- NMDhcpManager *self)
-{
- NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
- DBusGProxy *proxy;
-
- /* Create a new proxy for the client */
- proxy = dbus_g_proxy_new_for_peer (connection, "/", NM_DHCP_CLIENT_DBUS_IFACE);
- dbus_g_proxy_add_signal (proxy,
- "Event",
- DBUS_TYPE_G_MAP_OF_VARIANT,
- G_TYPE_INVALID);
- dbus_g_proxy_connect_signal (proxy,
- "Event",
- G_CALLBACK (nm_dhcp_manager_handle_event),
- self,
- NULL);
- g_hash_table_insert (priv->proxies, connection, proxy);
-}
-
-static void
-dis_connection_cb (NMDBusManager *mgr,
- DBusGConnection *connection,
- NMDhcpManager *self)
-{
- NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
- DBusGProxy *proxy;
-
- proxy = g_hash_table_lookup (priv->proxies, connection);
- if (proxy) {
- dbus_g_proxy_disconnect_signal (proxy,
- "Event",
- G_CALLBACK (nm_dhcp_manager_handle_event),
- self);
- g_hash_table_remove (priv->proxies, connection);
- }
-}
-#endif
-
static GType
get_client_type (const char *client, GError **error)
{
- gboolean use_dhclient, use_dhcpcd;
-
- /* If a client was disabled at build-time, these will return FALSE */
- use_dhclient = !!nm_dhcp_dhclient_get_path ();
- use_dhcpcd = !!nm_dhcp_dhcpcd_get_path ();
-
- if (!client) {
- if (use_dhclient)
- return NM_TYPE_DHCP_DHCLIENT;
- else if (use_dhcpcd)
- return NM_TYPE_DHCP_DHCPCD;
- else {
- g_set_error_literal (error,
- NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
- _("no usable DHCP client could be found."));
- return G_TYPE_INVALID;
+ GType client_gtype;
+
+ if (client)
+ client_gtype = is_client_enabled (client, error);
+ else {
+ /* Fallbacks */
+ client_gtype = is_client_enabled ("dhclient", NULL);
+ if (client_gtype == G_TYPE_INVALID)
+ client_gtype = is_client_enabled ("dhcpcd", NULL);
+ if (client_gtype == G_TYPE_INVALID)
+ client_gtype = is_client_enabled ("internal", NULL);
+ if (client_gtype == G_TYPE_INVALID) {
+ g_set_error_literal (error, NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
+ _("no usable DHCP client could be found."));
}
}
-
- if (!strcmp (client, "dhclient")) {
- if (!use_dhclient) {
- g_set_error_literal (error,
- NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
- _("'dhclient' could not be found or was disabled."));
- return G_TYPE_INVALID;
- }
- return NM_TYPE_DHCP_DHCLIENT;
- }
-
- if (!strcmp (client, "dhcpcd")) {
- if (!use_dhcpcd) {
- g_set_error_literal (error,
- NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
- _("'dhcpcd' could not be found or was disabled."));
- return G_TYPE_INVALID;
- }
- return NM_TYPE_DHCP_DHCPCD;
- }
-
- if (!strcmp (client, "internal"))
- return NM_TYPE_DHCP_SYSTEMD;
-
- g_set_error (error,
- NM_MANAGER_ERROR, NM_MANAGER_ERROR_FAILED,
- _("unsupported DHCP client '%s'"), client);
- return G_TYPE_INVALID;
+ return client_gtype;
}
static void client_state_changed (NMDhcpClient *client,
@@ -366,7 +224,8 @@ client_start (NMDhcpManager *self,
const char *dhcp_anycast_addr,
const char *hostname,
gboolean info_only,
- NMSettingIP6ConfigPrivacy privacy)
+ NMSettingIP6ConfigPrivacy privacy,
+ const char *last_ip4_address)
{
NMDhcpManagerPrivate *priv;
NMDhcpClient *client;
@@ -407,7 +266,7 @@ client_start (NMDhcpManager *self,
if (ipv6)
success = nm_dhcp_client_start_ip6 (client, dhcp_anycast_addr, hostname, info_only, privacy);
else
- success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname);
+ success = nm_dhcp_client_start_ip4 (client, dhcp_client_id, dhcp_anycast_addr, hostname, last_ip4_address);
if (!success) {
remove_client (self, client);
@@ -438,7 +297,8 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
const char *dhcp_hostname,
const char *dhcp_client_id,
guint32 timeout,
- const char *dhcp_anycast_addr)
+ const char *dhcp_anycast_addr,
+ const char *last_ip_address)
{
const char *hostname = NULL;
@@ -448,7 +308,7 @@ nm_dhcp_manager_start_ip4 (NMDhcpManager *self,
hostname = get_send_hostname (self, dhcp_hostname);
return client_start (self, iface, ifindex, hwaddr, uuid, priority, FALSE,
dhcp_client_id, timeout, dhcp_anycast_addr, hostname,
- FALSE, 0);
+ FALSE, 0, last_ip_address);
}
/* Caller owns a reference to the NMDhcpClient on return */
@@ -474,7 +334,7 @@ nm_dhcp_manager_start_ip6 (NMDhcpManager *self,
hostname = get_send_hostname (self, dhcp_hostname);
return client_start (self, iface, ifindex, hwaddr, uuid, priority, TRUE,
NULL, timeout, dhcp_anycast_addr, hostname, info_only,
- privacy);
+ privacy, NULL);
}
void
@@ -497,16 +357,15 @@ nm_dhcp_manager_get_lease_ip_configs (NMDhcpManager *self,
const char *uuid,
gboolean ipv6)
{
- NMDhcpManagerPrivate *priv;
+ ClientDesc *desc;
g_return_val_if_fail (NM_IS_DHCP_MANAGER (self), NULL);
g_return_val_if_fail (iface != NULL, NULL);
g_return_val_if_fail (uuid != NULL, NULL);
- priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
-
- if (priv->get_lease_ip_configs_func)
- return priv->get_lease_ip_configs_func (iface, uuid, ipv6);
+ desc = find_client_desc (NULL, NM_DHCP_MANAGER_GET_PRIVATE (self)->client_type);
+ if (desc && desc->get_lease_configs_func)
+ return desc->get_lease_configs_func (iface, uuid, ipv6);
return NULL;
}
@@ -527,24 +386,20 @@ static void
nm_dhcp_manager_init (NMDhcpManager *self)
{
NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self);
+ NMConfig *config = nm_config_get ();
const char *client;
GError *error = NULL;
-#if !HAVE_DBUS_GLIB_100
- DBusGConnection *g_connection;
-#endif
-
- /* Maps DBusGConnection :: DBusGProxy */
- priv->proxies = g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, g_object_unref);
/* Client-specific setup */
- client = nm_config_get_dhcp_client (nm_config_get ());
- priv->client_type = get_client_type (client, &error);
+ client = nm_config_get_dhcp_client (config);
+ if (nm_config_get_configure_and_quit (config)) {
+ if (g_strcmp0 (client, "internal") != 0)
+ nm_log_warn (LOGD_DHCP, "Using internal DHCP client since configure-and-quit is set.");
+ client = "internal";
+ }
- if (priv->client_type == NM_TYPE_DHCP_DHCLIENT)
- priv->get_lease_ip_configs_func = nm_dhcp_dhclient_get_lease_ip_configs;
- else if (priv->client_type == NM_TYPE_DHCP_SYSTEMD)
- priv->get_lease_ip_configs_func = nm_dhcp_systemd_get_lease_ip_configs;
- else if (priv->client_type == G_TYPE_INVALID) {
+ priv->client_type = get_client_type (client, &error);
+ if (priv->client_type == G_TYPE_INVALID) {
nm_log_warn (LOGD_DHCP, "No usable DHCP client found (%s)! DHCP configurations will fail.",
error->message);
}
@@ -554,33 +409,6 @@ nm_dhcp_manager_init (NMDhcpManager *self)
NULL,
(GDestroyNotify) g_object_unref);
g_assert (priv->clients);
-
- priv->dbus_mgr = nm_dbus_manager_get ();
-
-#if HAVE_DBUS_GLIB_100
- /* Register the socket our DHCP clients will return lease info on */
- nm_dbus_manager_private_server_register (priv->dbus_mgr, PRIV_SOCK_PATH, PRIV_SOCK_TAG);
- priv->new_conn_id = g_signal_connect (priv->dbus_mgr,
- NM_DBUS_MANAGER_PRIVATE_CONNECTION_NEW "::" PRIV_SOCK_TAG,
- (GCallback) new_connection_cb,
- self);
- priv->dis_conn_id = g_signal_connect (priv->dbus_mgr,
- NM_DBUS_MANAGER_PRIVATE_CONNECTION_DISCONNECTED "::" PRIV_SOCK_TAG,
- (GCallback) dis_connection_cb,
- self);
-#else
- g_connection = nm_dbus_manager_get_connection (priv->dbus_mgr);
- priv->proxy = dbus_g_proxy_new_for_name (g_connection,
- "org.freedesktop.nm_dhcp_client",
- "/",
- NM_DHCP_CLIENT_DBUS_IFACE);
- g_assert (priv->proxy);
- dbus_g_proxy_add_signal (priv->proxy, "Event", DBUS_TYPE_G_MAP_OF_VARIANT, G_TYPE_INVALID);
- dbus_g_proxy_connect_signal (priv->proxy, "Event",
- G_CALLBACK (nm_dhcp_manager_handle_event),
- self,
- NULL);
-#endif
}
static void
@@ -596,23 +424,6 @@ dispose (GObject *object)
g_list_free (values);
}
- if (priv->new_conn_id) {
- g_signal_handler_disconnect (priv->dbus_mgr, priv->new_conn_id);
- priv->new_conn_id = 0;
- }
- if (priv->dis_conn_id) {
- g_signal_handler_disconnect (priv->dbus_mgr, priv->dis_conn_id);
- priv->dis_conn_id = 0;
- }
- priv->dbus_mgr = NULL;
-
- if (priv->proxies) {
- g_hash_table_destroy (priv->proxies);
- priv->proxies = NULL;
- }
- if (priv->proxy)
- g_object_unref (priv->proxy);
-
G_OBJECT_CLASS (nm_dhcp_manager_parent_class)->dispose (object);
}
diff --git a/src/dhcp-manager/nm-dhcp-manager.h b/src/dhcp-manager/nm-dhcp-manager.h
index 0c3c3f2506..cf5519fbf8 100644
--- a/src/dhcp-manager/nm-dhcp-manager.h
+++ b/src/dhcp-manager/nm-dhcp-manager.h
@@ -61,7 +61,8 @@ NMDhcpClient * nm_dhcp_manager_start_ip4 (NMDhcpManager *manager,
const char *dhcp_hostname,
const char *dhcp_client_id,
guint32 timeout,
- const char *dhcp_anycast_addr);
+ const char *dhcp_anycast_addr,
+ const char *last_ip_address);
NMDhcpClient * nm_dhcp_manager_start_ip6 (NMDhcpManager *manager,
const char *iface,
diff --git a/src/dhcp-manager/nm-dhcp-systemd.c b/src/dhcp-manager/nm-dhcp-systemd.c
index 18b7b32180..3910339e5d 100644
--- a/src/dhcp-manager/nm-dhcp-systemd.c
+++ b/src/dhcp-manager/nm-dhcp-systemd.c
@@ -374,7 +374,7 @@ get_leasefile_path (const char *iface, const char *uuid, gboolean ipv6)
iface);
}
-GSList *
+static GSList *
nm_dhcp_systemd_get_lease_ip_configs (const char *iface,
const char *uuid,
gboolean ipv6)
@@ -402,6 +402,28 @@ nm_dhcp_systemd_get_lease_ip_configs (const char *iface,
/************************************************************/
static void
+_save_client_id (NMDhcpSystemd *self,
+ uint8_t type,
+ const uint8_t *client_id,
+ size_t len)
+{
+ gs_unref_bytes GBytes *b = NULL;
+ gs_free char *buf = NULL;
+
+ g_return_if_fail (self != NULL);
+ g_return_if_fail (client_id != NULL);
+ g_return_if_fail (len > 0);
+
+ if (!nm_dhcp_client_get_client_id (NM_DHCP_CLIENT (self))) {
+ buf = g_malloc (len + 1);
+ buf[0] = type;
+ memcpy (buf + 1, client_id, len);
+ b = g_bytes_new (buf, len + 1);
+ nm_dhcp_client_set_client_id (NM_DHCP_CLIENT (self), b);
+ }
+}
+
+static void
bound4_handle (NMDhcpSystemd *self)
{
NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (self);
@@ -428,9 +450,17 @@ bound4_handle (NMDhcpSystemd *self)
TRUE,
&error);
if (ip4_config) {
+ const uint8_t *client_id = NULL;
+ size_t client_id_len = 0;
+ uint8_t type = 0;
+
add_requests_to_options (options, dhcp4_requests);
sd_dhcp_lease_save (lease, priv->lease_file);
+ client_id = sd_dhcp_client_get_client_id(priv->client4, &type, &client_id_len);
+ if (client_id)
+ _save_client_id (self, type, client_id, client_id_len);
+
nm_dhcp_client_set_state (NM_DHCP_CLIENT (self),
NM_DHCP_STATE_BOUND,
G_OBJECT (ip4_config),
@@ -485,18 +515,17 @@ get_arp_type (const GByteArray *hwaddr)
}
static gboolean
-ip4_start (NMDhcpClient *client,
- const char *dhcp_client_id,
- const char *dhcp_anycast_addr,
- const char *hostname)
+ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address)
{
NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (client);
const char *iface = nm_dhcp_client_get_iface (client);
const GByteArray *hwaddr;
sd_dhcp_lease *lease = NULL;
+ GBytes *override_client_id;
const uint8_t *client_id = NULL;
size_t client_id_len = 0;
- struct in_addr last_addr;
+ struct in_addr last_addr = { 0 };
+ const char *hostname;
int r, i;
g_assert (priv->client4 == NULL);
@@ -549,36 +578,38 @@ ip4_start (NMDhcpClient *client,
sd_dhcp_lease_load (priv->lease_file, &lease);
- if (lease) {
- r = sd_dhcp_lease_get_address (lease, &last_addr);
- if (r == 0) {
- r = sd_dhcp_client_set_request_address (priv->client4, &last_addr);
- if (r < 0) {
- nm_log_warn (LOGD_DHCP4, "(%s): failed to set last IPv4 address (%d)", iface, r);
- goto error;
- }
+ if (last_ip4_address)
+ inet_pton (AF_INET, last_ip4_address, &last_addr);
+ else if (lease)
+ sd_dhcp_lease_get_address (lease, &last_addr);
+
+ if (last_addr.s_addr) {
+ r = sd_dhcp_client_set_request_address (priv->client4, &last_addr);
+ if (r < 0) {
+ nm_log_warn (LOGD_DHCP4, "(%s): failed to set last IPv4 address (%d)", iface, r);
+ goto error;
}
}
- if (dhcp_client_id) {
- gs_unref_bytes GBytes *b = NULL;
-
- b = nm_dhcp_utils_client_id_string_to_bytes (dhcp_client_id);
- if (b) {
- client_id = (const guint8 *) g_bytes_get_data (b, &client_id_len);
- g_assert (client_id && client_id_len);
- sd_dhcp_client_set_client_id (priv->client4,
- client_id[0],
- client_id + 1,
- client_id_len - 1);
- }
- } else {
+ override_client_id = nm_dhcp_client_get_client_id (client);
+ if (override_client_id) {
+ client_id = g_bytes_get_data (override_client_id, &client_id_len);
+ g_assert (client_id && client_id_len);
+ sd_dhcp_client_set_client_id (priv->client4,
+ client_id[0],
+ client_id + 1,
+ client_id_len - 1);
+ } else if (lease) {
r = sd_dhcp_lease_get_client_id (lease, &client_id, &client_id_len);
if (r == 0 && client_id_len) {
sd_dhcp_client_set_client_id (priv->client4,
client_id[0],
client_id + 1,
client_id_len - 1);
+ _save_client_id (NM_DHCP_SYSTEMD (client),
+ client_id[0],
+ client_id + 1,
+ client_id_len - 1);
}
}
@@ -591,6 +622,7 @@ ip4_start (NMDhcpClient *client,
sd_dhcp_client_set_request_option (priv->client4, dhcp4_requests[i].num);
}
+ hostname = nm_dhcp_client_get_hostname (client);
if (hostname) {
r = sd_dhcp_client_set_hostname (priv->client4, hostname);
if (r < 0) {
@@ -653,7 +685,6 @@ dhcp6_event_cb (sd_dhcp6_client *client, int event, gpointer user_data)
static gboolean
ip6_start (NMDhcpClient *client,
const char *dhcp_anycast_addr,
- const char *hostname,
gboolean info_only,
NMSettingIP6ConfigPrivacy privacy,
const GByteArray *duid)
@@ -808,3 +839,13 @@ nm_dhcp_systemd_class_init (NMDhcpSystemdClass *sdhcp_class)
client_class->stop = stop;
}
+static void __attribute__((constructor))
+register_dhcp_dhclient (void)
+{
+ g_type_init ();
+ _nm_dhcp_client_register (NM_TYPE_DHCP_SYSTEMD,
+ "internal",
+ NULL,
+ nm_dhcp_systemd_get_lease_ip_configs);
+}
+
diff --git a/src/dhcp-manager/nm-dhcp-systemd.h b/src/dhcp-manager/nm-dhcp-systemd.h
index 05e8e8da33..2a7a463eb5 100644
--- a/src/dhcp-manager/nm-dhcp-systemd.h
+++ b/src/dhcp-manager/nm-dhcp-systemd.h
@@ -41,9 +41,5 @@ typedef struct {
GType nm_dhcp_systemd_get_type (void);
-GSList *nm_dhcp_systemd_get_lease_ip_configs (const char *iface,
- const char *uuid,
- gboolean ipv6);
-
#endif /* NM_DHCP_SYSTEMD_H */
diff --git a/src/dhcp-manager/nm-dhcp-utils.c b/src/dhcp-manager/nm-dhcp-utils.c
index d9eb85df86..4b8c44a452 100644
--- a/src/dhcp-manager/nm-dhcp-utils.c
+++ b/src/dhcp-manager/nm-dhcp-utils.c
@@ -709,46 +709,17 @@ GBytes *
nm_dhcp_utils_client_id_string_to_bytes (const char *client_id)
{
GBytes *bytes = NULL;
- guint i = 0, x = 0;
guint len;
char *c;
- int a;
g_return_val_if_fail (client_id && client_id[0], NULL);
- /* Accept a binary client ID in hex digits with the ':' delimiter,
- * otherwise treat it as a string.
- */
- len = strlen (client_id);
- c = g_malloc0 (len / 2 + 1);
- while (client_id[i]) {
- a = g_ascii_xdigit_value (client_id[i++]);
- if (a >= 0) {
- if (client_id[i] != ':') {
- c[x] = ((guint8) a << 4);
- a = g_ascii_xdigit_value (client_id[i++]);
- }
- if (a >= 0)
- c[x++] |= (guint8) a;
- }
- if (client_id[i]) {
- if (client_id[i] != ':' || !client_id[i + 1]) {
- /* missing or trailing ':' is invalid for hex-format */
- a = -1;
- }
- i++;
- }
-
- if (a < 0) {
- g_clear_pointer (&c, g_free);
- break;
- }
- }
-
- if (c) {
- g_assert (x > 0);
- bytes = g_bytes_new_take (c, x);
- } else {
+ /* Try as hex encoded */
+ if (strchr (client_id, ':'))
+ bytes = nm_utils_hexstr2bin (client_id);
+ if (!bytes) {
+ /* Fall back to string */
+ len = strlen (client_id);
c = g_malloc (len + 1);
c[0] = 0; /* type: non-hardware address per RFC 2132 section 9.14 */
memcpy (c + 1, client_id, len);
diff --git a/src/dhcp-manager/tests/Makefile.am b/src/dhcp-manager/tests/Makefile.am
index 0292db2b7a..9dc5dcb13d 100644
--- a/src/dhcp-manager/tests/Makefile.am
+++ b/src/dhcp-manager/tests/Makefile.am
@@ -18,6 +18,10 @@ noinst_PROGRAMS = \
####### dhclient leases test #######
test_dhcp_dhclient_SOURCES = \
+ $(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.h \
+ $(top_srcdir)/src/dhcp-manager/nm-dhcp-dhclient-utils.c \
+ $(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.h \
+ $(top_srcdir)/src/dhcp-manager/nm-dhcp-utils.c \
test-dhcp-dhclient.c
test_dhcp_dhclient_LDADD = \
diff --git a/src/dhcp-manager/tests/test-dhcp-dhclient.c b/src/dhcp-manager/tests/test-dhcp-dhclient.c
index 2ddf6b5078..544bd89efe 100644
--- a/src/dhcp-manager/tests/test-dhcp-dhclient.c
+++ b/src/dhcp-manager/tests/test-dhcp-dhclient.c
@@ -23,30 +23,42 @@
#include <unistd.h>
#include <arpa/inet.h>
+#include "gsystem-local-alloc.h"
+#include "NetworkManagerUtils.h"
#include "nm-dhcp-dhclient-utils.h"
+#include "nm-dhcp-utils.h"
#include "nm-utils.h"
#include "nm-ip4-config.h"
#include "nm-platform.h"
-#define DEBUG 0
+#define DEBUG 1
static void
test_config (const char *orig,
const char *expected,
const char *hostname,
const char *dhcp_client_id,
+ GBytes *expected_new_client_id,
const char *iface,
const char *anycast_addr)
{
- char *new;
+ gs_free char *new = NULL;
+ gs_unref_bytes GBytes *client_id = NULL;
+ gs_unref_bytes GBytes *new_client_id = NULL;
+
+ if (dhcp_client_id) {
+ client_id = nm_dhcp_utils_client_id_string_to_bytes (dhcp_client_id);
+ g_assert (client_id);
+ }
new = nm_dhcp_dhclient_create_config (iface,
FALSE,
- dhcp_client_id,
+ client_id,
anycast_addr,
hostname,
"/path/to/dhclient.conf",
- orig);
+ orig,
+ &new_client_id);
g_assert (new != NULL);
#if DEBUG
@@ -60,9 +72,13 @@ test_config (const char *orig,
new, expected);
}
#endif
- g_assert (strlen (new) == strlen (expected));
- g_assert (strcmp (new, expected) == 0);
- g_free (new);
+ g_assert_cmpstr (new, ==, expected);
+
+ if (expected_new_client_id) {
+ g_assert (new_client_id);
+ g_assert (g_bytes_equal (new_client_id, expected_new_client_id));
+ } else
+ g_assert (new_client_id == NULL);
}
/*******************************************/
@@ -84,11 +100,7 @@ static const char *orig_missing_expected = \
static void
test_orig_missing (void)
{
- test_config (NULL, orig_missing_expected,
- NULL,
- NULL,
- "eth0",
- NULL);
+ test_config (NULL, orig_missing_expected, NULL, NULL, NULL, "eth0", NULL);
}
/*******************************************/
@@ -119,6 +131,7 @@ test_override_client_id (void)
test_config (override_client_id_orig, override_client_id_expected,
NULL,
"11:22:33:44:55:66",
+ NULL,
"eth0",
NULL);
}
@@ -147,6 +160,7 @@ test_quote_client_id (void)
test_config (NULL, quote_client_id_expected,
NULL,
"1234",
+ NULL,
"eth0",
NULL);
}
@@ -175,6 +189,7 @@ test_ascii_client_id (void)
test_config (NULL, ascii_client_id_expected,
NULL,
"qb:cd:ef:12:34:56",
+ NULL,
"eth0",
NULL);
}
@@ -184,7 +199,7 @@ test_ascii_client_id (void)
static const char *hex_single_client_id_expected = \
"# Created by NetworkManager\n"
"\n"
- "send dhcp-client-identifier ab:cd:e:12:34:56; # added by NetworkManager\n"
+ "send dhcp-client-identifier ab:cd:0e:12:34:56; # 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"
@@ -203,6 +218,84 @@ test_hex_single_client_id (void)
test_config (NULL, hex_single_client_id_expected,
NULL,
"ab:cd:e:12:34:56",
+ NULL,
+ "eth0",
+ NULL);
+}
+
+/*******************************************/
+
+static const char *existing_hex_client_id_orig = \
+ "send dhcp-client-identifier 00:30:04:20:7A:08;\n";
+
+static const char *existing_hex_client_id_expected = \
+ "# Created by NetworkManager\n"
+ "# Merged from /path/to/dhclient.conf\n"
+ "\n"
+ "send dhcp-client-identifier 00:30:04:20:7A:08;\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 static-routes;\n"
+ "also request wpad;\n"
+ "also request ntp-servers;\n"
+ "\n";
+
+static void
+test_existing_hex_client_id (void)
+{
+ gs_unref_bytes GBytes *new_client_id = NULL;
+ const guint8 bytes[] = { 0x00, 0x30, 0x04,0x20, 0x7A, 0x08 };
+
+ new_client_id = g_bytes_new (bytes, sizeof (bytes));
+ test_config (existing_hex_client_id_orig, existing_hex_client_id_expected,
+ NULL,
+ NULL,
+ new_client_id,
+ "eth0",
+ NULL);
+}
+
+/*******************************************/
+
+#define EACID "qb:cd:ef:12:34:56"
+
+static const char *existing_ascii_client_id_orig = \
+ "send dhcp-client-identifier \"" EACID "\";\n";
+
+static const char *existing_ascii_client_id_expected = \
+ "# Created by NetworkManager\n"
+ "# Merged from /path/to/dhclient.conf\n"
+ "\n"
+ "send dhcp-client-identifier \"" EACID "\";\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 static-routes;\n"
+ "also request wpad;\n"
+ "also request ntp-servers;\n"
+ "\n";
+
+static void
+test_existing_ascii_client_id (void)
+{
+ gs_unref_bytes GBytes *new_client_id = NULL;
+ char buf[STRLEN (EACID) + 1] = { 0 };
+
+ memcpy (buf + 1, EACID, STRLEN (EACID));
+ new_client_id = g_bytes_new (buf, sizeof (buf));
+ test_config (existing_ascii_client_id_orig, existing_ascii_client_id_expected,
+ NULL,
+ NULL,
+ new_client_id,
"eth0",
NULL);
}
@@ -235,6 +328,7 @@ test_override_hostname (void)
test_config (override_hostname_orig, override_hostname_expected,
"blahblah",
NULL,
+ NULL,
"eth0",
NULL);
}
@@ -269,6 +363,7 @@ test_existing_alsoreq (void)
test_config (existing_alsoreq_orig, existing_alsoreq_expected,
NULL,
NULL,
+ NULL,
"eth0",
NULL);
}
@@ -307,6 +402,7 @@ test_existing_multiline_alsoreq (void)
test_config (existing_multiline_alsoreq_orig, existing_multiline_alsoreq_expected,
NULL,
NULL,
+ NULL,
"eth0",
NULL);
}
@@ -616,6 +712,8 @@ main (int argc, char **argv)
g_test_add_func ("/dhcp/dhclient/quote_client_id", test_quote_client_id);
g_test_add_func ("/dhcp/dhclient/ascii_client_id", test_ascii_client_id);
g_test_add_func ("/dhcp/dhclient/hex_single_client_id", test_hex_single_client_id);
+ g_test_add_func ("/dhcp/dhclient/existing-hex-client-id", test_existing_hex_client_id);
+ g_test_add_func ("/dhcp/dhclient/existing-ascii-client-id", test_existing_ascii_client_id);
g_test_add_func ("/dhcp/dhclient/override_hostname", test_override_hostname);
g_test_add_func ("/dhcp/dhclient/existing_alsoreq", test_existing_alsoreq);
g_test_add_func ("/dhcp/dhclient/existing_multiline_alsoreq", test_existing_multiline_alsoreq);
diff --git a/src/main-utils.c b/src/main-utils.c
new file mode 100644
index 0000000000..55da4dfe41
--- /dev/null
+++ b/src/main-utils.c
@@ -0,0 +1,281 @@
+/* -*- 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 - 2012 Red Hat, Inc.
+ * Copyright (C) 2005 - 2008 Novell, Inc.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <locale.h>
+
+#include <glib.h>
+#include <glib/gi18n.h>
+#include <gmodule.h>
+
+#include "main-utils.h"
+#include "nm-posix-signals.h"
+#include "nm-logging.h"
+
+static sigset_t signal_set;
+static gboolean *quit_early = NULL;
+
+/*
+ * Thread function waiting for signals and processing them.
+ * Wait for signals in signal set. The semantics of sigwait() require that all
+ * threads (including the thread calling sigwait()) have the signal masked, for
+ * reliable operation. Otherwise, a signal that arrives while this thread is
+ * not blocked in sigwait() might be delivered to another thread.
+ */
+static void *
+signal_handling_thread (void *arg)
+{
+ GMainLoop *main_loop = arg;
+ int signo;
+
+ while (1) {
+ sigwait (&signal_set, &signo);
+
+ switch (signo) {
+ case SIGINT:
+ case SIGTERM:
+ nm_log_info (LOGD_CORE, "caught signal %d, shutting down normally.", signo);
+ *quit_early = TRUE; /* for quitting before entering the main loop */
+ g_main_loop_quit (main_loop);
+ break;
+ case SIGHUP:
+ /* Reread config stuff like system config files, VPN service files, etc */
+ nm_log_info (LOGD_CORE, "caught signal %d, not supported yet.", signo);
+ break;
+ case SIGPIPE:
+ /* silently ignore signal */
+ break;
+ default:
+ nm_log_err (LOGD_CORE, "caught unexpected signal %d", signo);
+ break;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * nm_main_utils_setup_signals:
+ * @main_loop: the #GMainLoop to quit when SIGINT or SIGTERM is received
+ * @quit_early: location of a variable that will be set to TRUE when
+ * SIGINT or SIGTERM is received
+ *
+ * Mask the signals we are interested in and create a signal handling thread.
+ * Because all threads inherit the signal mask from their creator, all threads
+ * in the process will have the signals masked. That's why setup_signals() has
+ * to be called before creating other threads.
+ *
+ * Returns: %TRUE on success
+ */
+gboolean
+nm_main_utils_setup_signals (GMainLoop *main_loop, gboolean *quit_early_ptr)
+{
+ pthread_t signal_thread_id;
+ sigset_t old_sig_mask;
+ int status;
+
+ g_return_val_if_fail (main_loop != NULL, FALSE);
+ g_return_val_if_fail (quit_early_ptr != NULL, FALSE);
+
+ quit_early = quit_early_ptr;
+
+ sigemptyset (&signal_set);
+ sigaddset (&signal_set, SIGHUP);
+ sigaddset (&signal_set, SIGINT);
+ sigaddset (&signal_set, SIGTERM);
+ sigaddset (&signal_set, SIGPIPE);
+
+ /* Block all signals of interest. */
+ status = pthread_sigmask (SIG_BLOCK, &signal_set, &old_sig_mask);
+ if (status != 0) {
+ fprintf (stderr, _("Failed to set signal mask: %d"), status);
+ return FALSE;
+ }
+ /* Save original mask so that we could use it for child processes. */
+ nm_save_original_signal_mask (old_sig_mask);
+
+ /* Create the signal handling thread. */
+ status = pthread_create (&signal_thread_id, NULL, signal_handling_thread, main_loop);
+ if (status != 0) {
+ fprintf (stderr, _("Failed to create signal handling thread: %d"), status);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+gboolean
+nm_main_utils_write_pidfile (const char *pidfile)
+{
+ char pid[16];
+ int fd;
+ gboolean success = FALSE;
+
+ if ((fd = open (pidfile, O_CREAT|O_WRONLY|O_TRUNC, 00644)) < 0) {
+ fprintf (stderr, _("Opening %s failed: %s\n"), pidfile, strerror (errno));
+ return FALSE;
+ }
+
+ g_snprintf (pid, sizeof (pid), "%d", getpid ());
+ if (write (fd, pid, strlen (pid)) < 0)
+ fprintf (stderr, _("Writing to %s failed: %s\n"), pidfile, strerror (errno));
+ else
+ success = TRUE;
+
+ if (close (fd))
+ fprintf (stderr, _("Closing %s failed: %s\n"), pidfile, strerror (errno));
+
+ return success;
+}
+
+/**
+ * nm_main_utils_check_pidfile:
+ * @pidfile: the pid file
+ * @name: the process name
+ *
+ * Checks whether the pidfile already exists and contains PID of a running
+ * process.
+ *
+ * Returns: %TRUE if the specified pidfile already exists and contains the PID
+ * of a running process named @name, or %FALSE if not
+ */
+gboolean
+nm_main_utils_check_pidfile (const char *pidfile, const char *name)
+{
+ char *contents = NULL;
+ gsize len = 0;
+ glong pid;
+ char *proc_cmdline = NULL;
+ gboolean nm_running = FALSE;
+ const char *process_name;
+
+ /* Setup runtime directory */
+ if (g_mkdir_with_parents (NMRUNDIR, 0755) != 0) {
+ nm_log_err (LOGD_CORE, "Cannot create '%s': %s", NMRUNDIR, strerror (errno));
+ exit (1);
+ }
+
+ if (!g_file_get_contents (pidfile, &contents, &len, NULL))
+ return FALSE;
+
+ if (len <= 0)
+ goto done;
+
+ errno = 0;
+ pid = strtol (contents, NULL, 10);
+ if (pid <= 0 || pid > 65536 || errno)
+ goto done;
+
+ g_free (contents);
+ proc_cmdline = g_strdup_printf ("/proc/%ld/cmdline", pid);
+ if (!g_file_get_contents (proc_cmdline, &contents, &len, NULL))
+ goto done;
+
+ process_name = strrchr (contents, '/');
+ if (process_name)
+ process_name++;
+ else
+ process_name = contents;
+ if (strcmp (process_name, name) == 0) {
+ /* Check that the process exists */
+ if (kill (pid, 0) == 0) {
+ fprintf (stderr, _("%s is already running (pid %ld)\n"), name, pid);
+ nm_running = TRUE;
+ }
+ }
+
+done:
+ g_free (proc_cmdline);
+ g_free (contents);
+ return nm_running;
+}
+
+gboolean
+nm_main_utils_early_setup (const char *progname,
+ char **argv[],
+ int *argc,
+ GOptionEntry *options,
+ GOptionEntry *more_options,
+ const char *summary)
+{
+ GOptionContext *opt_ctx = NULL;
+ GError *error = NULL;
+ gboolean success = FALSE;
+ int i;
+
+ /* Make GIO ignore the remote VFS service; otherwise it tries to use the
+ * session bus to contact the remote service, and NM shouldn't ever be
+ * talking on the session bus. See rh #588745
+ */
+ setenv ("GIO_USE_VFS", "local", 1);
+
+ /*
+ * Set the umask to 0022, which results in 0666 & ~0022 = 0644.
+ * Otherwise, if root (or an su'ing user) has a wacky umask, we could
+ * write out an unreadable resolv.conf.
+ */
+ umask (022);
+
+ /* Ensure gettext() gets the right environment (bgo #666516) */
+ setlocale (LC_ALL, "");
+
+ bindtextdomain (GETTEXT_PACKAGE, NMLOCALEDIR);
+ bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
+ textdomain (GETTEXT_PACKAGE);
+
+ if (getuid () != 0) {
+ fprintf (stderr, _("You must be root to run %s!\n"), progname);
+ exit (1);
+ }
+
+ for (i = 0; options[i].long_name; i++) {
+ if (!strcmp (options[i].long_name, "log-level"))
+ options[i].description = g_strdup_printf (options[i].description, nm_logging_all_levels_to_string ());
+ else if (!strcmp (options[i].long_name, "log-domains"))
+ options[i].description = g_strdup_printf (options[i].description, nm_logging_all_domains_to_string ());
+ }
+
+ /* Parse options */
+ opt_ctx = g_option_context_new (NULL);
+ g_option_context_set_translation_domain (opt_ctx, GETTEXT_PACKAGE);
+ g_option_context_set_ignore_unknown_options (opt_ctx, FALSE);
+ g_option_context_set_help_enabled (opt_ctx, TRUE);
+ g_option_context_add_main_entries (opt_ctx, options, NULL);
+ if (more_options)
+ g_option_context_add_main_entries (opt_ctx, more_options, NULL);
+ g_option_context_set_summary (opt_ctx, summary);
+
+ success = g_option_context_parse (opt_ctx, argc, argv, &error);
+ if (!success) {
+ fprintf (stderr, _("%s. Please use --help to see a list of valid options.\n"),
+ error->message);
+ g_clear_error (&error);
+ }
+ g_option_context_free (opt_ctx);
+
+ return success;
+}
+
diff --git a/src/main-utils.h b/src/main-utils.h
new file mode 100644
index 0000000000..472fa5e70e
--- /dev/null
+++ b/src/main-utils.h
@@ -0,0 +1,39 @@
+/* -*- 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) 2014 Red Hat, Inc.
+ */
+
+#ifndef __MAIN_UTILS_H__
+#define __MAIN_UTILS_H__
+
+#include <glib.h>
+
+gboolean nm_main_utils_setup_signals (GMainLoop *main_loop, gboolean *quit_early_ptr);
+
+gboolean nm_main_utils_write_pidfile (const char *pidfile);
+
+gboolean nm_main_utils_check_pidfile (const char *pidfile, const char *name);
+
+gboolean nm_main_utils_early_setup (const char *progname,
+ char **argv[],
+ int *argc,
+ GOptionEntry *options,
+ GOptionEntry *more_options,
+ const char *summary);
+
+#endif /* __MAIN_UTILS_H__ */
diff --git a/src/main.c b/src/main.c
index f4f693d1e4..476d2a7711 100644
--- a/src/main.c
+++ b/src/main.c
@@ -42,6 +42,7 @@
#include "gsystem-local-alloc.h"
#include "nm-dbus-interface.h"
#include "NetworkManagerUtils.h"
+#include "main-utils.h"
#include "nm-manager.h"
#include "nm-linux-platform.h"
#include "nm-dns-manager.h"
@@ -65,161 +66,7 @@
#define NM_DEFAULT_PID_FILE NMRUNDIR "/NetworkManager.pid"
#define NM_DEFAULT_SYSTEM_STATE_FILE NMSTATEDIR "/NetworkManager.state"
-/*
- * Globals
- */
static GMainLoop *main_loop = NULL;
-static gboolean quit_early = FALSE;
-static sigset_t signal_set;
-
-void *signal_handling_thread (void *arg);
-/*
- * Thread function waiting for signals and processing them.
- * Wait for signals in signal set. The semantics of sigwait() require that all
- * threads (including the thread calling sigwait()) have the signal masked, for
- * reliable operation. Otherwise, a signal that arrives while this thread is
- * not blocked in sigwait() might be delivered to another thread.
- */
-void *
-signal_handling_thread (void *arg)
-{
- int signo;
-
- while (1) {
- sigwait (&signal_set, &signo);
-
- switch (signo) {
- case SIGINT:
- case SIGTERM:
- nm_log_info (LOGD_CORE, "caught signal %d, shutting down normally.", signo);
- quit_early = TRUE; /* for quitting before entering the main loop */
- g_main_loop_quit (main_loop);
- break;
- case SIGHUP:
- /* Reread config stuff like system config files, VPN service files, etc */
- nm_log_info (LOGD_CORE, "caught signal %d, not supported yet.", signo);
- break;
- case SIGPIPE:
- /* silently ignore signal */
- break;
- default:
- nm_log_err (LOGD_CORE, "caught unexpected signal %d", signo);
- break;
- }
- }
- return NULL;
-}
-
-/*
- * Mask the signals we are interested in and create a signal handling thread.
- * Because all threads inherit the signal mask from their creator, all threads
- * in the process will have the signals masked. That's why setup_signals() has
- * to be called before creating other threads.
- */
-static gboolean
-setup_signals (void)
-{
- pthread_t signal_thread_id;
- sigset_t old_sig_mask;
- int status;
-
- sigemptyset (&signal_set);
- sigaddset (&signal_set, SIGHUP);
- sigaddset (&signal_set, SIGINT);
- sigaddset (&signal_set, SIGTERM);
- sigaddset (&signal_set, SIGPIPE);
-
- /* Block all signals of interest. */
- status = pthread_sigmask (SIG_BLOCK, &signal_set, &old_sig_mask);
- if (status != 0) {
- fprintf (stderr, _("Failed to set signal mask: %d"), status);
- return FALSE;
- }
- /* Save original mask so that we could use it for child processes. */
- nm_save_original_signal_mask (old_sig_mask);
-
- /* Create the signal handling thread. */
- status = pthread_create (&signal_thread_id, NULL, signal_handling_thread, NULL);
- if (status != 0) {
- fprintf (stderr, _("Failed to create signal handling thread: %d"), status);
- return FALSE;
- }
-
- return TRUE;
-}
-
-static gboolean
-write_pidfile (const char *pidfile)
-{
- char pid[16];
- int fd;
- gboolean success = FALSE;
-
- if ((fd = open (pidfile, O_CREAT|O_WRONLY|O_TRUNC, 00644)) < 0) {
- fprintf (stderr, _("Opening %s failed: %s\n"), pidfile, strerror (errno));
- return FALSE;
- }
-
- g_snprintf (pid, sizeof (pid), "%d", getpid ());
- if (write (fd, pid, strlen (pid)) < 0)
- fprintf (stderr, _("Writing to %s failed: %s\n"), pidfile, strerror (errno));
- else
- success = TRUE;
-
- if (close (fd))
- fprintf (stderr, _("Closing %s failed: %s\n"), pidfile, strerror (errno));
-
- return success;
-}
-
-/* Check whether the pidfile already exists and contains PID of a running NetworkManager
- * Returns: FALSE - specified pidfile doesn't exist or doesn't contain PID of a running NM process
- * TRUE - specified pidfile already exists and contains PID of a running NM process
- */
-static gboolean
-check_pidfile (const char *pidfile)
-{
- char *contents = NULL;
- gsize len = 0;
- glong pid;
- char *proc_cmdline = NULL;
- gboolean nm_running = FALSE;
- const char *process_name;
-
- if (!g_file_get_contents (pidfile, &contents, &len, NULL))
- return FALSE;
-
- if (len <= 0)
- goto done;
-
- errno = 0;
- pid = strtol (contents, NULL, 10);
- if (pid <= 0 || pid > 65536 || errno)
- goto done;
-
- g_free (contents);
- proc_cmdline = g_strdup_printf ("/proc/%ld/cmdline", pid);
- if (!g_file_get_contents (proc_cmdline, &contents, &len, NULL))
- goto done;
-
- process_name = strrchr (contents, '/');
- if (process_name)
- process_name++;
- else
- process_name = contents;
- if (strcmp (process_name, "NetworkManager") == 0) {
- /* Check that the process exists */
- if (kill (pid, 0) == 0) {
- fprintf (stderr, _("NetworkManager is already running (pid %ld)\n"), pid);
- nm_running = TRUE;
- }
- }
-
-done:
- g_free (proc_cmdline);
- g_free (contents);
- return nm_running;
-}
static gboolean
parse_state_file (const char *filename,
@@ -330,6 +177,13 @@ _init_nm_debug (const char *debug)
}
}
+static void
+manager_configure_quit (NMManager *manager, gpointer user_data)
+{
+ nm_log_info (LOGD_CORE, "quitting now that startup is complete");
+ g_main_loop_quit (main_loop);
+}
+
/*
* main
*
@@ -337,7 +191,6 @@ _init_nm_debug (const char *debug)
int
main (int argc, char *argv[])
{
- GOptionContext *opt_ctx = NULL;
char *opt_log_level = NULL;
char *opt_log_domains = NULL;
gboolean become_daemon = TRUE, run_from_build_dir = FALSE;
@@ -347,7 +200,6 @@ main (int argc, char *argv[])
gs_free char *state_file = NULL;
gboolean wifi_enabled = TRUE, net_enabled = TRUE, wwan_enabled = TRUE, wimax_enabled = TRUE;
gboolean success, show_version = FALSE;
- int i;
NMManager *manager = NULL;
gs_unref_object NMVpnManager *vpn_manager = NULL;
gs_unref_object NMDnsManager *dns_mgr = NULL;
@@ -361,6 +213,7 @@ main (int argc, char *argv[])
GError *error = NULL;
gboolean wrote_pidfile = FALSE;
char *bad_domains = NULL;
+ gboolean quit_early = FALSE;
GOptionEntry options[] = {
{ "version", 'V', 0, G_OPTION_ARG_NONE, &show_version, N_("Print NetworkManager version and exit"), NULL },
@@ -377,66 +230,15 @@ main (int argc, char *argv[])
{NULL}
};
- /* Make GIO ignore the remote VFS service; otherwise it tries to use the
- * session bus to contact the remote service, and NM shouldn't ever be
- * talking on the session bus. See rh #588745
- */
- setenv ("GIO_USE_VFS", "local", 1);
-
- /*
- * Set the umask to 0022, which results in 0666 & ~0022 = 0644.
- * Otherwise, if root (or an su'ing user) has a wacky umask, we could
- * write out an unreadable resolv.conf.
- */
- umask (022);
-
- /* Set locale to be able to use environment variables */
- setlocale (LC_ALL, "");
-
- bindtextdomain (GETTEXT_PACKAGE, NMLOCALEDIR);
- bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
- textdomain (GETTEXT_PACKAGE);
-
- if (!g_module_supported ()) {
- fprintf (stderr, _("GModules are not supported on your platform!\n"));
- exit (1);
- }
-
- if (getuid () != 0) {
- fprintf (stderr, _("You must be root to run NetworkManager!\n"));
- exit (1);
- }
-
- for (i = 0; options[i].long_name; i++) {
- if (!strcmp (options[i].long_name, "log-level")) {
- options[i].description = g_strdup_printf (options[i].description,
- nm_logging_all_levels_to_string ());
- } else if (!strcmp (options[i].long_name, "log-domains")) {
- options[i].description = g_strdup_printf (options[i].description,
- nm_logging_all_domains_to_string ());
- }
- }
-
- /* Parse options */
- opt_ctx = g_option_context_new (NULL);
- g_option_context_set_translation_domain (opt_ctx, GETTEXT_PACKAGE);
- g_option_context_set_ignore_unknown_options (opt_ctx, FALSE);
- g_option_context_set_help_enabled (opt_ctx, TRUE);
- g_option_context_add_main_entries (opt_ctx, options, NULL);
- g_option_context_add_main_entries (opt_ctx, nm_config_get_options (), NULL);
-
- g_option_context_set_summary (opt_ctx,
- _("NetworkManager monitors all network connections and automatically\nchooses the best connection to use. It also allows the user to\nspecify wireless access points which wireless cards in the computer\nshould associate with."));
-
- success = g_option_context_parse (opt_ctx, &argc, &argv, &error);
- g_option_context_free (opt_ctx);
+ main_loop = g_main_loop_new (NULL, FALSE);
- if (!success) {
- fprintf (stderr, _("%s. Please use --help to see a list of valid options.\n"),
- error->message);
- g_clear_error (&error);
+ if (!nm_main_utils_early_setup ("NetworkManager",
+ &argv,
+ &argc,
+ options,
+ nm_config_get_options (),
+ _("NetworkManager monitors all network connections and automatically\nchooses the best connection to use. It also allows the user to\nspecify wireless access points which wireless cards in the computer\nshould associate with.")))
exit (1);
- }
if (show_version) {
fprintf (stdout, NM_DIST_VERSION "\n");
@@ -482,12 +284,6 @@ main (int argc, char *argv[])
g_free (path);
}
- /* Setup runtime directory */
- if (g_mkdir_with_parents (NMRUNDIR, 0755) != 0) {
- nm_log_err (LOGD_CORE, "Cannot create '%s': %s", NMRUNDIR, strerror (errno));
- exit (1);
- }
-
/* Ensure state directory exists */
if (g_mkdir_with_parents (NMSTATEDIR, 0755) != 0) {
nm_log_err (LOGD_CORE, "Cannot create '%s': %s", NMSTATEDIR, strerror (errno));
@@ -498,7 +294,7 @@ main (int argc, char *argv[])
state_file = state_file ? state_file : g_strdup (NM_DEFAULT_SYSTEM_STATE_FILE);
/* check pid file */
- if (check_pidfile (pidfile))
+ if (nm_main_utils_check_pidfile (pidfile, "NetworkManager"))
exit (1);
/* Read the config file and CLI overrides */
@@ -549,14 +345,13 @@ main (int argc, char *argv[])
saved_errno);
exit (1);
}
- if (write_pidfile (pidfile))
- wrote_pidfile = TRUE;
+ wrote_pidfile = nm_main_utils_write_pidfile (pidfile);
}
_init_nm_debug (nm_config_get_debug (config));
/* Set up unix signal handling - before creating threads, but after daemonizing! */
- if (!setup_signals ())
+ if (!nm_main_utils_setup_signals (main_loop, &quit_early))
exit (1);
if (g_fatal_warnings) {
@@ -592,8 +387,6 @@ main (int argc, char *argv[])
#endif
);
- main_loop = g_main_loop_new (NULL, FALSE);
-
/* Set up platform interaction layer */
nm_linux_platform_setup ();
@@ -662,6 +455,8 @@ main (int argc, char *argv[])
}
}
+ g_signal_connect (manager, NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config);
+
nm_manager_start (manager);
/* Make sure the loopback interface is up. If interface is down, we bring
@@ -680,10 +475,10 @@ main (int argc, char *argv[])
success = TRUE;
/* Told to quit before getting to the mainloop by the signal handler */
- if (quit_early == TRUE)
- goto done;
+ if (!quit_early)
+ g_main_loop_run (main_loop);
- g_main_loop_run (main_loop);
+ nm_manager_stop (manager);
done:
g_clear_object (&manager);
diff --git a/src/nm-config.c b/src/nm-config.c
index 1f41553355..aba4fa1a1b 100644
--- a/src/nm-config.c
+++ b/src/nm-config.c
@@ -61,6 +61,8 @@ typedef struct {
char **no_auto_default;
char **ignore_carrier;
+
+ gboolean configure_and_quit;
} NMConfigPrivate;
static NMConfig *singleton = NULL;
@@ -72,41 +74,36 @@ G_DEFINE_TYPE (NMConfig, nm_config, G_TYPE_OBJECT)
/************************************************************************/
static gboolean
-_parse_bool_str (const char *str, gboolean *out_value)
+_get_bool_value (GKeyFile *keyfile,
+ const char *section,
+ const char *key,
+ gboolean default_value)
{
- gboolean value;
- gsize len;
- char *s = NULL;
-
- g_return_val_if_fail (str, FALSE);
-
- while (g_ascii_isspace (*str))
- str++;
-
- if (!*str)
- return FALSE;
-
- len = strlen (str);
-
- if (g_ascii_isspace (str[len-1])) {
- str = s = g_strdup (str);
- g_strchomp (s);
- }
-
- if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1"))
- value = TRUE;
- else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0"))
- value = FALSE;
- else {
- g_free (s);
- return FALSE;
+ gboolean value = default_value;
+ char *str;
+
+ g_return_val_if_fail (keyfile != NULL, default_value);
+ g_return_val_if_fail (section != NULL, default_value);
+ g_return_val_if_fail (key != NULL, default_value);
+
+ str = g_key_file_get_value (keyfile, section, key, NULL);
+ if (!str)
+ return default_value;
+
+ g_strstrip (str);
+ if (str[0]) {
+ if (!g_ascii_strcasecmp (str, "true") || !g_ascii_strcasecmp (str, "yes") || !g_ascii_strcasecmp (str, "on") || !g_ascii_strcasecmp (str, "1"))
+ value = TRUE;
+ else if (!g_ascii_strcasecmp (str, "false") || !g_ascii_strcasecmp (str, "no") || !g_ascii_strcasecmp (str, "off") || !g_ascii_strcasecmp (str, "0"))
+ value = FALSE;
+ else {
+ nm_log_warn (LOGD_CORE, "Unrecognized value for %s.%s: '%s'. Assuming '%s'",
+ section, key, str, default_value ? "true" : "false");
+ }
}
- if (out_value)
- *out_value = value;
-
- g_free (s);
- return TRUE;
+ g_free (str);
+ return value;
}
/************************************************************************/
@@ -218,6 +215,12 @@ nm_config_get_connectivity_response (NMConfig *config)
return NM_CONFIG_GET_PRIVATE (config)->connectivity_response;
}
+gboolean
+nm_config_get_configure_and_quit (NMConfig *config)
+{
+ return NM_CONFIG_GET_PRIVATE (config)->configure_and_quit;
+}
+
char *
nm_config_get_value (NMConfig *config, const char *group, const char *key, GError **error)
{
@@ -513,7 +516,6 @@ nm_config_new (GError **error)
GFileInfo *info;
GPtrArray *confs;
const char *name;
- char *value;
int i;
GString *config_description;
@@ -583,23 +585,9 @@ nm_config_new (GError **error)
if (!priv->plugins && STRLEN (CONFIG_PLUGINS_DEFAULT) > 0)
priv->plugins = g_strsplit (CONFIG_PLUGINS_DEFAULT, ",", -1);
- value = g_key_file_get_value (priv->keyfile, "main", "monitor-connection-files", NULL);
- priv->monitor_connection_files = FALSE;
- if (value) {
- if (!_parse_bool_str (value, &priv->monitor_connection_files))
- nm_log_warn (LOGD_CORE, "Unrecognized value for main.monitor-connection-files: %s. Assuming 'false'", value);
- g_free (value);
- }
+ priv->monitor_connection_files = _get_bool_value (priv->keyfile, "main", "monitor-connection-files", FALSE);
- value = g_key_file_get_value (priv->keyfile, "main", "auth-polkit", NULL);
- priv->auth_polkit = NM_CONFIG_DEFAULT_AUTH_POLKIT;
- if (value) {
- if (!_parse_bool_str (value, &priv->auth_polkit)) {
- nm_log_warn (LOGD_CORE, "Unrecognized value for main.auth-polkit: %s. Assuming '%s'", value,
- NM_CONFIG_DEFAULT_AUTH_POLKIT ? "true" : "false");
- }
- g_free (value);
- }
+ priv->auth_polkit = _get_bool_value (priv->keyfile, "main", "auth-polkit", NM_CONFIG_DEFAULT_AUTH_POLKIT);
priv->dhcp_client = g_key_file_get_value (priv->keyfile, "main", "dhcp", NULL);
priv->dns_mode = g_key_file_get_value (priv->keyfile, "main", "dns", NULL);
@@ -623,6 +611,8 @@ nm_config_new (GError **error)
priv->ignore_carrier = g_key_file_get_string_list (priv->keyfile, "main", "ignore-carrier", NULL, NULL);
+ priv->configure_and_quit = _get_bool_value (priv->keyfile, "main", "configure-and-quit", FALSE);
+
return singleton;
}
diff --git a/src/nm-config.h b/src/nm-config.h
index 27da4cbbc6..56f75fb5f6 100644
--- a/src/nm-config.h
+++ b/src/nm-config.h
@@ -61,6 +61,7 @@ const char *nm_config_get_debug (NMConfig *config);
const char *nm_config_get_connectivity_uri (NMConfig *config);
guint nm_config_get_connectivity_interval (NMConfig *config);
const char *nm_config_get_connectivity_response (NMConfig *config);
+gboolean nm_config_get_configure_and_quit (NMConfig *config);
gboolean nm_config_get_ethernet_can_auto_default (NMConfig *config, NMDevice *device);
void nm_config_set_ethernet_no_auto_default (NMConfig *config, NMDevice *device);
diff --git a/src/nm-iface-helper.c b/src/nm-iface-helper.c
new file mode 100644
index 0000000000..62f1b2aba4
--- /dev/null
+++ b/src/nm-iface-helper.c
@@ -0,0 +1,529 @@
+/* -*- 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) 2014 Red Hat, Inc.
+ */
+
+#include <config.h>
+#include <glib.h>
+#include <glib-unix.h>
+#include <getopt.h>
+#include <locale.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <glib/gi18n.h>
+#include <string.h>
+#include <sys/resource.h>
+#include <sys/stat.h>
+#include <signal.h>
+
+#include "gsystem-local-alloc.h"
+#include "NetworkManagerUtils.h"
+#include "nm-linux-platform.h"
+#include "nm-dhcp-manager.h"
+#include "nm-logging.h"
+#include "main-utils.h"
+#include "nm-rdisc.h"
+#include "nm-lndp-rdisc.h"
+#include "nm-utils.h"
+
+#if !defined(NM_DIST_VERSION)
+# define NM_DIST_VERSION VERSION
+#endif
+
+#define NMIH_PID_FILE_FMT NMRUNDIR "/nm-iface-helper-%d.pid"
+
+static GMainLoop *main_loop = NULL;
+static char *ifname = NULL;
+static int ifindex = -1;
+static gboolean slaac_required = FALSE;
+static gboolean dhcp4_required = FALSE;
+static int tempaddr = NM_SETTING_IP6_CONFIG_PRIVACY_UNKNOWN;
+static int priority = -1;
+
+static void
+dhcp4_state_changed (NMDhcpClient *client,
+ NMDhcpState state,
+ NMIP4Config *ip4_config,
+ GHashTable *options,
+ gpointer user_data)
+{
+ static NMIP4Config *last_config = NULL;
+ NMIP4Config *existing;
+
+ g_return_if_fail (!ip4_config || NM_IS_IP4_CONFIG (ip4_config));
+
+ nm_log_dbg (LOGD_DHCP4, "(%s): new DHCPv4 client state %d", ifname, state);
+
+ switch (state) {
+ case NM_DHCP_STATE_BOUND:
+ g_assert (ip4_config);
+ existing = nm_ip4_config_capture (ifindex, FALSE);
+ if (last_config)
+ nm_ip4_config_subtract (existing, last_config);
+
+ nm_ip4_config_merge (existing, ip4_config);
+ if (!nm_ip4_config_commit (existing, ifindex))
+ nm_log_warn (LOGD_DHCP4, "(%s): failed to apply DHCPv4 config", ifname);
+
+ if (last_config) {
+ g_object_unref (last_config);
+ last_config = nm_ip4_config_new ();
+ nm_ip4_config_replace (last_config, ip4_config, NULL);
+ }
+ break;
+ case NM_DHCP_STATE_TIMEOUT:
+ case NM_DHCP_STATE_DONE:
+ case NM_DHCP_STATE_FAIL:
+ if (dhcp4_required) {
+ nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 timed out or failed, quitting...", ifname);
+ g_main_loop_quit (main_loop);
+ } else
+ nm_log_warn (LOGD_DHCP4, "(%s): DHCPv4 timed out or failed", ifname);
+ break;
+ default:
+ break;
+ }
+}
+
+static void
+rdisc_config_changed (NMRDisc *rdisc, NMRDiscConfigMap changed, gpointer user_data)
+{
+ static NMIP6Config *last_config = NULL;
+ NMIP6Config *existing;
+ NMIP6Config *ip6_config;
+ static int system_support = -1;
+ guint ifa_flags = 0x00;
+ int i;
+
+ if (system_support == -1) {
+ /*
+ * Check, if both libnl and the kernel are recent enough,
+ * to help user space handling RA. If it's not supported,
+ * we have no ipv6-privacy and must add autoconf addresses
+ * as /128. The reason for the /128 is to prevent the kernel
+ * from adding a prefix route for this address.
+ **/
+ system_support = nm_platform_check_support_libnl_extended_ifa_flags () &&
+ nm_platform_check_support_kernel_extended_ifa_flags ();
+ }
+
+ if (system_support)
+ ifa_flags = IFA_F_NOPREFIXROUTE;
+ if (tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_TEMP_ADDR
+ || tempaddr == NM_SETTING_IP6_CONFIG_PRIVACY_PREFER_PUBLIC_ADDR)
+ {
+ /* without system_support, this flag will be ignored. Still set it, doesn't seem to do any harm. */
+ ifa_flags |= IFA_F_MANAGETEMPADDR;
+ }
+
+ ip6_config = nm_ip6_config_new ();
+
+ if (changed & NM_RDISC_CONFIG_GATEWAYS) {
+ /* Use the first gateway as ordered in router discovery cache. */
+ if (rdisc->gateways->len) {
+ NMRDiscGateway *gateway = &g_array_index (rdisc->gateways, NMRDiscGateway, 0);
+
+ nm_ip6_config_set_gateway (ip6_config, &gateway->address);
+ } else
+ nm_ip6_config_set_gateway (ip6_config, NULL);
+ }
+
+ if (changed & NM_RDISC_CONFIG_ADDRESSES) {
+ /* Rebuild address list from router discovery cache. */
+ nm_ip6_config_reset_addresses (ip6_config);
+
+ /* rdisc->addresses contains at most max_addresses entries.
+ * This is different from what the kernel does, which
+ * also counts static and temporary addresses when checking
+ * max_addresses.
+ **/
+ for (i = 0; i < rdisc->addresses->len; i++) {
+ NMRDiscAddress *discovered_address = &g_array_index (rdisc->addresses, NMRDiscAddress, i);
+ NMPlatformIP6Address address;
+
+ memset (&address, 0, sizeof (address));
+ address.address = discovered_address->address;
+ address.plen = system_support ? 64 : 128;
+ address.timestamp = discovered_address->timestamp;
+ address.lifetime = discovered_address->lifetime;
+ address.preferred = discovered_address->preferred;
+ if (address.preferred > address.lifetime)
+ address.preferred = address.lifetime;
+ address.source = NM_IP_CONFIG_SOURCE_RDISC;
+ address.flags = ifa_flags;
+
+ nm_ip6_config_add_address (ip6_config, &address);
+ }
+ }
+
+ if (changed & NM_RDISC_CONFIG_ROUTES) {
+ /* Rebuild route list from router discovery cache. */
+ nm_ip6_config_reset_routes (ip6_config);
+
+ for (i = 0; i < rdisc->routes->len; i++) {
+ NMRDiscRoute *discovered_route = &g_array_index (rdisc->routes, NMRDiscRoute, i);
+ NMPlatformIP6Route route;
+
+ /* Only accept non-default routes. The router has no idea what the
+ * local configuration or user preferences are, so sending routes
+ * with a prefix length of 0 is quite rude and thus ignored.
+ */
+ if (discovered_route->plen > 0) {
+ memset (&route, 0, sizeof (route));
+ route.network = discovered_route->network;
+ route.plen = discovered_route->plen;
+ route.gateway = discovered_route->gateway;
+ route.source = NM_IP_CONFIG_SOURCE_RDISC;
+ route.metric = priority;
+
+ nm_ip6_config_add_route (ip6_config, &route);
+ }
+ }
+ }
+
+ if (changed & NM_RDISC_CONFIG_DHCP_LEVEL) {
+ /* Unsupported until systemd DHCPv6 is ready */
+ }
+
+ /* hop_limit == 0 is a special value "unspecified", so do not touch
+ * in this case */
+ if (changed & NM_RDISC_CONFIG_HOP_LIMIT && rdisc->hop_limit > 0) {
+ char val[16];
+
+ g_snprintf (val, sizeof (val), "%d", rdisc->hop_limit);
+ nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "hop_limit"), val);
+ }
+
+ if (changed & NM_RDISC_CONFIG_MTU) {
+ char val[16];
+
+ g_snprintf (val, sizeof (val), "%d", rdisc->mtu);
+ nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "mtu"), val);
+ }
+
+ existing = nm_ip6_config_capture (ifindex, FALSE, tempaddr);
+ if (last_config)
+ nm_ip6_config_subtract (existing, last_config);
+
+ nm_ip6_config_merge (existing, ip6_config);
+ if (!nm_ip6_config_commit (existing, ifindex))
+ nm_log_warn (LOGD_IP6, "(%s): failed to apply IPv6 config", ifname);
+
+ if (last_config) {
+ g_object_unref (last_config);
+ last_config = nm_ip6_config_new ();
+ nm_ip6_config_replace (last_config, ip6_config, NULL);
+ }
+}
+
+static void
+rdisc_ra_timeout (NMRDisc *rdisc, gpointer user_data)
+{
+ if (slaac_required) {
+ nm_log_warn (LOGD_IP6, "(%s): IPv6 timed out or failed, quitting...", ifname);
+ g_main_loop_quit (main_loop);
+ } else
+ nm_log_warn (LOGD_IP6, "(%s): IPv6 timed out or failed", ifname);
+}
+
+static gboolean
+quit_handler (gpointer user_data)
+{
+ gboolean *quit_early_ptr = user_data;
+
+ *quit_early_ptr = TRUE;
+ g_main_loop_quit (main_loop);
+ return G_SOURCE_CONTINUE;
+}
+
+static void
+setup_signals (gboolean *quit_early_ptr)
+{
+ sigset_t sigmask;
+
+ sigemptyset (&sigmask);
+ pthread_sigmask (SIG_SETMASK, &sigmask, NULL);
+
+ signal (SIGPIPE, SIG_IGN);
+ g_unix_signal_add (SIGINT, quit_handler, quit_early_ptr);
+ g_unix_signal_add (SIGTERM, quit_handler, quit_early_ptr);
+}
+
+int
+main (int argc, char *argv[])
+{
+ char *opt_log_level = NULL;
+ char *opt_log_domains = NULL;
+ gboolean debug = FALSE, g_fatal_warnings = FALSE, become_daemon = FALSE;
+ gboolean show_version = FALSE, slaac = FALSE;
+ char *bad_domains = NULL, *dhcp4_hostname = NULL, *uuid = NULL;
+ char *iid_str = NULL, *dhcp4_clientid = NULL, *dhcp4_address = NULL;
+ gs_unref_object NMDhcpManager *dhcp_mgr = NULL;
+ GError *error = NULL;
+ gboolean wrote_pidfile = FALSE;
+ gs_free char *pidfile = NULL;
+ gboolean quit_early = FALSE;
+ gs_unref_object NMDhcpClient *dhcp4_client = NULL;
+ gs_unref_object NMRDisc *rdisc = NULL;
+ GByteArray *hwaddr = NULL;
+ size_t hwaddr_len = 0;
+ gconstpointer tmp;
+ gs_free NMUtilsIPv6IfaceId *iid = NULL;
+
+ GOptionEntry options[] = {
+ /* Interface/IP config */
+ { "ifname", 'i', 0, G_OPTION_ARG_STRING, &ifname, N_("The interface to manage"), N_("eth0") },
+ { "uuid", 'u', 0, G_OPTION_ARG_STRING, &uuid, N_("Connection UUID"), N_("661e8cd0-b618-46b8-9dc9-31a52baaa16b") },
+ { "slaac", 's', 0, G_OPTION_ARG_NONE, &slaac, N_("Whether to manage IPv6 SLAAC"), NULL },
+ { "slaac-required", '6', 0, G_OPTION_ARG_NONE, &slaac_required, N_("Whether SLAAC must be successful"), NULL },
+ { "slaac-tempaddr", 't', 0, G_OPTION_ARG_INT, &tempaddr, N_("Use an IPv6 temporary privacy address"), NULL },
+ { "dhcp4", 'd', 0, G_OPTION_ARG_STRING, &dhcp4_address, N_("Current DHCPv4 address"), NULL },
+ { "dhcp4-required", '4', 0, G_OPTION_ARG_NONE, &dhcp4_required, N_("Whether DHCPv4 must be successful"), NULL },
+ { "dhcp4-clientid", 'c', 0, G_OPTION_ARG_STRING, &dhcp4_clientid, N_("Hex-encoded DHCPv4 client ID"), NULL },
+ { "dhcp4-hostname", 'h', 0, G_OPTION_ARG_STRING, &dhcp4_hostname, N_("Hostname to send to DHCP server"), N_("barbar") },
+ { "priority", 'p', 0, G_OPTION_ARG_INT, &priority, N_("Route priority"), N_("10") },
+ { "iid", 'e', 0, G_OPTION_ARG_STRING, &iid_str, N_("Hex-encoded Interface Identifier"), N_("") },
+
+ /* Logging/debugging */
+ { "version", 'V', 0, G_OPTION_ARG_NONE, &show_version, N_("Print NetworkManager version and exit"), NULL },
+ { "no-daemon", 'n', G_OPTION_FLAG_REVERSE, G_OPTION_ARG_NONE, &become_daemon, N_("Don't become a daemon"), NULL },
+ { "debug", 'b', 0, G_OPTION_ARG_NONE, &debug, N_("Don't become a daemon, and log to stderr"), NULL },
+ { "log-level", 0, 0, G_OPTION_ARG_STRING, &opt_log_level, N_("Log level: one of [%s]"), "INFO" },
+ { "log-domains", 0, 0, G_OPTION_ARG_STRING, &opt_log_domains,
+ N_("Log domains separated by ',': any combination of [%s]"),
+ "PLATFORM,RFKILL,WIFI" },
+ { "g-fatal-warnings", 0, 0, G_OPTION_ARG_NONE, &g_fatal_warnings, N_("Make all warnings fatal"), NULL },
+ {NULL}
+ };
+
+ setpgid (getpid (), getpid ());
+
+ if (!nm_main_utils_early_setup ("nm-iface-helper",
+ &argv,
+ &argc,
+ options,
+ NULL,
+ _("nm-iface-helper is a small, standalone process that manages a single network interface.")))
+ exit (1);
+
+ if (show_version) {
+ fprintf (stdout, NM_DIST_VERSION "\n");
+ exit (0);
+ }
+
+ if (!ifname || !uuid) {
+ fprintf (stderr, _("An interface name and UUID are required\n"));
+ exit (1);
+ }
+
+ if (!nm_logging_setup (opt_log_level,
+ opt_log_domains,
+ &bad_domains,
+ &error)) {
+ fprintf (stderr,
+ _("%s. Please use --help to see a list of valid options.\n"),
+ error->message);
+ exit (1);
+ } else if (bad_domains) {
+ fprintf (stderr,
+ _("Ignoring unrecognized log domain(s) '%s' passed on command line.\n"),
+ bad_domains);
+ g_clear_pointer (&bad_domains, g_free);
+ }
+
+ pidfile = g_strdup_printf (NMIH_PID_FILE_FMT, ifindex);
+ g_assert (pidfile);
+
+ /* check pid file */
+ if (nm_main_utils_check_pidfile (pidfile, "nm-iface-helper"))
+ exit (1);
+
+ if (become_daemon && !debug) {
+ if (daemon (0, 0) < 0) {
+ int saved_errno;
+
+ saved_errno = errno;
+ fprintf (stderr, _("Could not daemonize: %s [error %u]\n"),
+ g_strerror (saved_errno),
+ saved_errno);
+ exit (1);
+ }
+ if (nm_main_utils_write_pidfile (pidfile))
+ wrote_pidfile = TRUE;
+ }
+
+ /* Set up unix signal handling - before creating threads, but after daemonizing! */
+ main_loop = g_main_loop_new (NULL, FALSE);
+ setup_signals (&quit_early);
+
+ if (g_fatal_warnings) {
+ GLogLevelFlags fatal_mask;
+
+ fatal_mask = g_log_set_always_fatal (G_LOG_FATAL_MASK);
+ fatal_mask |= G_LOG_LEVEL_WARNING | G_LOG_LEVEL_CRITICAL;
+ g_log_set_always_fatal (fatal_mask);
+ }
+
+ nm_logging_syslog_openlog (debug);
+
+#if !GLIB_CHECK_VERSION (2, 35, 0)
+ g_type_init ();
+#endif
+
+ nm_log_info (LOGD_CORE, "nm-iface-helper (version " NM_DIST_VERSION ") is starting...");
+
+ /* Set up platform interaction layer */
+ nm_linux_platform_setup ();
+
+ ifindex = nm_platform_link_get_ifindex (ifname);
+ if (ifindex <= 0) {
+ fprintf (stderr, _("Failed to find interface index for %s\n"), ifname);
+ exit (1);
+ }
+
+ tmp = nm_platform_link_get_address (ifindex, &hwaddr_len);
+ if (tmp) {
+ hwaddr = g_byte_array_sized_new (hwaddr_len);
+ g_byte_array_append (hwaddr, tmp, hwaddr_len);
+ }
+
+ if (iid_str) {
+ GBytes *bytes;
+ gsize ignored = 0;
+
+ bytes = nm_utils_hexstr2bin (iid_str);
+ if (!bytes || g_bytes_get_size (bytes) != sizeof (*iid)) {
+ fprintf (stderr, _("(%s): Invalid IID %s\n"), ifname, iid_str);
+ exit (1);
+ }
+ iid = g_bytes_unref_to_data (bytes, &ignored);
+ }
+
+ priority = MAX (0, priority);
+
+ if (dhcp4_address) {
+ nm_platform_sysctl_set (nm_utils_ip4_property_path (ifname, "promote_secondaries"), "1");
+
+ /* Initialize DHCP manager */
+ dhcp_mgr = nm_dhcp_manager_get ();
+ g_assert (dhcp_mgr != NULL);
+
+ dhcp4_client = nm_dhcp_manager_start_ip4 (dhcp_mgr,
+ ifname,
+ ifindex,
+ hwaddr,
+ uuid,
+ priority,
+ !!dhcp4_hostname,
+ dhcp4_hostname,
+ dhcp4_clientid,
+ 45,
+ NULL,
+ dhcp4_address);
+ g_assert (dhcp4_client);
+ g_signal_connect (dhcp4_client,
+ NM_DHCP_CLIENT_SIGNAL_STATE_CHANGED,
+ G_CALLBACK (dhcp4_state_changed),
+ NULL);
+ }
+
+ if (slaac) {
+ nm_platform_link_set_user_ipv6ll_enabled (ifindex, TRUE);
+
+ rdisc = nm_lndp_rdisc_new (ifindex, ifname);
+ g_assert (rdisc);
+
+ if (iid)
+ nm_rdisc_set_iid (rdisc, *iid);
+
+ nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "accept_ra"), "1");
+ nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "accept_ra_defrtr"), "0");
+ nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "accept_ra_pinfo"), "0");
+ nm_platform_sysctl_set (nm_utils_ip6_property_path (ifname, "accept_ra_rtr_pref"), "0");
+
+ g_signal_connect (rdisc,
+ NM_RDISC_CONFIG_CHANGED,
+ G_CALLBACK (rdisc_config_changed),
+ NULL);
+ g_signal_connect (rdisc,
+ NM_RDISC_RA_TIMEOUT,
+ G_CALLBACK (rdisc_ra_timeout),
+ NULL);
+ nm_rdisc_start (rdisc);
+ }
+
+ if (!quit_early)
+ g_main_loop_run (main_loop);
+
+ g_clear_pointer (&hwaddr, g_byte_array_unref);
+
+ nm_logging_syslog_closelog ();
+
+ if (pidfile && wrote_pidfile)
+ unlink (pidfile);
+
+ nm_log_info (LOGD_CORE, "exiting");
+ exit (0);
+}
+
+/*******************************************************/
+/* Stub functions */
+
+gconstpointer nm_config_get (void);
+const char *nm_config_get_dhcp_client (gpointer unused);
+gboolean nm_config_get_configure_and_quit (gpointer unused);
+gconstpointer nm_dbus_manager_get (void);
+void nm_dbus_manager_register_exported_type (gpointer unused, GType gtype, gconstpointer unused2);
+void nm_dbus_manager_register_object (gpointer unused, const char *path, gpointer object);
+
+gconstpointer
+nm_config_get (void)
+{
+ return GUINT_TO_POINTER (1);
+}
+
+const char *
+nm_config_get_dhcp_client (gpointer unused)
+{
+ return "internal";
+}
+
+gboolean
+nm_config_get_configure_and_quit (gpointer unused)
+{
+ return TRUE;
+}
+
+gconstpointer
+nm_dbus_manager_get (void)
+{
+ return GUINT_TO_POINTER (1);
+}
+
+void
+nm_dbus_manager_register_exported_type (gpointer unused, GType gtype, gconstpointer unused2)
+{
+}
+
+void
+nm_dbus_manager_register_object (gpointer unused, const char *path, gpointer object)
+{
+}
+
diff --git a/src/nm-ip4-config.c b/src/nm-ip4-config.c
index 72c1b0cd75..7548daded0 100644
--- a/src/nm-ip4-config.c
+++ b/src/nm-ip4-config.c
@@ -83,6 +83,7 @@ nm_ip4_config_new (void)
}
+#ifndef NM_IFACE_HELPER
void
nm_ip4_config_export (NMIP4Config *config)
{
@@ -94,6 +95,7 @@ nm_ip4_config_export (NMIP4Config *config)
nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, config);
}
}
+#endif
const char *
nm_ip4_config_get_dbus_path (const NMIP4Config *config)
@@ -1952,7 +1954,9 @@ nm_ip4_config_class_init (NMIP4ConfigClass *config_class)
g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
+#ifndef NM_IFACE_HELPER
nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
G_TYPE_FROM_CLASS (config_class),
&dbus_glib_nm_ip4_config_object_info);
+#endif
}
diff --git a/src/nm-ip6-config.c b/src/nm-ip6-config.c
index fe45f824d8..f0930b7453 100644
--- a/src/nm-ip6-config.c
+++ b/src/nm-ip6-config.c
@@ -73,6 +73,7 @@ nm_ip6_config_new (void)
return (NMIP6Config *) g_object_new (NM_TYPE_IP6_CONFIG, NULL);
}
+#ifndef NM_IFACE_HELPER
void
nm_ip6_config_export (NMIP6Config *config)
{
@@ -84,6 +85,7 @@ nm_ip6_config_export (NMIP6Config *config)
nm_dbus_manager_register_object (nm_dbus_manager_get (), priv->path, config);
}
}
+#endif
const char *
nm_ip6_config_get_dbus_path (const NMIP6Config *config)
@@ -1863,7 +1865,9 @@ nm_ip6_config_class_init (NMIP6ConfigClass *config_class)
g_object_class_install_properties (object_class, LAST_PROP, obj_properties);
+#ifndef NM_IFACE_HELPER
nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
G_TYPE_FROM_CLASS (config_class),
&dbus_glib_nm_ip6_config_object_info);
+#endif
}
diff --git a/src/nm-manager.c b/src/nm-manager.c
index f936235c78..d8973b7337 100644
--- a/src/nm-manager.c
+++ b/src/nm-manager.c
@@ -59,6 +59,7 @@
#include "nm-session-monitor.h"
#include "nm-activation-request.h"
#include "nm-core-internal.h"
+#include "nm-config.h"
#define NM_AUTOIP_DBUS_SERVICE "org.freedesktop.nm_avahi_autoipd"
#define NM_AUTOIP_DBUS_IFACE "org.freedesktop.nm_avahi_autoipd"
@@ -214,6 +215,7 @@ enum {
USER_PERMISSIONS_CHANGED,
ACTIVE_CONNECTION_ADDED,
ACTIVE_CONNECTION_REMOVED,
+ CONFIGURE_QUIT,
LAST_SIGNAL
};
@@ -706,6 +708,9 @@ check_if_startup_complete (NMManager *self)
g_signal_handlers_disconnect_by_func (dev, G_CALLBACK (device_has_pending_action_changed), self);
}
+
+ if (nm_config_get_configure_and_quit (nm_config_get ()))
+ g_signal_emit (self, signals[CONFIGURE_QUIT], 0);
}
static void
@@ -745,6 +750,8 @@ remove_device (NMManager *manager,
nm_device_set_unmanaged_quitting (device);
else
nm_device_set_unmanaged (device, NM_UNMANAGED_INTERNAL, TRUE, NM_DEVICE_STATE_REASON_REMOVED);
+ } else if (quitting && nm_config_get_configure_and_quit (nm_config_get ())) {
+ nm_device_spawn_iface_helper (device);
}
}
@@ -4167,6 +4174,16 @@ nm_manager_start (NMManager *self)
check_if_startup_complete (self);
}
+void
+nm_manager_stop (NMManager *self)
+{
+ NMManagerPrivate *priv = NM_MANAGER_GET_PRIVATE (self);
+
+ /* Remove all devices */
+ while (priv->devices)
+ remove_device (self, NM_DEVICE (priv->devices->data), TRUE, TRUE);
+}
+
static gboolean
handle_firmware_changed (gpointer user_data)
{
@@ -4990,9 +5007,7 @@ dispose (GObject *object)
G_CALLBACK (authority_changed_cb),
manager);
- /* Remove all devices */
- while (priv->devices)
- remove_device (manager, NM_DEVICE (priv->devices->data), TRUE, TRUE);
+ g_assert (priv->devices == NULL);
if (priv->ac_cleanup_id) {
g_source_remove (priv->ac_cleanup_id);
@@ -5258,6 +5273,13 @@ nm_manager_class_init (NMManagerClass *manager_class)
0, NULL, NULL, NULL,
G_TYPE_NONE, 1, G_TYPE_OBJECT);
+ signals[CONFIGURE_QUIT] =
+ g_signal_new (NM_MANAGER_CONFIGURE_QUIT,
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_FIRST,
+ 0, NULL, NULL, NULL,
+ G_TYPE_NONE, 0);
+
nm_dbus_manager_register_exported_type (nm_dbus_manager_get (),
G_TYPE_FROM_CLASS (manager_class),
&dbus_glib_nm_manager_object_info);
diff --git a/src/nm-manager.h b/src/nm-manager.h
index f623516159..3b00e8053a 100644
--- a/src/nm-manager.h
+++ b/src/nm-manager.h
@@ -59,6 +59,7 @@
/* Internal signals */
#define NM_MANAGER_ACTIVE_CONNECTION_ADDED "active-connection-added"
#define NM_MANAGER_ACTIVE_CONNECTION_REMOVED "active-connection-removed"
+#define NM_MANAGER_CONFIGURE_QUIT "configure-quit"
struct _NMManager {
@@ -88,6 +89,7 @@ NMManager * nm_manager_new (NMSettings *settings,
NMManager * nm_manager_get (void);
void nm_manager_start (NMManager *manager);
+void nm_manager_stop (NMManager *manager);
NMState nm_manager_get_state (NMManager *manager);
const GSList *nm_manager_get_active_connections (NMManager *manager);
GSList * nm_manager_get_activatable_connections (NMManager *manager);
diff --git a/src/settings/plugins/ifcfg-rh/reader.c b/src/settings/plugins/ifcfg-rh/reader.c
index 5243d77f18..f97c84c782 100644
--- a/src/settings/plugins/ifcfg-rh/reader.c
+++ b/src/settings/plugins/ifcfg-rh/reader.c
@@ -3224,7 +3224,6 @@ make_wireless_setting (shvarFile *ifcfg,
GError **error)
{
NMSettingWireless *s_wireless;
- GBytes *bytes = NULL;
char *value = NULL;
gint64 chan = 0;
@@ -3256,19 +3255,19 @@ make_wireless_setting (shvarFile *ifcfg,
value = svGetValue (ifcfg, "ESSID", TRUE);
if (value) {
- gsize ssid_len = 0, value_len = strlen (value);
- char *p = value, *tmp;
- char buf[33];
+ GBytes *bytes = NULL;
+ gsize ssid_len = 0;
+ gsize value_len = strlen (value);
- ssid_len = value_len;
if ( (value_len >= 2)
&& (value[0] == '"')
&& (value[value_len - 1] == '"')) {
/* Strip the quotes and unescape */
- p = value + 1;
+ char *p = value + 1;
+
value[value_len - 1] = '\0';
svUnescape (p);
- ssid_len = strlen (p);
+ bytes = g_bytes_new (p, strlen (p));
} else if ((value_len > 2) && (strncmp (value, "0x", 2) == 0)) {
/* Hex representation */
if (value_len % 2) {
@@ -3279,34 +3278,27 @@ make_wireless_setting (shvarFile *ifcfg,
goto error;
}
- p = value + 2;
- while (*p) {
- if (!g_ascii_isxdigit (*p)) {
- g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
- "Invalid SSID '%s' character (looks like hex SSID but '%c' isn't a hex digit)",
- value, *p);
- g_free (value);
- goto error;
- }
- p++;
+ bytes = nm_utils_hexstr2bin (value);
+ if (!bytes) {
+ g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
+ "Invalid SSID '%s' (looks like hex SSID but isn't)",
+ value);
+ g_free (value);
+ goto error;
}
+ } else
+ bytes = g_bytes_new (value, value_len);
- tmp = nm_utils_hexstr2bin (value + 2, value_len - 2);
- ssid_len = (value_len - 2) / 2;
- memcpy (buf, tmp, ssid_len);
- p = &buf[0];
- g_free (tmp);
- }
-
+ ssid_len = g_bytes_get_size (bytes);
if (ssid_len > 32 || ssid_len == 0) {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Invalid SSID '%s' (size %zu not between 1 and 32 inclusive)",
value, ssid_len);
+ g_bytes_unref (bytes);
g_free (value);
goto error;
}
- bytes = g_bytes_new (p, ssid_len);
g_object_set (s_wireless, NM_SETTING_WIRELESS_SSID, bytes, NULL);
g_bytes_unref (bytes);
g_free (value);
diff --git a/src/settings/plugins/ifnet/connection_parser.c b/src/settings/plugins/ifnet/connection_parser.c
index 5ffc0605e1..651cd8ea71 100644
--- a/src/settings/plugins/ifnet/connection_parser.c
+++ b/src/settings/plugins/ifnet/connection_parser.c
@@ -43,8 +43,16 @@ connection_id_from_ifnet_name (const char *conn_name)
int name_len = strlen (conn_name);
/* Convert a hex-encoded conn_name (only used for wifi SSIDs) to human-readable one */
- if ((name_len > 2) && (g_str_has_prefix (conn_name, "0x")))
- return nm_utils_hexstr2bin (conn_name + 2, name_len - 2);
+ if ((name_len > 2) && (g_str_has_prefix (conn_name, "0x"))) {
+ GBytes *bytes = nm_utils_hexstr2bin (conn_name);
+ char *buf;
+
+ if (bytes) {
+ buf = g_strndup (g_bytes_get_data (bytes, NULL), g_bytes_get_size (bytes));
+ g_bytes_unref (bytes);
+ return buf;
+ }
+ }
return g_strdup (conn_name);
}
@@ -882,7 +890,6 @@ make_wireless_connection_setting (const char *conn_name,
NMSetting8021x **s_8021x,
GError **error)
{
- GBytes *bytes;
const char *mac = NULL;
NMSettingWireless *wireless_setting = NULL;
gboolean adhoc = FALSE;
@@ -913,9 +920,8 @@ make_wireless_connection_setting (const char *conn_name,
/* handle ssid (hex and ascii) */
if (conn_name) {
+ GBytes *bytes;
gsize ssid_len = 0, value_len = strlen (conn_name);
- const char *p;
- char *tmp, *converted = NULL;
ssid_len = value_len;
if ((value_len > 2) && (g_str_has_prefix (conn_name, "0x"))) {
@@ -926,32 +932,27 @@ make_wireless_connection_setting (const char *conn_name,
conn_name);
goto error;
}
- // ignore "0x"
- p = conn_name + 2;
- if (!is_hex (p)) {
+
+ bytes = nm_utils_hexstr2bin (conn_name);
+ if (!bytes) {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
- "Invalid SSID '%s' character (looks like hex SSID but '%c' isn't a hex digit)",
- conn_name, *p);
+ "Invalid SSID '%s' (looks like hex SSID but isn't)",
+ conn_name);
goto error;
-
}
- tmp = nm_utils_hexstr2bin (p, value_len - 2);
- ssid_len = (value_len - 2) / 2;
- converted = g_malloc0 (ssid_len + 1);
- memcpy (converted, tmp, ssid_len);
- g_free (tmp);
- }
+ } else
+ bytes = g_bytes_new (conn_name, value_len);
+ ssid_len = g_bytes_get_size (bytes);
if (ssid_len > 32 || ssid_len == 0) {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Invalid SSID '%s' (size %zu not between 1 and 32 inclusive)",
conn_name, ssid_len);
goto error;
}
- bytes = g_bytes_new (converted ? converted : conn_name, ssid_len);
+
g_object_set (wireless_setting, NM_SETTING_WIRELESS_SSID, bytes, NULL);
g_bytes_unref (bytes);
- g_free (converted);
} else {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
"Missing SSID");
@@ -1095,7 +1096,7 @@ add_one_wep_key (const char *ssid,
}
- converted = nm_utils_bin2hexstr (tmp, strlen (tmp), strlen (tmp) * 2);
+ converted = nm_utils_bin2hexstr (tmp, strlen (tmp), -1);
g_free (tmp);
} else {
g_set_error (error, NM_SETTINGS_ERROR, NM_SETTINGS_ERROR_INVALID_CONNECTION,
diff --git a/src/supplicant-manager/nm-supplicant-config.c b/src/supplicant-manager/nm-supplicant-config.c
index f4ee4c3a2c..2b857517a3 100644
--- a/src/supplicant-manager/nm-supplicant-config.c
+++ b/src/supplicant-manager/nm-supplicant-config.c
@@ -542,8 +542,8 @@ add_wep_key (NMSupplicantConfig *self,
const char *name,
NMWepKeyType wep_type)
{
- char *value;
- gboolean success;
+ GBytes *bytes;
+ gboolean success = FALSE;
size_t key_len = key ? strlen (key) : 0;
if (!key || !key_len)
@@ -552,9 +552,15 @@ add_wep_key (NMSupplicantConfig *self,
if ( (wep_type == NM_WEP_KEY_TYPE_UNKNOWN)
|| (wep_type == NM_WEP_KEY_TYPE_KEY)) {
if ((key_len == 10) || (key_len == 26)) {
- value = nm_utils_hexstr2bin (key, strlen (key));
- success = nm_supplicant_config_add_option (self, name, value, key_len / 2, TRUE);
- g_free (value);
+ bytes = nm_utils_hexstr2bin (key);
+ if (bytes) {
+ success = nm_supplicant_config_add_option (self,
+ name,
+ g_bytes_get_data (bytes, NULL),
+ g_bytes_get_size (bytes),
+ TRUE);
+ g_bytes_unref (bytes);
+ }
if (!success) {
nm_log_warn (LOGD_SUPPLICANT, "Error adding %s to supplicant config.", name);
return FALSE;
@@ -590,8 +596,7 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
NMSetting8021x *setting_8021x,
const char *con_uuid)
{
- char *value;
- gboolean success;
+ gboolean success = FALSE;
const char *key_mgmt, *auth_alg;
const char *psk;
@@ -612,10 +617,18 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
size_t psk_len = strlen (psk);
if (psk_len == 64) {
+ GBytes *bytes;
+
/* Hex PSK */
- value = nm_utils_hexstr2bin (psk, psk_len);
- success = nm_supplicant_config_add_option (self, "psk", value, psk_len / 2, TRUE);
- g_free (value);
+ bytes = nm_utils_hexstr2bin (psk);
+ if (bytes) {
+ success = nm_supplicant_config_add_option (self,
+ "psk",
+ g_bytes_get_data (bytes, NULL),
+ g_bytes_get_size (bytes),
+ TRUE);
+ g_bytes_unref (bytes);
+ }
if (!success) {
nm_log_warn (LOGD_SUPPLICANT, "Error adding 'psk' to supplicant config.");
return FALSE;
@@ -653,6 +666,7 @@ nm_supplicant_config_add_setting_wireless_security (NMSupplicantConfig *self,
const char *wep1 = nm_setting_wireless_security_get_wep_key (setting, 1);
const char *wep2 = nm_setting_wireless_security_get_wep_key (setting, 2);
const char *wep3 = nm_setting_wireless_security_get_wep_key (setting, 3);
+ char *value;
if (!add_wep_key (self, wep0, "wep_key0", wep_type))
return FALSE;