From 22762324e841df851219cec0a79bb063824c5dfc Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 6 Nov 2014 17:51:09 -0600 Subject: [PATCH 01/11] libnm,core: enhance nm_utils_hexstr2bin() Make the type return GBytes since most in-tree users want that. Allow the function to accept many more formats as valid hex, including bytes delimited by ':' and a leading '0x'. --- clients/cli/settings.c | 10 +- libnm-core/nm-utils.c | 105 +++++++++--------- libnm-core/nm-utils.h | 5 +- libnm-core/tests/test-general.c | 37 ++++++ libnm/libnm.ver | 1 - libnm/nm-device.c | 15 ++- src/dhcp-manager/nm-dhcp-utils.c | 41 +------ src/settings/plugins/ifcfg-rh/reader.c | 42 +++---- .../plugins/ifnet/connection_parser.c | 41 +++---- src/supplicant-manager/nm-supplicant-config.c | 34 ++++-- 10 files changed, 177 insertions(+), 154 deletions(-) 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/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 */ -/** - * nm_utils_hex2byte: - * @hex: a string representing a hex byte - * - * Converts a hex string (2 characters) into its byte representation. - * - * Return value: a byte, or -1 if @hex doesn't represent a hex byte - */ -int -nm_utils_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; -} - /** * nm_utils_hexstr2bin: - * @hex: an hex string - * @len: the length of the @hex string (it has to be even) + * @hex: a string of hexadecimal characters with optional ':' separators * - * Converts a hexadecimal string @hex into a byte-array. The returned array - * length is @len/2. + * 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: (transfer full): a array of bytes, or %NULL on error + * Return value: (transfer full): the converted bytes, or %NULL on error */ -char * -nm_utils_hexstr2bin (const char *hex, size_t len) +GBytes * +nm_utils_hexstr2bin (const char *hex) { - size_t i; - int a; - const char * ipos = hex; - char * buf = NULL; - char * opos; + guint i = 0, x = 0; + gs_free guint8 *c = NULL; + int a, b; + gboolean found_colon = FALSE; - /* Length must be a multiple of 2 */ - if ((len % 2) != 0) - return NULL; + g_return_val_if_fail (hex != NULL, NULL); - 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); + if (strncasecmp (hex, "0x", 2) == 0) + hex += 2; + found_colon = !!strchr (hex, ':'); + + 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/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/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; From ee255036362043c9b8fcf48a9f41abc2e2dd122d Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 29 Oct 2014 09:36:47 -0500 Subject: [PATCH 02/11] core: split signal/pidfile/option handling into separate source file We'll use this from more than one spot. --- po/POTFILES.in | 1 + src/Makefile.am | 2 + src/main-utils.c | 281 +++++++++++++++++++++++++++++++++++++++++++++++ src/main-utils.h | 39 +++++++ src/main.c | 238 ++------------------------------------- 5 files changed, 335 insertions(+), 226 deletions(-) create mode 100644 src/main-utils.c create mode 100644 src/main-utils.h diff --git a/po/POTFILES.in b/po/POTFILES.in index 5c44103240..b964b93f0e 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 diff --git a/src/Makefile.am b/src/Makefile.am index a0447bdd42..5735a3f6f0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -124,6 +124,8 @@ sbin_PROGRAMS = NetworkManager NetworkManager_SOURCES = \ $(nm_device_sources) $(nm_device_headers) \ + main-utils.c \ + main-utils.h \ main.c NetworkManager_LDADD = libNetworkManager.la 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 +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#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 + +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..29cfd4bfd8 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, @@ -337,7 +184,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 +193,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 +206,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 +223,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); + main_loop = g_main_loop_new (NULL, FALSE); - /* - * 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")); + 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 (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); - - if (!success) { - fprintf (stderr, _("%s. Please use --help to see a list of valid options.\n"), - error->message); - g_clear_error (&error); - exit (1); - } if (show_version) { fprintf (stdout, NM_DIST_VERSION "\n"); @@ -482,12 +277,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 +287,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 +338,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 +380,6 @@ main (int argc, char *argv[]) #endif ); - main_loop = g_main_loop_new (NULL, FALSE); - /* Set up platform interaction layer */ nm_linux_platform_setup (); From 0b98dc4387c52616aa39979ce61848e6ca8b06b7 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 29 Oct 2014 15:04:38 -0500 Subject: [PATCH 03/11] dhcp: move D-Bus DHCP listener into separate class This simplifies the manager and ensures that only the clients that use D-Bus-based DHCP helpers need to care about them. --- src/Makefile.am | 2 + src/dhcp-manager/nm-dhcp-client.c | 27 ++- src/dhcp-manager/nm-dhcp-client.h | 11 +- src/dhcp-manager/nm-dhcp-dhclient.c | 10 + src/dhcp-manager/nm-dhcp-dhcpcd.c | 9 + src/dhcp-manager/nm-dhcp-listener.c | 289 ++++++++++++++++++++++++++++ src/dhcp-manager/nm-dhcp-listener.h | 39 ++++ src/dhcp-manager/nm-dhcp-manager.c | 237 +---------------------- 8 files changed, 377 insertions(+), 247 deletions(-) create mode 100644 src/dhcp-manager/nm-dhcp-listener.c create mode 100644 src/dhcp-manager/nm-dhcp-listener.h diff --git a/src/Makefile.am b/src/Makefile.am index 5735a3f6f0..92c8b58cf7 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -172,6 +172,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-listener.c \ + dhcp-manager/nm-dhcp-listener.h \ dhcp-manager/nm-dhcp-dhclient.c \ dhcp-manager/nm-dhcp-dhclient.h \ dhcp-manager/nm-dhcp-dhclient-utils.c \ diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c index b6b571eb5f..d6e308b959 100644 --- a/src/dhcp-manager/nm-dhcp-client.c +++ b/src/dhcp-manager/nm-dhcp-client.c @@ -673,10 +673,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 +687,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 +730,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..ca0fdd391b 100644 --- a/src/dhcp-manager/nm-dhcp-client.h +++ b/src/dhcp-manager/nm-dhcp-client.h @@ -128,10 +128,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 +140,12 @@ 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); + #endif /* __NETWORKMANAGER_DHCP_CLIENT_H__ */ diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c index e4b37377bc..58da218150 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -43,6 +43,7 @@ #include "nm-dhcp-manager.h" #include "nm-posix-signals.h" #include "NetworkManagerUtils.h" +#include "nm-dhcp-listener.h" G_DEFINE_TYPE (NMDhcpDhclient, nm_dhcp_dhclient, NM_TYPE_DHCP_CLIENT) @@ -612,6 +613,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 +625,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); diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c index a018c4cd42..0313462d7e 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) @@ -190,6 +191,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 +202,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); 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 +#include + +#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..beb9c94c65 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 #include -#include #include #include #include @@ -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"; @@ -60,76 +53,15 @@ 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) -{ - 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 NMDhcpClient * -get_client_for_pid (NMDhcpManager *manager, GPid pid) -{ - NMDhcpManagerPrivate *priv; - GHashTableIter iter; - gpointer value; - - g_return_val_if_fail (NM_IS_DHCP_MANAGER (manager), 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); - - if (nm_dhcp_client_get_pid (candidate) == pid) - return candidate; - } - - return NULL; -} +/***************************************************/ static NMDhcpClient * get_client_for_ifindex (NMDhcpManager *manager, int ifindex, gboolean ip6) @@ -155,123 +87,6 @@ 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) { @@ -529,12 +344,6 @@ nm_dhcp_manager_init (NMDhcpManager *self) NMDhcpManagerPrivate *priv = NM_DHCP_MANAGER_GET_PRIVATE (self); 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 ()); @@ -554,33 +363,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 +378,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); } From 318a8c2d727bee5c401f2d6b7f28f706ecb4dfb2 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 29 Oct 2014 17:12:46 -0500 Subject: [PATCH 04/11] dhcp: move client-specific knowledge out of the manager --- src/Makefile.am | 22 +++-- src/dhcp-manager/nm-dhcp-client.h | 11 +++ src/dhcp-manager/nm-dhcp-dhclient.c | 14 ++- src/dhcp-manager/nm-dhcp-dhclient.h | 6 -- src/dhcp-manager/nm-dhcp-dhcpcd.c | 12 ++- src/dhcp-manager/nm-dhcp-dhcpcd.h | 2 - src/dhcp-manager/nm-dhcp-manager.c | 147 +++++++++++++++++----------- src/dhcp-manager/nm-dhcp-systemd.c | 12 ++- src/dhcp-manager/nm-dhcp-systemd.h | 4 - src/dhcp-manager/tests/Makefile.am | 2 + 10 files changed, 153 insertions(+), 79 deletions(-) diff --git a/src/Makefile.am b/src/Makefile.am index 92c8b58cf7..e186632548 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -124,6 +124,7 @@ 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 @@ -155,8 +156,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 \ @@ -174,14 +188,6 @@ nm_sources = \ dhcp-manager/nm-dhcp-utils.h \ dhcp-manager/nm-dhcp-listener.c \ dhcp-manager/nm-dhcp-listener.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-manager.c \ dhcp-manager/nm-dhcp-manager.h \ \ diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h index ca0fdd391b..a9ce9bec37 100644 --- a/src/dhcp-manager/nm-dhcp-client.h +++ b/src/dhcp-manager/nm-dhcp-client.h @@ -99,6 +99,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); diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c index 58da218150..8f1718f8a6 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -56,7 +56,7 @@ typedef struct { char *pid_file; } NMDhcpDhclientPrivate; -const char * +static const char * nm_dhcp_dhclient_get_path (void) { const char *path = NULL; @@ -123,7 +123,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) @@ -653,3 +653,13 @@ nm_dhcp_dhclient_class_init (NMDhcpDhclientClass *dhclient_class) client_class->get_duid = get_duid; } +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 0313462d7e..c4d91b1247 100644 --- a/src/dhcp-manager/nm-dhcp-dhcpcd.c +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c @@ -48,7 +48,7 @@ typedef struct { char *pid_file; } NMDhcpDhcpcdPrivate; -const char * +static const char * nm_dhcp_dhcpcd_get_path (void) { const char *path = NULL; @@ -227,3 +227,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-manager.c b/src/dhcp-manager/nm-dhcp-manager.c index beb9c94c65..dbc0684dbc 100644 --- a/src/dhcp-manager/nm-dhcp-manager.c +++ b/src/dhcp-manager/nm-dhcp-manager.c @@ -52,7 +52,6 @@ typedef GSList * (*GetLeaseConfigFunc) (const char *iface, const char *uuid, gbo typedef struct { GType client_type; - GetLeaseConfigFunc get_lease_ip_configs_func; GHashTable * clients; char * default_hostname; } NMDhcpManagerPrivate; @@ -63,6 +62,79 @@ G_DEFINE_TYPE (NMDhcpManager, nm_dhcp_manager, G_TYPE_OBJECT) /***************************************************/ +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) +{ + ClientDesc *desc; + GSList *iter; + + g_return_if_fail (gtype != G_TYPE_INVALID); + g_return_if_fail (name != NULL); + + 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); + } + + 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 ClientDesc * +find_client_desc (const char *name, GType gtype) +{ + GSList *iter; + + g_return_val_if_fail (name || gtype, NULL); + + for (iter = client_descs; iter; iter = iter->next) { + ClientDesc *desc = iter->data; + + 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) { @@ -90,52 +162,23 @@ get_client_for_ifindex (NMDhcpManager *manager, int ifindex, gboolean ip6) static GType get_client_type (const char *client, GError **error) { - gboolean use_dhclient, use_dhcpcd; + GType client_gtype; - /* 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; + 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, @@ -312,16 +355,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; } @@ -348,12 +390,7 @@ nm_dhcp_manager_init (NMDhcpManager *self) /* Client-specific setup */ client = nm_config_get_dhcp_client (nm_config_get ()); priv->client_type = get_client_type (client, &error); - - 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) { + if (priv->client_type == G_TYPE_INVALID) { nm_log_warn (LOGD_DHCP, "No usable DHCP client found (%s)! DHCP configurations will fail.", error->message); } diff --git a/src/dhcp-manager/nm-dhcp-systemd.c b/src/dhcp-manager/nm-dhcp-systemd.c index 18b7b32180..6b06c45288 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) @@ -808,3 +808,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/tests/Makefile.am b/src/dhcp-manager/tests/Makefile.am index 0292db2b7a..1eb02d6323 100644 --- a/src/dhcp-manager/tests/Makefile.am +++ b/src/dhcp-manager/tests/Makefile.am @@ -18,6 +18,8 @@ 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 \ test-dhcp-dhclient.c test_dhcp_dhclient_LDADD = \ From e43174f368f4cb319897626207afea30f5896147 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 3 Nov 2014 14:39:25 -0600 Subject: [PATCH 05/11] dhcp: preserve DHCPv4 client ID for later use If we can, read the existing client ID from the leasefile and preserve it for later use. --- src/dhcp-manager/nm-dhcp-client.c | 28 +++- src/dhcp-manager/nm-dhcp-client.h | 5 +- src/dhcp-manager/nm-dhcp-dhclient-utils.c | 134 ++++++++++++++------ src/dhcp-manager/nm-dhcp-dhclient-utils.h | 7 +- src/dhcp-manager/nm-dhcp-dhclient.c | 51 ++++++-- src/dhcp-manager/nm-dhcp-dhcpcd.c | 1 - src/dhcp-manager/nm-dhcp-systemd.c | 58 +++++++-- src/dhcp-manager/tests/Makefile.am | 2 + src/dhcp-manager/tests/test-dhcp-dhclient.c | 124 ++++++++++++++++-- 9 files changed, 327 insertions(+), 83 deletions(-) diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c index d6e308b959..f455efa732 100644 --- a/src/dhcp-manager/nm-dhcp-client.c +++ b/src/dhcp-manager/nm-dhcp-client.c @@ -45,6 +45,7 @@ typedef struct { guint32 priority; guint32 timeout; GByteArray * duid; + GBytes * client_id; NMDhcpState state; pid_t pid; @@ -143,6 +144,29 @@ 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; +} + /********************************************/ static const char *state_table[NM_DHCP_STATE_MAX + 1] = { @@ -374,7 +398,9 @@ 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); + + return NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_anycast_addr, hostname); } /* uuid_parse does not work for machine-id, so we use our own converter */ diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h index a9ce9bec37..fda14cf4a5 100644 --- a/src/dhcp-manager/nm-dhcp-client.h +++ b/src/dhcp-manager/nm-dhcp-client.h @@ -64,7 +64,6 @@ typedef struct { /* Methods */ gboolean (*ip4_start) (NMDhcpClient *self, - const char *dhcp_client_id, const char *anycast_addr, const char *hostname); @@ -126,6 +125,8 @@ 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); + gboolean nm_dhcp_client_start_ip4 (NMDhcpClient *self, const char *dhcp_client_id, const char *dhcp_anycast_addr, @@ -158,5 +159,7 @@ gboolean nm_dhcp_client_handle_event (gpointer unused, 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 #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; + 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 (!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; - 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 8f1718f8a6..76e8d68c2c 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -44,6 +44,7 @@ #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) @@ -152,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; @@ -174,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); @@ -258,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; @@ -285,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); @@ -475,23 +478,28 @@ dhclient_start (NMDhcpClient *client, static gboolean ip4_start (NMDhcpClient *client, - const char *dhcp_client_id, const char *dhcp_anycast_addr, const char *hostname) { NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client); + GBytes *client_id; + gs_unref_bytes GBytes *new_client_id = NULL; const char *iface, *uuid; + 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); - 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 @@ -508,7 +516,7 @@ ip6_start (NMDhcpClient *client, iface = nm_dhcp_client_get_iface (client); uuid = nm_dhcp_client_get_uuid (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; @@ -545,6 +553,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) { @@ -651,6 +677,7 @@ 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)) diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c index c4d91b1247..5a3aee3f4d 100644 --- a/src/dhcp-manager/nm-dhcp-dhcpcd.c +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c @@ -74,7 +74,6 @@ 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) { diff --git a/src/dhcp-manager/nm-dhcp-systemd.c b/src/dhcp-manager/nm-dhcp-systemd.c index 6b06c45288..35b08a1d3b 100644 --- a/src/dhcp-manager/nm-dhcp-systemd.c +++ b/src/dhcp-manager/nm-dhcp-systemd.c @@ -401,6 +401,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) { @@ -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), @@ -486,7 +516,6 @@ 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) { @@ -494,6 +523,7 @@ ip4_start (NMDhcpClient *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; @@ -560,25 +590,25 @@ ip4_start (NMDhcpClient *client, } } - 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); } } diff --git a/src/dhcp-manager/tests/Makefile.am b/src/dhcp-manager/tests/Makefile.am index 1eb02d6323..9dc5dcb13d 100644 --- a/src/dhcp-manager/tests/Makefile.am +++ b/src/dhcp-manager/tests/Makefile.am @@ -20,6 +20,8 @@ noinst_PROGRAMS = \ 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 #include +#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); } @@ -267,6 +361,7 @@ static void test_existing_alsoreq (void) { test_config (existing_alsoreq_orig, existing_alsoreq_expected, + NULL, NULL, NULL, "eth0", @@ -305,6 +400,7 @@ static void test_existing_multiline_alsoreq (void) { test_config (existing_multiline_alsoreq_orig, existing_multiline_alsoreq_expected, + NULL, NULL, NULL, "eth0", @@ -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); From 034917e1299513ebcdd3ecbfb9d4c0adb5a641f5 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 3 Nov 2014 18:12:25 -0600 Subject: [PATCH 06/11] dhcp: preserve hostname for later use --- src/dhcp-manager/nm-dhcp-client.c | 18 ++++++++++++++++-- src/dhcp-manager/nm-dhcp-client.h | 6 +++--- src/dhcp-manager/nm-dhcp-dhclient.c | 11 +++++------ src/dhcp-manager/nm-dhcp-dhcpcd.c | 10 ++++------ src/dhcp-manager/nm-dhcp-systemd.c | 7 +++---- 5 files changed, 31 insertions(+), 21 deletions(-) diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c index f455efa732..69ed1120d6 100644 --- a/src/dhcp-manager/nm-dhcp-client.c +++ b/src/dhcp-manager/nm-dhcp-client.c @@ -46,6 +46,7 @@ typedef struct { guint32 timeout; GByteArray * duid; GBytes * client_id; + char * hostname; NMDhcpState state; pid_t pid; @@ -167,6 +168,14 @@ nm_dhcp_client_set_client_id (NMDhcpClient *self, GBytes *client_id) 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] = { @@ -400,7 +409,10 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self, nm_dhcp_client_set_client_id (self, dhcp_client_id ? nm_dhcp_utils_client_id_string_to_bytes (dhcp_client_id) : NULL); - return NM_DHCP_CLIENT_GET_CLASS (self)->ip4_start (self, dhcp_anycast_addr, hostname); + 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); } /* uuid_parse does not work for machine-id, so we use our own converter */ @@ -544,6 +556,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)", @@ -551,7 +566,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); diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h index fda14cf4a5..5651c52025 100644 --- a/src/dhcp-manager/nm-dhcp-client.h +++ b/src/dhcp-manager/nm-dhcp-client.h @@ -64,12 +64,10 @@ typedef struct { /* Methods */ gboolean (*ip4_start) (NMDhcpClient *self, - const char *anycast_addr, - const char *hostname); + const char *anycast_addr); gboolean (*ip6_start) (NMDhcpClient *self, const char *anycast_addr, - const char *hostname, gboolean info_only, NMSettingIP6ConfigPrivacy privacy, const GByteArray *duid); @@ -127,6 +125,8 @@ 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, diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c index 76e8d68c2c..c953c80d21 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -477,19 +477,18 @@ dhclient_start (NMDhcpClient *client, } static gboolean -ip4_start (NMDhcpClient *client, - const char *dhcp_anycast_addr, - const char *hostname) +ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr) { NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client); GBytes *client_id; gs_unref_bytes GBytes *new_client_id = NULL; - const char *iface, *uuid; + 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, client_id, dhcp_anycast_addr, hostname, &new_client_id); if (priv->conf_file) { @@ -505,16 +504,16 @@ 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) { 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, NULL); if (!priv->conf_file) { diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c index 5a3aee3f4d..a39f953dbe 100644 --- a/src/dhcp-manager/nm-dhcp-dhcpcd.c +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c @@ -73,16 +73,14 @@ dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED) } static gboolean -ip4_start (NMDhcpClient *client, - const char *dhcp_anycast_addr, - const char *hostname) +ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr) { 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) diff --git a/src/dhcp-manager/nm-dhcp-systemd.c b/src/dhcp-manager/nm-dhcp-systemd.c index 35b08a1d3b..6d5204c8d9 100644 --- a/src/dhcp-manager/nm-dhcp-systemd.c +++ b/src/dhcp-manager/nm-dhcp-systemd.c @@ -515,9 +515,7 @@ get_arp_type (const GByteArray *hwaddr) } static gboolean -ip4_start (NMDhcpClient *client, - const char *dhcp_anycast_addr, - const char *hostname) +ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr) { NMDhcpSystemdPrivate *priv = NM_DHCP_SYSTEMD_GET_PRIVATE (client); const char *iface = nm_dhcp_client_get_iface (client); @@ -527,6 +525,7 @@ ip4_start (NMDhcpClient *client, const uint8_t *client_id = NULL; size_t client_id_len = 0; struct in_addr last_addr; + const char *hostname; int r, i; g_assert (priv->client4 == NULL); @@ -621,6 +620,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) { @@ -683,7 +683,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) From 49cac9f32f5eb31698687216dd8e26b4b49629d8 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Mon, 3 Nov 2014 22:35:22 -0600 Subject: [PATCH 07/11] dhcp: track last IPv4 address on start for renewal Really only used by systemd because it doesn't have as good lease handling, but it's also necessary if we switch DHCP clients mid-stream (which we'll be doing later) since the new DHCP client won't have a lease file for the current IP address, and thus has nowhere to pull the current IP address from to request the same address from the DHCP server. --- src/devices/nm-device.c | 3 ++- src/dhcp-manager/nm-dhcp-client.c | 5 +++-- src/dhcp-manager/nm-dhcp-client.h | 6 ++++-- src/dhcp-manager/nm-dhcp-dhclient.c | 2 +- src/dhcp-manager/nm-dhcp-dhcpcd.c | 2 +- src/dhcp-manager/nm-dhcp-manager.c | 12 +++++++----- src/dhcp-manager/nm-dhcp-manager.h | 3 ++- src/dhcp-manager/nm-dhcp-systemd.c | 22 ++++++++++++---------- 8 files changed, 32 insertions(+), 23 deletions(-) diff --git a/src/devices/nm-device.c b/src/devices/nm-device.c index 9eb25c96aa..9d8932f18a 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); diff --git a/src/dhcp-manager/nm-dhcp-client.c b/src/dhcp-manager/nm-dhcp-client.c index 69ed1120d6..0ffd573849 100644 --- a/src/dhcp-manager/nm-dhcp-client.c +++ b/src/dhcp-manager/nm-dhcp-client.c @@ -393,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; @@ -412,7 +413,7 @@ nm_dhcp_client_start_ip4 (NMDhcpClient *self, 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); + 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 */ diff --git a/src/dhcp-manager/nm-dhcp-client.h b/src/dhcp-manager/nm-dhcp-client.h index 5651c52025..d732aa05ef 100644 --- a/src/dhcp-manager/nm-dhcp-client.h +++ b/src/dhcp-manager/nm-dhcp-client.h @@ -64,7 +64,8 @@ typedef struct { /* Methods */ gboolean (*ip4_start) (NMDhcpClient *self, - const char *anycast_addr); + const char *anycast_addr, + const char *last_ip4_address); gboolean (*ip6_start) (NMDhcpClient *self, const char *anycast_addr, @@ -130,7 +131,8 @@ 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, diff --git a/src/dhcp-manager/nm-dhcp-dhclient.c b/src/dhcp-manager/nm-dhcp-dhclient.c index c953c80d21..4cabb2257b 100644 --- a/src/dhcp-manager/nm-dhcp-dhclient.c +++ b/src/dhcp-manager/nm-dhcp-dhclient.c @@ -477,7 +477,7 @@ dhclient_start (NMDhcpClient *client, } static gboolean -ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr) +ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr, const char *last_ip4_address) { NMDhcpDhclientPrivate *priv = NM_DHCP_DHCLIENT_GET_PRIVATE (client); GBytes *client_id; diff --git a/src/dhcp-manager/nm-dhcp-dhcpcd.c b/src/dhcp-manager/nm-dhcp-dhcpcd.c index a39f953dbe..9aacba7667 100644 --- a/src/dhcp-manager/nm-dhcp-dhcpcd.c +++ b/src/dhcp-manager/nm-dhcp-dhcpcd.c @@ -73,7 +73,7 @@ dhcpcd_child_setup (gpointer user_data G_GNUC_UNUSED) } static gboolean -ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr) +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; diff --git a/src/dhcp-manager/nm-dhcp-manager.c b/src/dhcp-manager/nm-dhcp-manager.c index dbc0684dbc..563b7ac91a 100644 --- a/src/dhcp-manager/nm-dhcp-manager.c +++ b/src/dhcp-manager/nm-dhcp-manager.c @@ -224,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; @@ -265,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); @@ -296,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; @@ -306,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 */ @@ -332,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 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 6d5204c8d9..3910339e5d 100644 --- a/src/dhcp-manager/nm-dhcp-systemd.c +++ b/src/dhcp-manager/nm-dhcp-systemd.c @@ -515,7 +515,7 @@ get_arp_type (const GByteArray *hwaddr) } static gboolean -ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr) +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); @@ -524,7 +524,7 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr) 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; @@ -578,14 +578,16 @@ ip4_start (NMDhcpClient *client, const char *dhcp_anycast_addr) 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; } } From 7df18cba5bfae1ae1ccd87d574dfd2acd9c86e83 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Thu, 30 Oct 2014 18:59:10 -0500 Subject: [PATCH 08/11] core: add nm_utils_ip4_property_path() --- src/NetworkManagerUtils.c | 51 ++++++++++++++++++++++++++++++--------- src/NetworkManagerUtils.h | 1 + 2 files changed, 41 insertions(+), 11 deletions(-) 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; + return _get_property_path (ifname, property, TRUE); +} - 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 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); From a01e2ff91d3233526ce359d65be73c098573adfb Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 2 Apr 2014 12:41:04 -0500 Subject: [PATCH 09/11] core: add option to quit when startup is complete (rh #863515) (rh #1083683) Cloud setups often have a never-changing setup and since every cycle counts, they don't really want a management process running in the background after network setup is complete. Since it's likely a VM, it's not like links are going to go up/down very often. Add a new "configure-quit=true/false" config option which, when set to true, will quit NetworkManager after startup and initial configuration is complete. --- src/main.c | 19 +++++++++++++++++++ src/nm-config.c | 10 ++++++++++ src/nm-config.h | 1 + 3 files changed, 30 insertions(+) diff --git a/src/main.c b/src/main.c index 29cfd4bfd8..708545fd4c 100644 --- a/src/main.c +++ b/src/main.c @@ -177,6 +177,20 @@ _init_nm_debug (const char *debug) } } +static void +manager_startup_complete (NMManager *manager, GParamSpec *pspec, gpointer user_data) +{ + NMConfig *config = NM_CONFIG (user_data); + gboolean startup = FALSE; + + g_object_get (G_OBJECT (manager), NM_MANAGER_STARTUP, &startup, NULL); + + if (!startup && nm_config_get_configure_and_quit (config)) { + nm_log_info (LOGD_CORE, "quitting now that startup is complete"); + g_main_loop_quit (main_loop); + } +} + /* * main * @@ -448,6 +462,11 @@ main (int argc, char *argv[]) } } + g_signal_connect (manager, + "notify::" NM_MANAGER_STARTUP, + G_CALLBACK (manager_startup_complete), + config); + nm_manager_start (manager); /* Make sure the loopback interface is up. If interface is down, we bring diff --git a/src/nm-config.c b/src/nm-config.c index 1f41553355..26d5cb437a 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; @@ -218,6 +220,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) { @@ -623,6 +631,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 = g_key_file_get_boolean (priv->keyfile, "main", "configure-and-quit", NULL); + 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); From 9ff8c01d4aa0efe3ad943b1f10b61477d2658e0a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Klime=C5=A1?= Date: Fri, 23 May 2014 13:24:15 +0200 Subject: [PATCH 10/11] man: document 'configure-and-quit' configuration option --- man/NetworkManager.conf.xml.in | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) 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"> @@ -199,6 +199,28 @@ Copyright (C) 2010 - 2013 Red Hat, Inc. + + configure-and-quit + + + When set to true, 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 true, + 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 false, meaning that + NetworkManager will continue running after initial network + configuration and continue responding to system and hardware events, + D-Bus requests, and user commands. + + + + dns Set the DNS (resolv.conf) processing mode. From 5149fd120d24e7622fb264fffaa4bed04eb579d6 Mon Sep 17 00:00:00 2001 From: Dan Williams Date: Wed, 29 Oct 2014 09:12:18 -0500 Subject: [PATCH 11/11] iface-helper: add nm-iface-helper for dynamic configure-then-quit support When quitting, the Manager asks each device to spawn the interface helper, which persists and manages dynamic address on the interface after NetworkManager is gone. If the dynamic address cannot be maintaned, the helper quits and the interface's address may be removed when their lifetime runs out. To keep the helper as simple as possible, NetworkManager passes most of the configuration on the command-line, including some properties of the device's current state, which are necessary for the helper to maintain DHCP leases or IPv6 SLAAC addresses. --- .gitignore | 1 + contrib/fedora/rpm/NetworkManager.spec | 1 + po/POTFILES.in | 1 + src/Makefile.am | 85 +++- src/devices/nm-device.c | 167 ++++++++ src/devices/nm-device.h | 2 + src/dhcp-manager/nm-dhcp-manager.c | 9 +- src/main.c | 24 +- src/nm-config.c | 74 ++-- src/nm-iface-helper.c | 529 +++++++++++++++++++++++++ src/nm-ip4-config.c | 4 + src/nm-ip6-config.c | 4 + src/nm-manager.c | 28 +- src/nm-manager.h | 2 + 14 files changed, 862 insertions(+), 69 deletions(-) create mode 100644 src/nm-iface-helper.c 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/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/po/POTFILES.in b/po/POTFILES.in index b964b93f0e..0571f0e238 100644 --- a/po/POTFILES.in +++ b/po/POTFILES.in @@ -147,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 e186632548..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 @@ -458,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/devices/nm-device.c b/src/devices/nm-device.c index 9d8932f18a..3390bf765e 100644 --- a/src/devices/nm-device.c +++ b/src/devices/nm-device.c @@ -6990,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-manager.c b/src/dhcp-manager/nm-dhcp-manager.c index 563b7ac91a..3147980b4b 100644 --- a/src/dhcp-manager/nm-dhcp-manager.c +++ b/src/dhcp-manager/nm-dhcp-manager.c @@ -386,11 +386,18 @@ 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; /* Client-specific setup */ - client = nm_config_get_dhcp_client (nm_config_get ()); + 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"; + } + 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.", diff --git a/src/main.c b/src/main.c index 708545fd4c..476d2a7711 100644 --- a/src/main.c +++ b/src/main.c @@ -178,17 +178,10 @@ _init_nm_debug (const char *debug) } static void -manager_startup_complete (NMManager *manager, GParamSpec *pspec, gpointer user_data) +manager_configure_quit (NMManager *manager, gpointer user_data) { - NMConfig *config = NM_CONFIG (user_data); - gboolean startup = FALSE; - - g_object_get (G_OBJECT (manager), NM_MANAGER_STARTUP, &startup, NULL); - - if (!startup && nm_config_get_configure_and_quit (config)) { - nm_log_info (LOGD_CORE, "quitting now that startup is complete"); - g_main_loop_quit (main_loop); - } + nm_log_info (LOGD_CORE, "quitting now that startup is complete"); + g_main_loop_quit (main_loop); } /* @@ -462,10 +455,7 @@ main (int argc, char *argv[]) } } - g_signal_connect (manager, - "notify::" NM_MANAGER_STARTUP, - G_CALLBACK (manager_startup_complete), - config); + g_signal_connect (manager, NM_MANAGER_CONFIGURE_QUIT, G_CALLBACK (manager_configure_quit), config); nm_manager_start (manager); @@ -485,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 26d5cb437a..aba4fa1a1b 100644 --- a/src/nm-config.c +++ b/src/nm-config.c @@ -74,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; + gboolean value = default_value; + char *str; - g_return_val_if_fail (str, FALSE); + 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); - while (g_ascii_isspace (*str)) - str++; + str = g_key_file_get_value (keyfile, section, key, NULL); + if (!str) + return default_value; - if (!*str) - return FALSE; - - len = strlen (str); - - if (g_ascii_isspace (str[len-1])) { - str = s = g_strdup (str); - g_strchomp (s); + 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 (!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; - } - - if (out_value) - *out_value = value; - - g_free (s); - return TRUE; + g_free (str); + return value; } /************************************************************************/ @@ -521,7 +516,6 @@ nm_config_new (GError **error) GFileInfo *info; GPtrArray *confs; const char *name; - char *value; int i; GString *config_description; @@ -591,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); @@ -631,7 +611,7 @@ 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 = g_key_file_get_boolean (priv->keyfile, "main", "configure-and-quit", NULL); + priv->configure_and_quit = _get_bool_value (priv->keyfile, "main", "configure-and-quit", FALSE); return singleton; } 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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);